nvidia-videofx-filter.c 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283
  1. #include <obs-module.h>
  2. #include <util/threading.h>
  3. #include <dxgi.h>
  4. #include <d3d11.h>
  5. #include <d3d11_1.h>
  6. #include "nvvfx-load.h"
  7. /* -------------------------------------------------------- */
  8. #define do_log(level, format, ...) \
  9. blog(level, "[NVIDIA Video Effect: '%s'] " format, obs_source_get_name(filter->context), ##__VA_ARGS__)
  10. #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
  11. #define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
  12. #define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__)
  13. #ifdef _DEBUG
  14. #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
  15. #else
  16. #define debug(format, ...)
  17. #endif
  18. /* -------------------------------------------------------- */
  19. #define S_MODE "mode"
  20. #define S_MODE_QUALITY 0
  21. #define S_MODE_PERF 1
  22. #define S_MODE_QUALITY_CHAIR 2
  23. #define S_MODE_PERF_CHAIR 3
  24. #define S_THRESHOLDFX "threshold"
  25. #define S_THRESHOLDFX_DEFAULT 1.0
  26. #define S_PROCESSING "processing_interval"
  27. #define MT_ obs_module_text
  28. #define TEXT_MODE MT_("Nvvfx.Method.Greenscreen.Mode")
  29. #define TEXT_MODE_QUALITY MT_("Nvvfx.Method.Greenscreen.Quality")
  30. #define TEXT_MODE_PERF MT_("Nvvfx.Method.Greenscreen.Performance")
  31. #define TEXT_MODE_QUALITY_CHAIR MT_("Nvvfx.Method.Greenscreen.Quality.Chair")
  32. #define TEXT_MODE_PERF_CHAIR MT_("Nvvfx.Method.Greenscreen.Performance.Chair")
  33. #define TEXT_MODE_THRESHOLD MT_("Nvvfx.Method.Greenscreen.Threshold")
  34. #define TEXT_DEPRECATION MT_("Nvvfx.OutdatedSDK")
  35. #define TEXT_PROCESSING MT_("Nvvfx.Method.Greenscreen.Processing")
  36. #define TEXT_PROCESSING_HINT MT_("Nvvfx.Method.Greenscreen.Processing.Hint")
  37. /* Blur & background blur FX */
  38. #define S_STRENGTH "intensity"
  39. #define S_STRENGTH_DEFAULT 0.5
  40. #define TEXT_MODE_BLUR_STRENGTH MT_("Nvvfx.Method.Blur.Strength")
  41. enum nvvfx_fx_id { S_FX_AIGS, S_FX_BLUR, S_FX_BG_BLUR };
  42. bool nvvfx_loaded = false;
  43. bool nvvfx_new_sdk = false;
  44. /* clang-format off */
  45. struct nvvfx_data {
  46. obs_source_t *context;
  47. bool images_allocated;
  48. bool initial_render;
  49. volatile bool processing_stop;
  50. bool processed_frame;
  51. bool target_valid;
  52. bool got_new_frame;
  53. /* RTX SDK vars */
  54. NvVFX_Handle handle;
  55. CUstream stream; // CUDA stream
  56. int mode; // 0 = quality, 1 = performance
  57. NvCVImage *src_img; // src img in obs format (RGBA ?) on GPU
  58. NvCVImage *BGR_src_img; // src img in BGR on GPU
  59. NvCVImage *A_dst_img; // mask img on GPU
  60. NvCVImage *dst_img; // Greenscreen: alpha mask texture; blur: texture initialized from d3d11 (RGBA, chunky, u8)
  61. NvCVImage *stage; // used for transfer to texture
  62. unsigned int version;
  63. NvVFX_StateObjectHandle stateObjectHandle;
  64. /* alpha mask effect */
  65. gs_effect_t *effect;
  66. gs_texrender_t *render;
  67. gs_texrender_t *render_unorm;
  68. gs_texture_t *alpha_texture; // either alpha or blur
  69. uint32_t width; // width of texture
  70. uint32_t height; // height of texture
  71. enum gs_color_space space;
  72. gs_eparam_t *mask_param;
  73. gs_eparam_t *image_param;
  74. gs_eparam_t *threshold_param;
  75. gs_eparam_t *multiplier_param;
  76. float threshold;
  77. /* Every nth frame is processed through the FX (where n =
  78. * processing_interval) so that the same mask is used for n consecutive
  79. * frames. This is to alleviate the GPU load. Default: 1 (process every frame).
  80. * Only used for AIGS.
  81. */
  82. int processing_interval;
  83. int processing_counter;
  84. /* blur specific */
  85. enum nvvfx_fx_id filter_id;
  86. NvVFX_Handle handle_blur;
  87. float strength; // from 0 to 1, default = 0.5
  88. NvCVImage *blur_BGR_dst_img; // dst img of blur FX (BGR, chunky, u8)
  89. NvCVImage *RGBA_dst_img; // tmp img used to transfer to texture
  90. NvCVImage *blur_dst_img; // dst img initialized from d3d11 texture (RGBA, chunky, u8)
  91. gs_texture_t *blur_texture;
  92. gs_eparam_t *blur_param;
  93. };
  94. /* clang-format on */
  95. static const char *nv_greenscreen_filter_name(void *unused)
  96. {
  97. UNUSED_PARAMETER(unused);
  98. return obs_module_text("Nvvfx.Method.Greenscreen");
  99. }
  100. static const char *nv_blur_filter_name(void *unused)
  101. {
  102. UNUSED_PARAMETER(unused);
  103. return obs_module_text("Nvvfx.Method.BlurFilter");
  104. }
  105. static const char *nv_background_blur_filter_name(void *unused)
  106. {
  107. UNUSED_PARAMETER(unused);
  108. return obs_module_text("Nvvfx.Method.BackgroundBlurFilter");
  109. }
  110. static void nvvfx_filter_update(void *data, obs_data_t *settings)
  111. {
  112. struct nvvfx_data *filter = (struct nvvfx_data *)data;
  113. NvCV_Status vfxErr;
  114. enum nvvfx_fx_id id = filter->filter_id;
  115. filter->threshold = (float)obs_data_get_double(settings, S_THRESHOLDFX);
  116. filter->processing_interval = (int)obs_data_get_int(settings, S_PROCESSING);
  117. float strength = (float)obs_data_get_double(settings, S_STRENGTH);
  118. if (id == S_FX_AIGS || id == S_FX_BG_BLUR) {
  119. int mode = id == S_FX_BG_BLUR ? (int)S_MODE_PERF : (int)obs_data_get_int(settings, S_MODE);
  120. if (filter->mode != mode) {
  121. filter->mode = mode;
  122. vfxErr = NvVFX_SetU32(filter->handle, NVVFX_MODE, mode);
  123. vfxErr = NvVFX_Load(filter->handle);
  124. if (NVCV_SUCCESS != vfxErr)
  125. error("Error loading AI Greenscreen FX %i", vfxErr);
  126. }
  127. }
  128. if (id == S_FX_BLUR || id == S_FX_BG_BLUR) {
  129. if (filter->strength != strength) {
  130. filter->strength = strength;
  131. vfxErr = NvVFX_SetF32(filter->handle_blur, NVVFX_STRENGTH, filter->strength);
  132. vfxErr = NvVFX_Load(filter->handle_blur);
  133. }
  134. }
  135. }
  136. static void nvvfx_filter_actual_destroy(void *data)
  137. {
  138. struct nvvfx_data *filter = (struct nvvfx_data *)data;
  139. if (!nvvfx_loaded) {
  140. bfree(filter);
  141. return;
  142. }
  143. os_atomic_set_bool(&filter->processing_stop, true);
  144. if (filter->images_allocated) {
  145. obs_enter_graphics();
  146. if (filter->filter_id == S_FX_AIGS)
  147. gs_texture_destroy(filter->alpha_texture);
  148. else
  149. gs_texture_destroy(filter->blur_texture);
  150. gs_texrender_destroy(filter->render);
  151. gs_texrender_destroy(filter->render_unorm);
  152. obs_leave_graphics();
  153. NvCVImage_Destroy(filter->src_img);
  154. NvCVImage_Destroy(filter->BGR_src_img);
  155. NvCVImage_Destroy(filter->A_dst_img);
  156. NvCVImage_Destroy(filter->dst_img);
  157. NvCVImage_Destroy(filter->stage);
  158. if (filter->filter_id != S_FX_AIGS) {
  159. NvCVImage_Destroy(filter->blur_BGR_dst_img);
  160. NvCVImage_Destroy(filter->RGBA_dst_img);
  161. NvCVImage_Destroy(filter->blur_dst_img);
  162. }
  163. }
  164. if (filter->handle) {
  165. if (filter->stateObjectHandle) {
  166. NvVFX_DeallocateState(filter->handle, filter->stateObjectHandle);
  167. }
  168. NvVFX_DestroyEffect(filter->handle);
  169. }
  170. if (filter->handle_blur) {
  171. NvVFX_DestroyEffect(filter->handle_blur);
  172. }
  173. if (filter->stream)
  174. NvVFX_CudaStreamDestroy(filter->stream);
  175. if (filter->effect) {
  176. obs_enter_graphics();
  177. gs_effect_destroy(filter->effect);
  178. obs_leave_graphics();
  179. }
  180. bfree(filter);
  181. }
  182. static void nvvfx_filter_destroy(void *data)
  183. {
  184. obs_queue_task(OBS_TASK_GRAPHICS, nvvfx_filter_actual_destroy, data, false);
  185. }
  186. static void *log_nverror_destroy(struct nvvfx_data *filter, NvCV_Status vfxErr)
  187. {
  188. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  189. error("Error creating NVIDIA Video FX; error %i: %s", vfxErr, errString);
  190. nvvfx_filter_destroy(filter);
  191. return NULL;
  192. }
  193. //---------------------------------------------------------------------------//
  194. // filter creation //
  195. static bool nvvfx_filter_create_internal(struct nvvfx_data *filter)
  196. {
  197. NvCV_Status vfxErr;
  198. enum nvvfx_fx_id id = filter->filter_id;
  199. /* 1. Create FX */
  200. switch (id) {
  201. case S_FX_AIGS:
  202. vfxErr = NvVFX_CreateEffect(NVVFX_FX_GREEN_SCREEN, &filter->handle);
  203. break;
  204. case S_FX_BLUR:
  205. vfxErr = NvVFX_CreateEffect(NVVFX_FX_BGBLUR, &filter->handle_blur);
  206. break;
  207. case S_FX_BG_BLUR:
  208. vfxErr = NvVFX_CreateEffect(NVVFX_FX_GREEN_SCREEN, &filter->handle);
  209. if (NVCV_SUCCESS != vfxErr)
  210. log_nverror_destroy(filter, vfxErr);
  211. vfxErr = NvVFX_CreateEffect(NVVFX_FX_BGBLUR, &filter->handle_blur);
  212. break;
  213. default:
  214. return false;
  215. }
  216. if (NVCV_SUCCESS != vfxErr)
  217. log_nverror_destroy(filter, vfxErr);
  218. /* debug */
  219. #ifdef _DEBUG
  220. if (id == S_FX_AIGS || id == S_FX_BG_BLUR) {
  221. const char *info2;
  222. vfxErr = NvVFX_GetString(filter->handle, NVVFX_INFO, &info2);
  223. blog(LOG_DEBUG, "aigs fx settings :%s", info2);
  224. }
  225. if (id == S_FX_BLUR || id == S_FX_BG_BLUR) {
  226. const char *info;
  227. vfxErr = NvVFX_GetString(filter->handle_blur, NVVFX_INFO, &info);
  228. blog(LOG_DEBUG, "blur fx settings :%s", info);
  229. }
  230. #endif
  231. /* 2. Set models path & initialize CudaStream */
  232. vfxErr = NvVFX_CudaStreamCreate(&filter->stream);
  233. if (NVCV_SUCCESS != vfxErr)
  234. log_nverror_destroy(filter, vfxErr);
  235. if (id == S_FX_AIGS || id == S_FX_BG_BLUR) {
  236. char buffer[MAX_PATH];
  237. char modelDir[MAX_PATH];
  238. nvvfx_get_sdk_path(buffer, MAX_PATH);
  239. size_t max_len = sizeof(buffer) / sizeof(char);
  240. snprintf(modelDir, max_len, "%s\\models", buffer);
  241. vfxErr = NvVFX_SetString(filter->handle, NVVFX_MODEL_DIRECTORY, modelDir);
  242. vfxErr = NvVFX_SetCudaStream(filter->handle, NVVFX_CUDA_STREAM, filter->stream);
  243. if (NVCV_SUCCESS != vfxErr)
  244. log_nverror_destroy(filter, vfxErr);
  245. }
  246. if (id == S_FX_BLUR || id == S_FX_BG_BLUR) {
  247. vfxErr = NvVFX_SetCudaStream(filter->handle_blur, NVVFX_CUDA_STREAM, filter->stream);
  248. if (NVCV_SUCCESS != vfxErr)
  249. log_nverror_destroy(filter, vfxErr);
  250. }
  251. return true;
  252. }
  253. static void nvvfx_logger_callback(void *data, const char *msg)
  254. {
  255. UNUSED_PARAMETER(data);
  256. blog(LOG_ERROR, "[NVIDIA Video Effect: '%s']", msg);
  257. }
  258. static void *nvvfx_filter_create(obs_data_t *settings, obs_source_t *context, enum nvvfx_fx_id id)
  259. {
  260. struct nvvfx_data *filter = (struct nvvfx_data *)bzalloc(sizeof(*filter));
  261. if (!nvvfx_loaded) {
  262. nvvfx_filter_destroy(filter);
  263. return NULL;
  264. }
  265. NvCV_Status vfxErr;
  266. filter->context = context;
  267. filter->mode = -1; // should be 0 or 1; -1 triggers an update
  268. filter->images_allocated = false;
  269. filter->processed_frame = true; // start processing when false
  270. filter->width = 0;
  271. filter->height = 0;
  272. filter->initial_render = false;
  273. os_atomic_set_bool(&filter->processing_stop, false);
  274. filter->processing_interval = 1;
  275. filter->processing_counter = 0;
  276. // set nvvfx_fx_id
  277. filter->filter_id = id;
  278. // blur specific
  279. filter->strength = -1;
  280. #ifdef _DEBUG
  281. /* sanity check of sdk version */
  282. if (NvVFX_GetVersion(&filter->version) == NVCV_SUCCESS) {
  283. uint8_t major = (filter->version >> 24) & 0xff;
  284. uint8_t minor = (filter->version >> 16) & 0x00ff;
  285. uint8_t build = (filter->version >> 8) & 0x0000ff;
  286. uint8_t revision = (filter->version >> 0) & 0x000000ff;
  287. nvvfx_new_sdk = filter->version >= MIN_VFX_SDK_VERSION && nvvfx_new_sdk;
  288. }
  289. #endif
  290. /* 1. Create FX */
  291. /* 2. Set models path & initialize CudaStream */
  292. if (!nvvfx_filter_create_internal(filter))
  293. return NULL;
  294. /* 3. Load effect. */
  295. char *effect_path = obs_module_file(id != S_FX_AIGS ? "rtx_blur.effect" : "rtx_greenscreen.effect");
  296. obs_enter_graphics();
  297. filter->effect = gs_effect_create_from_file(effect_path, NULL);
  298. bfree(effect_path);
  299. if (filter->effect) {
  300. if (id == S_FX_AIGS) {
  301. filter->mask_param = gs_effect_get_param_by_name(filter->effect, "mask");
  302. filter->threshold_param = gs_effect_get_param_by_name(filter->effect, "threshold");
  303. } else {
  304. filter->blur_param = gs_effect_get_param_by_name(filter->effect, "blurred");
  305. }
  306. filter->image_param = gs_effect_get_param_by_name(filter->effect, "image");
  307. filter->multiplier_param = gs_effect_get_param_by_name(filter->effect, "multiplier");
  308. }
  309. obs_leave_graphics();
  310. /* 4. Allocate state for the AIGS & background blur */
  311. if (nvvfx_new_sdk && id != S_FX_BLUR) {
  312. vfxErr = NvVFX_AllocateState(filter->handle, &filter->stateObjectHandle);
  313. if (NVCV_SUCCESS != vfxErr)
  314. return log_nverror_destroy(filter, vfxErr);
  315. vfxErr = NvVFX_SetStateObjectHandleArray(filter->handle, NVVFX_STATE, &filter->stateObjectHandle);
  316. if (NVCV_SUCCESS != vfxErr)
  317. return log_nverror_destroy(filter, vfxErr);
  318. }
  319. if (!filter->effect) {
  320. nvvfx_filter_destroy(filter);
  321. return NULL;
  322. }
  323. nvvfx_filter_update(filter, settings);
  324. /* Setup NVIDIA logger */
  325. if (nvvfx_new_sdk)
  326. vfxErr = NvVFX_ConfigureLogger(NVCV_LOG_ERROR, NULL, &nvvfx_logger_callback, filter);
  327. return filter;
  328. }
  329. static void *nv_greenscreen_filter_create(obs_data_t *settings, obs_source_t *context)
  330. {
  331. return nvvfx_filter_create(settings, context, S_FX_AIGS);
  332. }
  333. static void *nv_blur_filter_create(obs_data_t *settings, obs_source_t *context)
  334. {
  335. return nvvfx_filter_create(settings, context, S_FX_BLUR);
  336. }
  337. static void *nv_background_blur_filter_create(obs_data_t *settings, obs_source_t *context)
  338. {
  339. return nvvfx_filter_create(settings, context, S_FX_BG_BLUR);
  340. }
  341. static void nvvfx_filter_reset(void *data, calldata_t *calldata)
  342. {
  343. struct nvvfx_data *filter = (struct nvvfx_data *)data;
  344. NvCV_Status vfxErr;
  345. os_atomic_set_bool(&filter->processing_stop, true);
  346. // [A] first destroy
  347. if (filter->handle) {
  348. if (filter->stateObjectHandle) {
  349. NvVFX_DeallocateState(filter->handle, filter->stateObjectHandle);
  350. }
  351. NvVFX_DestroyEffect(filter->handle);
  352. }
  353. if (filter->handle_blur) {
  354. NvVFX_DestroyEffect(filter->handle_blur);
  355. }
  356. if (filter->stream) {
  357. NvVFX_CudaStreamDestroy(filter->stream);
  358. }
  359. // [B] recreate
  360. /* 1. Create FX */
  361. /* 2. Set models path & initialize CudaStream */
  362. if (!nvvfx_filter_create_internal(filter))
  363. return;
  364. /* 3. load FX */
  365. if (filter->filter_id != S_FX_BLUR) {
  366. vfxErr = NvVFX_SetU32(filter->handle, NVVFX_MODE, filter->mode);
  367. if (NVCV_SUCCESS != vfxErr)
  368. error("Error loading NVIDIA Video FX %i", vfxErr);
  369. // reallocate state object
  370. vfxErr = NvVFX_AllocateState(filter->handle, &filter->stateObjectHandle);
  371. vfxErr = NvVFX_SetStateObjectHandleArray(filter->handle, NVVFX_STATE, &filter->stateObjectHandle);
  372. vfxErr = NvVFX_Load(filter->handle);
  373. if (NVCV_SUCCESS != vfxErr)
  374. error("Error loading NVIDIA Video FX %i", vfxErr);
  375. }
  376. if (filter->filter_id != S_FX_AIGS) {
  377. vfxErr = NvVFX_SetF32(filter->handle_blur, NVVFX_STRENGTH, filter->strength);
  378. if (NVCV_SUCCESS != vfxErr)
  379. error("Error loading NVIDIA Video FX %i", vfxErr);
  380. vfxErr = NvVFX_Load(filter->handle_blur);
  381. if (NVCV_SUCCESS != vfxErr)
  382. error("Error loading blur FX %i", vfxErr);
  383. }
  384. filter->images_allocated = false;
  385. os_atomic_set_bool(&filter->processing_stop, false);
  386. }
  387. //---------------------------------------------------------------------------//
  388. // intialization of images //
  389. static bool create_alpha_texture(struct nvvfx_data *filter)
  390. {
  391. NvCV_Status vfxErr;
  392. uint32_t width = filter->width;
  393. uint32_t height = filter->height;
  394. /* 1. create alpha texture */
  395. if (filter->alpha_texture) {
  396. gs_texture_destroy(filter->alpha_texture);
  397. }
  398. filter->alpha_texture = gs_texture_create(width, height, GS_A8, 1, NULL, 0);
  399. if (filter->alpha_texture == NULL) {
  400. error("Alpha texture couldn't be created");
  401. return false;
  402. }
  403. struct ID3D11Texture2D *d11texture = (struct ID3D11Texture2D *)gs_texture_get_obj(filter->alpha_texture);
  404. /* 2. Create NvCVImage which will hold final alpha texture. */
  405. if (!filter->dst_img)
  406. NvCVImage_Create(width, height, NVCV_A, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1, &filter->dst_img);
  407. vfxErr = NvCVImage_InitFromD3D11Texture(filter->dst_img, d11texture);
  408. if (vfxErr != NVCV_SUCCESS) {
  409. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  410. error("Error passing dst ID3D11Texture to img; error %i: %s", vfxErr, errString);
  411. return false;
  412. }
  413. return true;
  414. }
  415. static bool create_blur_texture(struct nvvfx_data *filter)
  416. {
  417. NvCV_Status vfxErr;
  418. uint32_t width = filter->width;
  419. uint32_t height = filter->height;
  420. /* 1. Create blur texture */
  421. if (filter->blur_texture) {
  422. gs_texture_destroy(filter->blur_texture);
  423. }
  424. filter->blur_texture = gs_texture_create(width, height, GS_RGBA_UNORM, 1, NULL, 0);
  425. if (filter->blur_texture == NULL) {
  426. error("Blur texture couldn't be created");
  427. return false;
  428. }
  429. struct ID3D11Texture2D *d11texture = (struct ID3D11Texture2D *)gs_texture_get_obj(filter->blur_texture);
  430. /* 2. Create NvCVImage which will hold final blur texture */
  431. if (!filter->blur_dst_img)
  432. NvCVImage_Create(width, height, NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1, &filter->blur_dst_img);
  433. vfxErr = NvCVImage_InitFromD3D11Texture(filter->blur_dst_img, d11texture);
  434. if (vfxErr != NVCV_SUCCESS) {
  435. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  436. error("Error passing dst ID3D11Texture to img; error %i: %s", vfxErr, errString);
  437. return false;
  438. }
  439. return true;
  440. }
  441. static bool create_texrenders(struct nvvfx_data *filter)
  442. {
  443. if (filter->render)
  444. gs_texrender_destroy(filter->render);
  445. filter->render = gs_texrender_create(gs_get_format_from_space(filter->space), GS_ZS_NONE);
  446. if (!filter->render) {
  447. error("Failed to create render texrenderer");
  448. return false;
  449. }
  450. if (filter->render_unorm)
  451. gs_texrender_destroy(filter->render_unorm);
  452. filter->render_unorm = gs_texrender_create(GS_BGRA_UNORM, GS_ZS_NONE);
  453. if (!filter->render_unorm) {
  454. error("Failed to create render_unorm texrenderer");
  455. return false;
  456. }
  457. return true;
  458. }
  459. static bool init_blur_images(struct nvvfx_data *filter)
  460. {
  461. uint32_t width = filter->width;
  462. uint32_t height = filter->height;
  463. enum nvvfx_fx_id id = filter->filter_id;
  464. /* 1. Create and allocate Blur BGR NvCVimage (blur FX dst) */
  465. NvCVImage_Create(width, height, NVCV_BGR, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1, &filter->blur_BGR_dst_img);
  466. NvCVImage_Alloc(filter->blur_BGR_dst_img, width, height, NVCV_BGR, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1);
  467. /* 2. Create dst NvCVImage */
  468. if (filter->RGBA_dst_img) {
  469. NvCVImage_Realloc(filter->RGBA_dst_img, width, height, NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1);
  470. } else {
  471. NvCVImage_Create(width, height, NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1, &filter->RGBA_dst_img);
  472. NvCVImage_Alloc(filter->RGBA_dst_img, width, height, NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1);
  473. }
  474. /* 3. Set input & output images for nv blur FX */
  475. NvVFX_SetImage(filter->handle_blur, NVVFX_INPUT_IMAGE, filter->BGR_src_img);
  476. if (id != S_FX_BLUR)
  477. NvVFX_SetImage(filter->handle_blur, NVVFX_INPUT_IMAGE_1, filter->A_dst_img);
  478. else
  479. NvVFX_SetImage(filter->handle_blur, NVVFX_INPUT_IMAGE_1, NULL);
  480. NvVFX_SetImage(filter->handle_blur, NVVFX_OUTPUT_IMAGE, filter->blur_BGR_dst_img);
  481. if (NvVFX_Load(filter->handle_blur) != NVCV_SUCCESS) {
  482. error("Error loading blur FX");
  483. return false;
  484. }
  485. return true;
  486. }
  487. static void init_images(struct nvvfx_data *filter)
  488. {
  489. NvCV_Status vfxErr;
  490. uint32_t width = filter->width;
  491. uint32_t height = filter->height;
  492. /* 1. Create alpha texture & associated NvCVImage */
  493. if (filter->filter_id == S_FX_BG_BLUR || filter->filter_id == S_FX_AIGS) {
  494. if (!create_alpha_texture(filter))
  495. goto fail;
  496. }
  497. /* 2. Create blur texture & associated NvCVImage */
  498. if (filter->filter_id == S_FX_BG_BLUR || filter->filter_id == S_FX_BLUR) {
  499. if (!create_blur_texture(filter))
  500. goto fail;
  501. }
  502. /* 3. Create texrenders */
  503. if (!create_texrenders(filter))
  504. goto fail;
  505. /* 4. Create and allocate BGR NvCVImage (alpha mask FX src) */
  506. if (filter->BGR_src_img) {
  507. NvCVImage_Realloc(filter->BGR_src_img, width, height, NVCV_BGR, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1);
  508. } else {
  509. NvCVImage_Create(width, height, NVCV_BGR, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1, &filter->BGR_src_img);
  510. NvCVImage_Alloc(filter->BGR_src_img, width, height, NVCV_BGR, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1);
  511. }
  512. /* 5. Create and allocate Alpha NvCVimage (mask fx dst). */
  513. if (filter->A_dst_img) {
  514. NvCVImage_Realloc(filter->A_dst_img, width, height, NVCV_A, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1);
  515. } else {
  516. NvCVImage_Create(width, height, NVCV_A, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1, &filter->A_dst_img);
  517. NvCVImage_Alloc(filter->A_dst_img, width, height, NVCV_A, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1);
  518. }
  519. /* 6. Create stage NvCVImage which will be used as buffer for transfer */
  520. vfxErr = NvCVImage_Create(width, height, NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1, &filter->stage);
  521. vfxErr = NvCVImage_Alloc(filter->stage, width, height, NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1);
  522. if (vfxErr != NVCV_SUCCESS) {
  523. goto fail;
  524. }
  525. /* 7. Init blur images */
  526. if (filter->filter_id == S_FX_BLUR || filter->filter_id == S_FX_BG_BLUR) {
  527. if (!init_blur_images(filter))
  528. goto fail;
  529. }
  530. /* 8. Pass settings for AIGS (AI Greenscreen) FX */
  531. if (filter->filter_id == S_FX_BG_BLUR || filter->filter_id == S_FX_AIGS) {
  532. NvVFX_SetImage(filter->handle, NVVFX_INPUT_IMAGE, filter->BGR_src_img);
  533. NvVFX_SetImage(filter->handle, NVVFX_OUTPUT_IMAGE, filter->A_dst_img);
  534. if (filter->width)
  535. NvVFX_SetU32(filter->handle, NVVFX_MAX_INPUT_WIDTH, filter->width);
  536. if (filter->height)
  537. NvVFX_SetU32(filter->handle, NVVFX_MAX_INPUT_HEIGHT, filter->height);
  538. vfxErr = NvVFX_Load(filter->handle);
  539. if (NVCV_SUCCESS != vfxErr)
  540. error("Error loading AI Greenscreen FX %i", vfxErr);
  541. }
  542. filter->images_allocated = true;
  543. return;
  544. fail:
  545. error("Error during allocation of images");
  546. os_atomic_set_bool(&filter->processing_stop, true);
  547. return;
  548. }
  549. //---------------------------------------------------------------------------//
  550. // video processing functions //
  551. enum nvvfx_run_mode { SYNC, ASYNC };
  552. static bool process_texture(struct nvvfx_data *filter)
  553. {
  554. enum nvvfx_fx_id id = filter->filter_id;
  555. CUstream process_stream = filter->stream;
  556. /* 1. Map src img holding texture. */
  557. NvCV_Status vfxErr = NvCVImage_MapResource(filter->src_img, process_stream);
  558. if (vfxErr != NVCV_SUCCESS) {
  559. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  560. error("Error mapping resource for source texture; error %i : %s", vfxErr, errString);
  561. goto fail;
  562. }
  563. /* 2. Convert to BGR. */
  564. vfxErr = NvCVImage_Transfer(filter->src_img, filter->BGR_src_img, 1.0f, process_stream, filter->stage);
  565. if (vfxErr != NVCV_SUCCESS) {
  566. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  567. error("Error converting src to BGR img; error %i: %s", vfxErr, errString);
  568. goto fail;
  569. }
  570. vfxErr = NvCVImage_UnmapResource(filter->src_img, process_stream);
  571. if (vfxErr != NVCV_SUCCESS) {
  572. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  573. error("Error unmapping resource for src texture; error %i: %s", vfxErr, errString);
  574. goto fail;
  575. }
  576. /* 3. Run AIGS (AI Greenscreen) fx */
  577. if (id != S_FX_BLUR) {
  578. vfxErr = NvVFX_Run(filter->handle, SYNC);
  579. if (vfxErr != NVCV_SUCCESS) {
  580. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  581. error("Error running the AIGS FX; error %i: %s", vfxErr, errString);
  582. if (vfxErr == NVCV_ERR_CUDA)
  583. nvvfx_filter_reset(filter, NULL);
  584. }
  585. }
  586. if (id != S_FX_AIGS) {
  587. /* 4. BLUR FX */
  588. /* 4a. Run BLUR FX except for AIGS */
  589. vfxErr = NvVFX_Run(filter->handle_blur, SYNC);
  590. if (vfxErr != NVCV_SUCCESS) {
  591. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  592. error("Error running the BLUR FX; error %i: %s", vfxErr, errString);
  593. if (vfxErr == NVCV_ERR_CUDA)
  594. nvvfx_filter_reset(filter, NULL);
  595. }
  596. /* 4b. Transfer blur result to an intermediate dst [RGBA, chunky, u8] */
  597. vfxErr = NvCVImage_Transfer(filter->blur_BGR_dst_img, filter->RGBA_dst_img, 1.0f, filter->stream,
  598. filter->stage);
  599. if (vfxErr != NVCV_SUCCESS) {
  600. error("Error transferring blurred to intermediate img [RGBA, chunky, u8], error %i, ", vfxErr);
  601. goto fail;
  602. }
  603. /* 5. Map blur dst texture before transfer from dst img provided by FX */
  604. vfxErr = NvCVImage_MapResource(filter->blur_dst_img, filter->stream);
  605. if (vfxErr != NVCV_SUCCESS) {
  606. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  607. error("Error mapping resource for dst texture; error %i: %s", vfxErr, errString);
  608. goto fail;
  609. }
  610. vfxErr = NvCVImage_Transfer(filter->RGBA_dst_img, filter->blur_dst_img, 1.0f, filter->stream,
  611. filter->stage);
  612. if (vfxErr != NVCV_SUCCESS) {
  613. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  614. error("Error transferring mask to alpha texture; error %i: %s ", vfxErr, errString);
  615. goto fail;
  616. }
  617. vfxErr = NvCVImage_UnmapResource(filter->blur_dst_img, filter->stream);
  618. if (vfxErr != NVCV_SUCCESS) {
  619. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  620. error("Error unmapping resource for dst texture; error %i: %s", vfxErr, errString);
  621. goto fail;
  622. }
  623. } else {
  624. /* 4. Map dst texture before transfer from dst img provided by AIGS FX */
  625. vfxErr = NvCVImage_MapResource(filter->dst_img, filter->stream);
  626. if (vfxErr != NVCV_SUCCESS) {
  627. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  628. error("Error mapping resource for dst texture; error %i: %s", vfxErr, errString);
  629. goto fail;
  630. }
  631. vfxErr = NvCVImage_Transfer(filter->A_dst_img, filter->dst_img, 1.0f, filter->stream, filter->stage);
  632. if (vfxErr != NVCV_SUCCESS) {
  633. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  634. error("Error transferring mask to alpha texture; error %i: %s ", vfxErr, errString);
  635. goto fail;
  636. }
  637. vfxErr = NvCVImage_UnmapResource(filter->dst_img, filter->stream);
  638. if (vfxErr != NVCV_SUCCESS) {
  639. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  640. error("Error unmapping resource for dst texture; error %i: %s", vfxErr, errString);
  641. goto fail;
  642. }
  643. }
  644. return true;
  645. fail:
  646. os_atomic_set_bool(&filter->processing_stop, true);
  647. return false;
  648. }
  649. static struct obs_source_frame *nvvfx_filter_video(void *data, struct obs_source_frame *frame)
  650. {
  651. struct nvvfx_data *filter = (struct nvvfx_data *)data;
  652. filter->got_new_frame = true;
  653. return frame;
  654. }
  655. static void nvvfx_filter_tick(void *data, float t)
  656. {
  657. UNUSED_PARAMETER(t);
  658. struct nvvfx_data *filter = (struct nvvfx_data *)data;
  659. if (filter->processing_stop) {
  660. return;
  661. }
  662. if (!obs_filter_get_target(filter->context)) {
  663. return;
  664. }
  665. obs_source_t *target = obs_filter_get_target(filter->context);
  666. filter->target_valid = true;
  667. const uint32_t cx = obs_source_get_base_width(target);
  668. const uint32_t cy = obs_source_get_base_height(target);
  669. // initially the sizes are 0
  670. if (!cx || !cy) {
  671. filter->target_valid = false;
  672. return;
  673. }
  674. /* minimum size supported by SDK is (512,288) */
  675. if (filter->filter_id != S_FX_BLUR) {
  676. filter->target_valid = cx >= 512 && cy >= 288;
  677. if (!filter->target_valid) {
  678. error("Size must be larger than (512,288)");
  679. return;
  680. }
  681. }
  682. if (cx != filter->width || cy != filter->height) {
  683. filter->images_allocated = false;
  684. filter->width = cx;
  685. filter->height = cy;
  686. }
  687. if (!filter->images_allocated) {
  688. obs_enter_graphics();
  689. init_images(filter);
  690. obs_leave_graphics();
  691. filter->initial_render = false;
  692. }
  693. filter->processed_frame = false;
  694. }
  695. static const char *get_tech_name_and_multiplier(enum gs_color_space current_space, enum gs_color_space source_space,
  696. float *multiplier)
  697. {
  698. const char *tech_name = "Draw";
  699. *multiplier = 1.f;
  700. switch (source_space) {
  701. case GS_CS_SRGB:
  702. case GS_CS_SRGB_16F:
  703. if (current_space == GS_CS_709_SCRGB) {
  704. tech_name = "DrawMultiply";
  705. *multiplier = obs_get_video_sdr_white_level() / 80.0f;
  706. }
  707. break;
  708. case GS_CS_709_EXTENDED:
  709. switch (current_space) {
  710. case GS_CS_SRGB:
  711. case GS_CS_SRGB_16F:
  712. tech_name = "DrawTonemap";
  713. break;
  714. case GS_CS_709_SCRGB:
  715. tech_name = "DrawMultiply";
  716. *multiplier = obs_get_video_sdr_white_level() / 80.0f;
  717. }
  718. break;
  719. case GS_CS_709_SCRGB:
  720. switch (current_space) {
  721. case GS_CS_SRGB:
  722. case GS_CS_SRGB_16F:
  723. tech_name = "DrawMultiplyTonemap";
  724. *multiplier = 80.0f / obs_get_video_sdr_white_level();
  725. break;
  726. case GS_CS_709_EXTENDED:
  727. tech_name = "DrawMultiply";
  728. *multiplier = 80.0f / obs_get_video_sdr_white_level();
  729. }
  730. }
  731. return tech_name;
  732. }
  733. static void draw_greenscreen_blur(struct nvvfx_data *filter, bool has_blur)
  734. {
  735. const enum gs_color_space source_space = filter->space;
  736. float multiplier;
  737. const char *technique = get_tech_name_and_multiplier(gs_get_color_space(), source_space, &multiplier);
  738. const enum gs_color_format format = gs_get_format_from_space(source_space);
  739. if (obs_source_process_filter_begin_with_color_space(filter->context, format, source_space,
  740. OBS_ALLOW_DIRECT_RENDERING)) {
  741. if (has_blur) {
  742. gs_effect_set_texture_srgb(filter->blur_param, filter->blur_texture);
  743. } else {
  744. gs_effect_set_texture(filter->mask_param, filter->alpha_texture);
  745. gs_effect_set_float(filter->threshold_param, min(filter->threshold, 0.95f));
  746. }
  747. gs_effect_set_texture_srgb(filter->image_param, gs_texrender_get_texture(filter->render));
  748. gs_effect_set_float(filter->multiplier_param, multiplier);
  749. gs_blend_state_push();
  750. gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
  751. obs_source_process_filter_tech_end(filter->context, filter->effect, 0, 0, technique);
  752. gs_blend_state_pop();
  753. }
  754. }
  755. static void nvvfx_filter_render(void *data, gs_effect_t *effect, bool has_blur)
  756. {
  757. NvCV_Status vfxErr;
  758. struct nvvfx_data *filter = (struct nvvfx_data *)data;
  759. if (filter->processing_stop || (has_blur && filter->strength == 0)) {
  760. obs_source_skip_video_filter(filter->context);
  761. return;
  762. }
  763. obs_source_t *const target = obs_filter_get_target(filter->context);
  764. obs_source_t *const parent = obs_filter_get_parent(filter->context);
  765. /* Skip if processing of a frame hasn't yet started */
  766. if (!filter->target_valid || !target || !parent) {
  767. obs_source_skip_video_filter(filter->context);
  768. return;
  769. }
  770. /* Render processed image from earlier in the frame */
  771. if (filter->processed_frame) {
  772. draw_greenscreen_blur(filter, has_blur);
  773. return;
  774. }
  775. /* 1. Render to retrieve texture. */
  776. if (!filter->render) {
  777. obs_source_skip_video_filter(filter->context);
  778. return;
  779. }
  780. const uint32_t target_flags = obs_source_get_output_flags(target);
  781. const uint32_t parent_flags = obs_source_get_output_flags(parent);
  782. bool custom_draw = (target_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
  783. bool async = (target_flags & OBS_SOURCE_ASYNC) != 0;
  784. const enum gs_color_space preferred_spaces[] = {
  785. GS_CS_SRGB,
  786. GS_CS_SRGB_16F,
  787. GS_CS_709_EXTENDED,
  788. };
  789. const enum gs_color_space source_space =
  790. obs_source_get_color_space(target, OBS_COUNTOF(preferred_spaces), preferred_spaces);
  791. if (filter->space != source_space) {
  792. filter->space = source_space;
  793. init_images(filter);
  794. filter->initial_render = false;
  795. }
  796. gs_texrender_t *const render = filter->render;
  797. gs_texrender_reset(render);
  798. gs_blend_state_push();
  799. gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
  800. if (gs_texrender_begin_with_color_space(render, filter->width, filter->height, source_space)) {
  801. struct vec4 clear_color;
  802. vec4_zero(&clear_color);
  803. gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
  804. gs_ortho(0.0f, (float)filter->width, 0.0f, (float)filter->height, -100.0f, 100.0f);
  805. if (target == parent && !custom_draw && !async)
  806. obs_source_default_render(target);
  807. else
  808. obs_source_video_render(target);
  809. gs_texrender_end(render);
  810. gs_texrender_t *const render_unorm = filter->render_unorm;
  811. gs_texrender_reset(render_unorm);
  812. if (gs_texrender_begin_with_color_space(render_unorm, filter->width, filter->height, GS_CS_SRGB)) {
  813. const bool previous = gs_framebuffer_srgb_enabled();
  814. gs_enable_framebuffer_srgb(true);
  815. gs_enable_blending(false);
  816. gs_ortho(0.0f, (float)filter->width, 0.0f, (float)filter->height, -100.0f, 100.0f);
  817. const char *tech_name = "ConvertUnorm";
  818. float multiplier = 1.f;
  819. if (!has_blur) {
  820. switch (source_space) {
  821. case GS_CS_709_EXTENDED:
  822. tech_name = "ConvertUnormTonemap";
  823. break;
  824. case GS_CS_709_SCRGB:
  825. tech_name = "ConvertUnormMultiplyTonemap";
  826. multiplier = 80.0f / obs_get_video_sdr_white_level();
  827. }
  828. } else {
  829. switch (source_space) {
  830. case GS_CS_709_SCRGB:
  831. tech_name = "ConvertUnormMultiply";
  832. multiplier = 80.0f / obs_get_video_sdr_white_level();
  833. }
  834. }
  835. gs_effect_set_texture_srgb(filter->image_param, gs_texrender_get_texture(render));
  836. gs_effect_set_float(filter->multiplier_param, multiplier);
  837. while (gs_effect_loop(filter->effect, tech_name)) {
  838. gs_draw(GS_TRIS, 0, 3);
  839. }
  840. gs_texrender_end(render_unorm);
  841. gs_enable_blending(true);
  842. gs_enable_framebuffer_srgb(previous);
  843. }
  844. }
  845. gs_blend_state_pop();
  846. /* 2. Initialize src_texture (only at startup or reset) */
  847. if (!filter->initial_render) {
  848. struct ID3D11Texture2D *d11texture2 =
  849. (struct ID3D11Texture2D *)gs_texture_get_obj(gs_texrender_get_texture(filter->render_unorm));
  850. if (!d11texture2) {
  851. error("Couldn't retrieve d3d11texture2d.");
  852. return;
  853. }
  854. if (!filter->src_img) {
  855. vfxErr = NvCVImage_Create(filter->width, filter->height, NVCV_BGRA, NVCV_U8, NVCV_CHUNKY,
  856. NVCV_GPU, 1, &filter->src_img);
  857. if (vfxErr != NVCV_SUCCESS) {
  858. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  859. error("Error creating src img; error %i: %s", vfxErr, errString);
  860. os_atomic_set_bool(&filter->processing_stop, true);
  861. return;
  862. }
  863. }
  864. vfxErr = NvCVImage_InitFromD3D11Texture(filter->src_img, d11texture2);
  865. if (vfxErr != NVCV_SUCCESS) {
  866. const char *errString = NvCV_GetErrorStringFromCode(vfxErr);
  867. error("Error passing src ID3D11Texture to img; error %i: %s", vfxErr, errString);
  868. os_atomic_set_bool(&filter->processing_stop, true);
  869. return;
  870. }
  871. filter->initial_render = true;
  872. }
  873. /* 3. Process FX (outputs a mask) & draw. */
  874. if (filter->initial_render && filter->images_allocated) {
  875. bool draw = true;
  876. if (!async || filter->got_new_frame) {
  877. if (!has_blur) {
  878. if (filter->processing_counter % filter->processing_interval == 0) {
  879. draw = process_texture(filter);
  880. filter->processing_counter = 1;
  881. } else {
  882. filter->processing_counter++;
  883. }
  884. } else {
  885. draw = process_texture(filter);
  886. }
  887. filter->got_new_frame = false;
  888. }
  889. if (draw) {
  890. draw_greenscreen_blur(filter, has_blur);
  891. filter->processed_frame = true;
  892. }
  893. } else {
  894. obs_source_skip_video_filter(filter->context);
  895. }
  896. UNUSED_PARAMETER(effect);
  897. }
  898. static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect)
  899. {
  900. nvvfx_filter_render(data, effect, false);
  901. }
  902. static void nv_blur_filter_render(void *data, gs_effect_t *effect)
  903. {
  904. nvvfx_filter_render(data, effect, true);
  905. }
  906. static enum gs_color_space nvvfx_filter_get_color_space(void *data, size_t count,
  907. const enum gs_color_space *preferred_spaces)
  908. {
  909. const enum gs_color_space potential_spaces[] = {
  910. GS_CS_SRGB,
  911. GS_CS_SRGB_16F,
  912. GS_CS_709_EXTENDED,
  913. };
  914. struct nvvfx_data *const filter = data;
  915. const enum gs_color_space source_space = obs_source_get_color_space(
  916. obs_filter_get_target(filter->context), OBS_COUNTOF(potential_spaces), potential_spaces);
  917. enum gs_color_space space = source_space;
  918. for (size_t i = 0; i < count; ++i) {
  919. space = preferred_spaces[i];
  920. if (space == source_space)
  921. break;
  922. }
  923. return space;
  924. }
  925. static obs_properties_t *nvvfx_filter_properties(void *data)
  926. {
  927. struct nvvfx_data *filter = (struct nvvfx_data *)data;
  928. obs_properties_t *props = obs_properties_create();
  929. if (filter->filter_id != S_FX_AIGS) {
  930. obs_property_t *strength =
  931. obs_properties_add_float_slider(props, S_STRENGTH, TEXT_MODE_BLUR_STRENGTH, 0, 1, 0.05);
  932. } else {
  933. obs_property_t *mode =
  934. obs_properties_add_list(props, S_MODE, TEXT_MODE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
  935. obs_property_list_add_int(mode, TEXT_MODE_QUALITY, S_MODE_QUALITY);
  936. obs_property_list_add_int(mode, TEXT_MODE_PERF, S_MODE_PERF);
  937. obs_property_list_add_int(mode, TEXT_MODE_QUALITY_CHAIR, S_MODE_QUALITY_CHAIR);
  938. obs_property_list_add_int(mode, TEXT_MODE_PERF_CHAIR, S_MODE_PERF_CHAIR);
  939. obs_property_t *threshold =
  940. obs_properties_add_float_slider(props, S_THRESHOLDFX, TEXT_MODE_THRESHOLD, 0, 1, 0.05);
  941. obs_property_t *partial = obs_properties_add_int_slider(props, S_PROCESSING, TEXT_PROCESSING, 1, 4, 1);
  942. obs_property_set_long_description(partial, TEXT_PROCESSING_HINT);
  943. }
  944. unsigned int version = get_lib_version();
  945. if (version && version < MIN_VFX_SDK_VERSION) {
  946. obs_property_t *warning = obs_properties_add_text(props, "deprecation", NULL, OBS_TEXT_INFO);
  947. obs_property_text_set_info_type(warning, OBS_TEXT_INFO_WARNING);
  948. obs_property_set_long_description(warning, TEXT_DEPRECATION);
  949. }
  950. return props;
  951. }
  952. static void nvvfx_filter_defaults(obs_data_t *settings)
  953. {
  954. obs_data_set_default_int(settings, S_MODE, S_MODE_QUALITY);
  955. obs_data_set_default_double(settings, S_THRESHOLDFX, S_THRESHOLDFX_DEFAULT);
  956. obs_data_set_default_int(settings, S_PROCESSING, 1);
  957. obs_data_set_default_double(settings, S_STRENGTH, S_STRENGTH_DEFAULT);
  958. }
  959. bool load_nvidia_vfx(void)
  960. {
  961. bool old_sdk_loaded = false;
  962. unsigned int version = get_lib_version();
  963. uint8_t major = (version >> 24) & 0xff;
  964. uint8_t minor = (version >> 16) & 0x00ff;
  965. uint8_t build = (version >> 8) & 0x0000ff;
  966. uint8_t revision = (version >> 0) & 0x000000ff;
  967. if (version) {
  968. blog(LOG_INFO, "[NVIDIA VIDEO FX]: NVIDIA VIDEO FX version: %i.%i.%i.%i", major, minor, build,
  969. revision);
  970. if (version < MIN_VFX_SDK_VERSION) {
  971. blog(LOG_INFO,
  972. "[NVIDIA VIDEO FX]: NVIDIA VIDEO Effects SDK is outdated. Please update both audio & video SDK.");
  973. }
  974. }
  975. if (!load_nv_vfx_libs()) {
  976. blog(LOG_INFO, "[NVIDIA VIDEO FX]: FX disabled, redistributable not found or could not be loaded.");
  977. return false;
  978. }
  979. #define LOAD_SYM_FROM_LIB(sym, lib, dll) \
  980. if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) { \
  981. DWORD err = GetLastError(); \
  982. printf("[NVIDIA VIDEO FX]: Couldn't load " #sym " from " dll ": %lu (0x%lx)", err, err); \
  983. release_nv_vfx(); \
  984. goto unload_everything; \
  985. }
  986. #define LOAD_SYM_FROM_LIB2(sym, lib, dll) \
  987. if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) { \
  988. DWORD err = GetLastError(); \
  989. printf("[NVIDIA VIDEO FX]: Couldn't load " #sym " from " dll ": %lu (0x%lx)", err, err); \
  990. nvvfx_new_sdk = false; \
  991. } else { \
  992. nvvfx_new_sdk = true; \
  993. }
  994. #define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_videofx, "NVVideoEffects.dll")
  995. LOAD_SYM(NvVFX_GetVersion);
  996. LOAD_SYM(NvVFX_CreateEffect);
  997. LOAD_SYM(NvVFX_DestroyEffect);
  998. LOAD_SYM(NvVFX_SetU32);
  999. LOAD_SYM(NvVFX_SetS32);
  1000. LOAD_SYM(NvVFX_SetF32);
  1001. LOAD_SYM(NvVFX_SetF64);
  1002. LOAD_SYM(NvVFX_SetU64);
  1003. LOAD_SYM(NvVFX_SetObject);
  1004. LOAD_SYM(NvVFX_SetCudaStream);
  1005. LOAD_SYM(NvVFX_SetImage);
  1006. LOAD_SYM(NvVFX_SetString);
  1007. LOAD_SYM(NvVFX_GetU32);
  1008. LOAD_SYM(NvVFX_GetS32);
  1009. LOAD_SYM(NvVFX_GetF32);
  1010. LOAD_SYM(NvVFX_GetF64);
  1011. LOAD_SYM(NvVFX_GetU64);
  1012. LOAD_SYM(NvVFX_GetObject);
  1013. LOAD_SYM(NvVFX_GetCudaStream);
  1014. LOAD_SYM(NvVFX_GetImage);
  1015. LOAD_SYM(NvVFX_GetString);
  1016. LOAD_SYM(NvVFX_Run);
  1017. LOAD_SYM(NvVFX_Load);
  1018. LOAD_SYM(NvVFX_CudaStreamCreate);
  1019. LOAD_SYM(NvVFX_CudaStreamDestroy);
  1020. LOAD_SYM(NvVFX_SetStateObjectHandleArray);
  1021. LOAD_SYM(NvVFX_AllocateState);
  1022. LOAD_SYM(NvVFX_DeallocateState);
  1023. LOAD_SYM(NvVFX_ResetState);
  1024. old_sdk_loaded = true;
  1025. #undef LOAD_SYM
  1026. #define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_cvimage, "NVCVImage.dll")
  1027. LOAD_SYM(NvCV_GetErrorStringFromCode);
  1028. LOAD_SYM(NvCVImage_Init);
  1029. LOAD_SYM(NvCVImage_InitView);
  1030. LOAD_SYM(NvCVImage_Alloc);
  1031. LOAD_SYM(NvCVImage_Realloc);
  1032. LOAD_SYM(NvCVImage_Dealloc);
  1033. LOAD_SYM(NvCVImage_Create);
  1034. LOAD_SYM(NvCVImage_Destroy);
  1035. LOAD_SYM(NvCVImage_ComponentOffsets);
  1036. LOAD_SYM(NvCVImage_Transfer);
  1037. LOAD_SYM(NvCVImage_TransferRect);
  1038. LOAD_SYM(NvCVImage_TransferFromYUV);
  1039. LOAD_SYM(NvCVImage_TransferToYUV);
  1040. LOAD_SYM(NvCVImage_MapResource);
  1041. LOAD_SYM(NvCVImage_UnmapResource);
  1042. LOAD_SYM(NvCVImage_Composite);
  1043. LOAD_SYM(NvCVImage_CompositeRect);
  1044. LOAD_SYM(NvCVImage_CompositeOverConstant);
  1045. LOAD_SYM(NvCVImage_FlipY);
  1046. LOAD_SYM(NvCVImage_GetYUVPointers);
  1047. LOAD_SYM(NvCVImage_InitFromD3D11Texture);
  1048. LOAD_SYM(NvCVImage_ToD3DFormat);
  1049. LOAD_SYM(NvCVImage_FromD3DFormat);
  1050. LOAD_SYM(NvCVImage_ToD3DColorSpace);
  1051. LOAD_SYM(NvCVImage_FromD3DColorSpace);
  1052. #undef LOAD_SYM
  1053. #define LOAD_SYM(sym) LOAD_SYM_FROM_LIB2(sym, nv_videofx, "NVVideoEffects.dll")
  1054. LOAD_SYM(NvVFX_ConfigureLogger);
  1055. if (!nvvfx_new_sdk) {
  1056. blog(LOG_INFO, "[NVIDIA VIDEO FX]: SDK loaded but old redistributable detected. Please upgrade.");
  1057. }
  1058. #undef LOAD_SYM
  1059. int err;
  1060. NvVFX_Handle h = NULL;
  1061. /* load the effect to check if the GPU is supported */
  1062. err = NvVFX_CreateEffect(NVVFX_FX_GREEN_SCREEN, &h);
  1063. if (err != NVCV_SUCCESS) {
  1064. if (err == NVCV_ERR_UNSUPPORTEDGPU) {
  1065. blog(LOG_INFO, "[NVIDIA VIDEO FX]: disabled, unsupported GPU");
  1066. } else {
  1067. blog(LOG_ERROR, "[NVIDIA VIDEO FX]: disabled, error %i", err);
  1068. }
  1069. goto unload_everything;
  1070. }
  1071. NvVFX_DestroyEffect(h);
  1072. nvvfx_loaded = true;
  1073. blog(LOG_INFO, "[NVIDIA VIDEO FX]: enabled, redistributable found");
  1074. return true;
  1075. unload_everything:
  1076. nvvfx_loaded = false;
  1077. blog(LOG_INFO, "[NVIDIA VIDEO FX]: disabled, redistributable not found");
  1078. release_nv_vfx();
  1079. return false;
  1080. }
  1081. void unload_nvidia_vfx(void)
  1082. {
  1083. release_nv_vfx();
  1084. }
  1085. struct obs_source_info nvidia_greenscreen_filter_info = {
  1086. .id = "nv_greenscreen_filter",
  1087. .type = OBS_SOURCE_TYPE_FILTER,
  1088. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB,
  1089. .get_name = nv_greenscreen_filter_name,
  1090. .create = nv_greenscreen_filter_create,
  1091. .destroy = nvvfx_filter_destroy,
  1092. .get_defaults = nvvfx_filter_defaults,
  1093. .get_properties = nvvfx_filter_properties,
  1094. .update = nvvfx_filter_update,
  1095. .filter_video = nvvfx_filter_video,
  1096. .video_render = nv_greenscreen_filter_render,
  1097. .video_tick = nvvfx_filter_tick,
  1098. .video_get_color_space = nvvfx_filter_get_color_space,
  1099. };
  1100. struct obs_source_info nvidia_blur_filter_info = {
  1101. .id = "nv_blur_filter",
  1102. .type = OBS_SOURCE_TYPE_FILTER,
  1103. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB,
  1104. .get_name = nv_blur_filter_name,
  1105. .create = nv_blur_filter_create,
  1106. .destroy = nvvfx_filter_destroy,
  1107. .get_defaults = nvvfx_filter_defaults,
  1108. .get_properties = nvvfx_filter_properties,
  1109. .update = nvvfx_filter_update,
  1110. .filter_video = nvvfx_filter_video,
  1111. .video_render = nv_blur_filter_render,
  1112. .video_tick = nvvfx_filter_tick,
  1113. .video_get_color_space = nvvfx_filter_get_color_space,
  1114. };
  1115. struct obs_source_info nvidia_background_blur_filter_info = {
  1116. .id = "nv_background_blur_filter",
  1117. .type = OBS_SOURCE_TYPE_FILTER,
  1118. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB,
  1119. .get_name = nv_background_blur_filter_name,
  1120. .create = nv_background_blur_filter_create,
  1121. .destroy = nvvfx_filter_destroy,
  1122. .get_defaults = nvvfx_filter_defaults,
  1123. .get_properties = nvvfx_filter_properties,
  1124. .update = nvvfx_filter_update,
  1125. .filter_video = nvvfx_filter_video,
  1126. .video_render = nv_blur_filter_render,
  1127. .video_tick = nvvfx_filter_tick,
  1128. .video_get_color_space = nvvfx_filter_get_color_space,
  1129. };