winrt-capture.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. #include "winrt-capture.h"
  2. extern "C" EXPORT BOOL winrt_capture_supported()
  3. try {
  4. /* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
  5. return winrt::Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent(
  6. L"Windows.Foundation.UniversalApiContract", 8);
  7. } catch (const winrt::hresult_error &err) {
  8. blog(LOG_ERROR, "winrt_capture_supported (0x%08X): %s", err.code().value,
  9. winrt::to_string(err.message()).c_str());
  10. return false;
  11. } catch (...) {
  12. blog(LOG_ERROR, "winrt_capture_supported (0x%08X)", winrt::to_hresult().value);
  13. return false;
  14. }
  15. extern "C" EXPORT BOOL winrt_capture_cursor_toggle_supported()
  16. try {
  17. return winrt::Windows::Foundation::Metadata::ApiInformation::IsPropertyPresent(
  18. L"Windows.Graphics.Capture.GraphicsCaptureSession", L"IsCursorCaptureEnabled");
  19. } catch (const winrt::hresult_error &err) {
  20. blog(LOG_ERROR, "winrt_capture_cursor_toggle_supported (0x%08X): %s", err.code().value,
  21. winrt::to_string(err.message()).c_str());
  22. return false;
  23. } catch (...) {
  24. blog(LOG_ERROR, "winrt_capture_cursor_toggle_supported (0x%08X)", winrt::to_hresult().value);
  25. return false;
  26. }
  27. template<typename T>
  28. static winrt::com_ptr<T> GetDXGIInterfaceFromObject(winrt::Windows::Foundation::IInspectable const &object)
  29. {
  30. auto access = object.as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
  31. winrt::com_ptr<T> result;
  32. winrt::check_hresult(access->GetInterface(winrt::guid_of<T>(), result.put_void()));
  33. return result;
  34. }
  35. static bool get_client_box(HWND window, uint32_t width, uint32_t height, D3D11_BOX *client_box)
  36. {
  37. RECT client_rect{}, window_rect{};
  38. POINT upper_left{};
  39. /* check iconic (minimized) twice, ABA is very unlikely */
  40. bool client_box_available = !IsIconic(window) && GetClientRect(window, &client_rect) && !IsIconic(window) &&
  41. (client_rect.right > 0) && (client_rect.bottom > 0) &&
  42. (DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &window_rect,
  43. sizeof(window_rect)) == S_OK) &&
  44. ClientToScreen(window, &upper_left);
  45. if (client_box_available) {
  46. const uint32_t left = (upper_left.x > window_rect.left) ? (upper_left.x - window_rect.left) : 0;
  47. client_box->left = left;
  48. const uint32_t top = (upper_left.y > window_rect.top) ? (upper_left.y - window_rect.top) : 0;
  49. client_box->top = top;
  50. uint32_t texture_width = 1;
  51. if (width > left) {
  52. texture_width = std::min(width - left, (uint32_t)client_rect.right);
  53. }
  54. uint32_t texture_height = 1;
  55. if (height > top) {
  56. texture_height = std::min(height - top, (uint32_t)client_rect.bottom);
  57. }
  58. client_box->right = left + texture_width;
  59. client_box->bottom = top + texture_height;
  60. client_box->front = 0;
  61. client_box->back = 1;
  62. client_box_available = (client_box->right <= width) && (client_box->bottom <= height);
  63. }
  64. return client_box_available;
  65. }
  66. static DXGI_FORMAT get_pixel_format(HWND window, HMONITOR monitor, BOOL force_sdr)
  67. {
  68. static constexpr DXGI_FORMAT sdr_format = DXGI_FORMAT_B8G8R8A8_UNORM;
  69. if (force_sdr)
  70. return sdr_format;
  71. if (window)
  72. monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
  73. return (monitor && gs_is_monitor_hdr(monitor)) ? DXGI_FORMAT_R16G16B16A16_FLOAT : sdr_format;
  74. }
  75. struct winrt_capture {
  76. HWND window;
  77. BOOL client_area;
  78. BOOL force_sdr;
  79. HMONITOR monitor;
  80. DXGI_FORMAT format;
  81. bool capture_cursor;
  82. BOOL cursor_visible;
  83. gs_texture_t *texture;
  84. bool texture_written;
  85. winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
  86. winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device{nullptr};
  87. ComPtr<ID3D11DeviceContext> context;
  88. winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool{nullptr};
  89. winrt::Windows::Graphics::Capture::GraphicsCaptureSession session{nullptr};
  90. winrt::Windows::Graphics::SizeInt32 last_size;
  91. winrt::Windows::Graphics::Capture::GraphicsCaptureItem::Closed_revoker closed;
  92. winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::FrameArrived_revoker frame_arrived;
  93. uint32_t texture_width;
  94. uint32_t texture_height;
  95. D3D11_BOX client_box;
  96. BOOL active;
  97. struct winrt_capture *next;
  98. void on_closed(winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &,
  99. winrt::Windows::Foundation::IInspectable const &)
  100. {
  101. active = FALSE;
  102. }
  103. void on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender,
  104. winrt::Windows::Foundation::IInspectable const &)
  105. {
  106. const winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame = sender.TryGetNextFrame();
  107. const winrt::Windows::Graphics::SizeInt32 frame_content_size = frame.ContentSize();
  108. winrt::com_ptr<ID3D11Texture2D> frame_surface =
  109. GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
  110. /* need GetDesc because ContentSize is not reliable */
  111. D3D11_TEXTURE2D_DESC desc;
  112. frame_surface->GetDesc(&desc);
  113. obs_enter_graphics();
  114. if (desc.Format == get_pixel_format(window, monitor, force_sdr)) {
  115. if (!client_area || get_client_box(window, desc.Width, desc.Height, &client_box)) {
  116. if (client_area) {
  117. texture_width = client_box.right - client_box.left;
  118. texture_height = client_box.bottom - client_box.top;
  119. } else {
  120. texture_width = desc.Width;
  121. texture_height = desc.Height;
  122. }
  123. if (texture) {
  124. if (texture_width != gs_texture_get_width(texture) ||
  125. texture_height != gs_texture_get_height(texture)) {
  126. gs_texture_destroy(texture);
  127. texture = nullptr;
  128. }
  129. }
  130. if (!texture) {
  131. const gs_color_format color_format =
  132. desc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT ? GS_RGBA16F : GS_BGRA;
  133. texture = gs_texture_create(texture_width, texture_height, color_format, 1,
  134. NULL, 0);
  135. }
  136. if (client_area) {
  137. context->CopySubresourceRegion((ID3D11Texture2D *)gs_texture_get_obj(texture),
  138. 0, 0, 0, 0, frame_surface.get(), 0, &client_box);
  139. } else {
  140. /* if they gave an SRV, we could avoid this copy */
  141. context->CopyResource((ID3D11Texture2D *)gs_texture_get_obj(texture),
  142. frame_surface.get());
  143. }
  144. texture_written = true;
  145. }
  146. if (frame_content_size.Width != last_size.Width ||
  147. frame_content_size.Height != last_size.Height) {
  148. format = desc.Format;
  149. frame_pool.Recreate(
  150. device,
  151. static_cast<winrt::Windows::Graphics::DirectX::DirectXPixelFormat>(format), 2,
  152. frame_content_size);
  153. last_size = frame_content_size;
  154. }
  155. } else {
  156. active = FALSE;
  157. }
  158. obs_leave_graphics();
  159. }
  160. };
  161. static struct winrt_capture *capture_list;
  162. static void winrt_capture_device_loss_release(void *data)
  163. {
  164. winrt_capture *capture = static_cast<winrt_capture *>(data);
  165. capture->active = FALSE;
  166. capture->frame_arrived.revoke();
  167. try {
  168. capture->frame_pool.Close();
  169. } catch (winrt::hresult_error &err) {
  170. blog(LOG_ERROR, "Direct3D11CaptureFramePool::Close (0x%08X): %s", err.code().value,
  171. winrt::to_string(err.message()).c_str());
  172. } catch (...) {
  173. blog(LOG_ERROR, "Direct3D11CaptureFramePool::Close (0x%08X)", winrt::to_hresult().value);
  174. }
  175. try {
  176. capture->session.Close();
  177. } catch (winrt::hresult_error &err) {
  178. blog(LOG_ERROR, "GraphicsCaptureSession::Close (0x%08X): %s", err.code().value,
  179. winrt::to_string(err.message()).c_str());
  180. } catch (...) {
  181. blog(LOG_ERROR, "GraphicsCaptureSession::Close (0x%08X)", winrt::to_hresult().value);
  182. }
  183. capture->session = nullptr;
  184. capture->frame_pool = nullptr;
  185. capture->context = nullptr;
  186. capture->device = nullptr;
  187. capture->item = nullptr;
  188. }
  189. static bool winrt_capture_border_toggle_supported()
  190. try {
  191. return winrt::Windows::Foundation::Metadata::ApiInformation::IsPropertyPresent(
  192. L"Windows.Graphics.Capture.GraphicsCaptureSession", L"IsBorderRequired");
  193. } catch (const winrt::hresult_error &err) {
  194. blog(LOG_ERROR, "winrt_capture_border_toggle_supported (0x%08X): %s", err.code().value,
  195. winrt::to_string(err.message()).c_str());
  196. return false;
  197. } catch (...) {
  198. blog(LOG_ERROR, "winrt_capture_border_toggle_supported (0x%08X)", winrt::to_hresult().value);
  199. return false;
  200. }
  201. static winrt::Windows::Graphics::Capture::GraphicsCaptureItem
  202. winrt_capture_create_item(IGraphicsCaptureItemInterop *const interop_factory, HWND window, HMONITOR monitor)
  203. {
  204. winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
  205. if (window) {
  206. try {
  207. const HRESULT hr = interop_factory->CreateForWindow(
  208. window, winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
  209. reinterpret_cast<void **>(winrt::put_abi(item)));
  210. if (FAILED(hr))
  211. blog(LOG_ERROR, "CreateForWindow (0x%08X)", hr);
  212. } catch (winrt::hresult_error &err) {
  213. blog(LOG_ERROR, "CreateForWindow (0x%08X): %s", err.code().value,
  214. winrt::to_string(err.message()).c_str());
  215. } catch (...) {
  216. blog(LOG_ERROR, "CreateForWindow (0x%08X)", winrt::to_hresult().value);
  217. }
  218. } else {
  219. assert(monitor);
  220. try {
  221. const HRESULT hr = interop_factory->CreateForMonitor(
  222. monitor, winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
  223. reinterpret_cast<void **>(winrt::put_abi(item)));
  224. if (FAILED(hr))
  225. blog(LOG_ERROR, "CreateForMonitor (0x%08X)", hr);
  226. } catch (winrt::hresult_error &err) {
  227. blog(LOG_ERROR, "CreateForMonitor (0x%08X): %s", err.code().value,
  228. winrt::to_string(err.message()).c_str());
  229. } catch (...) {
  230. blog(LOG_ERROR, "CreateForMonitor (0x%08X)", winrt::to_hresult().value);
  231. }
  232. }
  233. return item;
  234. }
  235. static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
  236. {
  237. winrt_capture *capture = static_cast<winrt_capture *>(data);
  238. auto activation_factory =
  239. winrt::get_activation_factory<winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
  240. auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
  241. winrt::Windows::Graphics::Capture::GraphicsCaptureItem item =
  242. winrt_capture_create_item(interop_factory.get(), capture->window, capture->monitor);
  243. if (!item)
  244. return;
  245. ID3D11Device *const d3d_device = (ID3D11Device *)device_void;
  246. ComPtr<IDXGIDevice> dxgi_device;
  247. if (FAILED(d3d_device->QueryInterface(&dxgi_device)))
  248. blog(LOG_ERROR, "Failed to get DXGI device");
  249. winrt::com_ptr<IInspectable> inspectable;
  250. if (FAILED(CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(), inspectable.put())))
  251. blog(LOG_ERROR, "Failed to get WinRT device");
  252. const winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device =
  253. inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
  254. const winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool =
  255. winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::Create(
  256. device, static_cast<winrt::Windows::Graphics::DirectX::DirectXPixelFormat>(capture->format), 2,
  257. capture->last_size);
  258. const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session = frame_pool.CreateCaptureSession(item);
  259. if (winrt_capture_border_toggle_supported()) {
  260. winrt::Windows::Graphics::Capture::GraphicsCaptureAccess::RequestAccessAsync(
  261. winrt::Windows::Graphics::Capture::GraphicsCaptureAccessKind::Borderless)
  262. .get();
  263. session.IsBorderRequired(false);
  264. }
  265. if (winrt_capture_cursor_toggle_supported())
  266. session.IsCursorCaptureEnabled(capture->capture_cursor && capture->cursor_visible);
  267. capture->item = item;
  268. capture->device = device;
  269. d3d_device->GetImmediateContext(&capture->context);
  270. capture->frame_pool = frame_pool;
  271. capture->session = session;
  272. capture->frame_arrived =
  273. frame_pool.FrameArrived(winrt::auto_revoke, {capture, &winrt_capture::on_frame_arrived});
  274. try {
  275. session.StartCapture();
  276. capture->active = TRUE;
  277. } catch (winrt::hresult_error &err) {
  278. blog(LOG_ERROR, "StartCapture (0x%08X): %s", err.code().value, winrt::to_string(err.message()).c_str());
  279. } catch (...) {
  280. blog(LOG_ERROR, "StartCapture (0x%08X)", winrt::to_hresult().value);
  281. }
  282. }
  283. static struct winrt_capture *winrt_capture_init_internal(BOOL cursor, HWND window, BOOL client_area, BOOL force_sdr,
  284. HMONITOR monitor)
  285. try {
  286. ID3D11Device *const d3d_device = (ID3D11Device *)gs_get_device_obj();
  287. ComPtr<IDXGIDevice> dxgi_device;
  288. HRESULT hr = d3d_device->QueryInterface(&dxgi_device);
  289. if (FAILED(hr)) {
  290. blog(LOG_ERROR, "Failed to get DXGI device");
  291. return nullptr;
  292. }
  293. winrt::com_ptr<IInspectable> inspectable;
  294. hr = CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(), inspectable.put());
  295. if (FAILED(hr)) {
  296. blog(LOG_ERROR, "Failed to get WinRT device");
  297. return nullptr;
  298. }
  299. auto activation_factory =
  300. winrt::get_activation_factory<winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
  301. auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
  302. winrt::Windows::Graphics::Capture::GraphicsCaptureItem item =
  303. winrt_capture_create_item(interop_factory.get(), window, monitor);
  304. if (!item)
  305. return nullptr;
  306. const winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device =
  307. inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
  308. const winrt::Windows::Graphics::SizeInt32 size = item.Size();
  309. const DXGI_FORMAT format = get_pixel_format(window, monitor, force_sdr);
  310. const winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool =
  311. winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::Create(
  312. device, static_cast<winrt::Windows::Graphics::DirectX::DirectXPixelFormat>(format), 2, size);
  313. const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session = frame_pool.CreateCaptureSession(item);
  314. if (winrt_capture_border_toggle_supported()) {
  315. winrt::Windows::Graphics::Capture::GraphicsCaptureAccess::RequestAccessAsync(
  316. winrt::Windows::Graphics::Capture::GraphicsCaptureAccessKind::Borderless)
  317. .get();
  318. session.IsBorderRequired(false);
  319. }
  320. /* disable cursor capture if possible since ours performs better */
  321. const BOOL cursor_toggle_supported = winrt_capture_cursor_toggle_supported();
  322. if (cursor_toggle_supported)
  323. session.IsCursorCaptureEnabled(cursor);
  324. struct winrt_capture *capture = new winrt_capture{};
  325. capture->window = window;
  326. capture->client_area = client_area;
  327. capture->force_sdr = force_sdr;
  328. capture->monitor = monitor;
  329. capture->format = format;
  330. capture->capture_cursor = cursor && cursor_toggle_supported;
  331. capture->cursor_visible = cursor;
  332. capture->item = item;
  333. capture->device = device;
  334. d3d_device->GetImmediateContext(&capture->context);
  335. capture->frame_pool = frame_pool;
  336. capture->session = session;
  337. capture->last_size = size;
  338. capture->closed = item.Closed(winrt::auto_revoke, {capture, &winrt_capture::on_closed});
  339. capture->frame_arrived =
  340. frame_pool.FrameArrived(winrt::auto_revoke, {capture, &winrt_capture::on_frame_arrived});
  341. capture->next = capture_list;
  342. capture_list = capture;
  343. session.StartCapture();
  344. capture->active = TRUE;
  345. gs_device_loss callbacks;
  346. callbacks.device_loss_release = winrt_capture_device_loss_release;
  347. callbacks.device_loss_rebuild = winrt_capture_device_loss_rebuild;
  348. callbacks.data = capture;
  349. gs_register_loss_callbacks(&callbacks);
  350. return capture;
  351. } catch (const winrt::hresult_error &err) {
  352. blog(LOG_ERROR, "winrt_capture_init (0x%08X): %s", err.code().value, winrt::to_string(err.message()).c_str());
  353. return nullptr;
  354. } catch (...) {
  355. blog(LOG_ERROR, "winrt_capture_init (0x%08X)", winrt::to_hresult().value);
  356. return nullptr;
  357. }
  358. extern "C" EXPORT struct winrt_capture *winrt_capture_init_window(BOOL cursor, HWND window, BOOL client_area,
  359. BOOL force_sdr)
  360. {
  361. return winrt_capture_init_internal(cursor, window, client_area, force_sdr, NULL);
  362. }
  363. extern "C" EXPORT struct winrt_capture *winrt_capture_init_monitor(BOOL cursor, HMONITOR monitor, BOOL force_sdr)
  364. {
  365. return winrt_capture_init_internal(cursor, NULL, false, force_sdr, monitor);
  366. }
  367. extern "C" EXPORT void winrt_capture_free(struct winrt_capture *capture)
  368. {
  369. if (capture) {
  370. struct winrt_capture *current = capture_list;
  371. if (current == capture) {
  372. capture_list = capture->next;
  373. } else {
  374. struct winrt_capture *previous;
  375. do {
  376. previous = current;
  377. current = current->next;
  378. } while (current != capture);
  379. previous->next = current->next;
  380. }
  381. obs_enter_graphics();
  382. gs_unregister_loss_callbacks(capture);
  383. gs_texture_destroy(capture->texture);
  384. obs_leave_graphics();
  385. capture->frame_arrived.revoke();
  386. capture->closed.revoke();
  387. try {
  388. if (capture->frame_pool)
  389. capture->frame_pool.Close();
  390. } catch (winrt::hresult_error &err) {
  391. blog(LOG_ERROR, "Direct3D11CaptureFramePool::Close (0x%08X): %s", err.code().value,
  392. winrt::to_string(err.message()).c_str());
  393. } catch (...) {
  394. blog(LOG_ERROR, "Direct3D11CaptureFramePool::Close (0x%08X)", winrt::to_hresult().value);
  395. }
  396. try {
  397. if (capture->session)
  398. capture->session.Close();
  399. } catch (winrt::hresult_error &err) {
  400. blog(LOG_ERROR, "GraphicsCaptureSession::Close (0x%08X): %s", err.code().value,
  401. winrt::to_string(err.message()).c_str());
  402. } catch (...) {
  403. blog(LOG_ERROR, "GraphicsCaptureSession::Close (0x%08X)", winrt::to_hresult().value);
  404. }
  405. delete capture;
  406. }
  407. }
  408. extern "C" EXPORT BOOL winrt_capture_active(const struct winrt_capture *capture)
  409. {
  410. return capture->active;
  411. }
  412. extern "C" EXPORT BOOL winrt_capture_show_cursor(struct winrt_capture *capture, BOOL visible)
  413. {
  414. BOOL success = FALSE;
  415. try {
  416. if (capture->capture_cursor) {
  417. if (capture->cursor_visible != visible) {
  418. capture->session.IsCursorCaptureEnabled(visible);
  419. capture->cursor_visible = visible;
  420. }
  421. }
  422. success = TRUE;
  423. } catch (winrt::hresult_error &err) {
  424. blog(LOG_ERROR, "GraphicsCaptureSession::IsCursorCaptureEnabled (0x%08X): %s", err.code().value,
  425. winrt::to_string(err.message()).c_str());
  426. } catch (...) {
  427. blog(LOG_ERROR, "GraphicsCaptureSession::IsCursorCaptureEnabled (0x%08X)", winrt::to_hresult().value);
  428. }
  429. return success;
  430. }
  431. extern "C" EXPORT enum gs_color_space winrt_capture_get_color_space(const struct winrt_capture *capture)
  432. {
  433. return (capture->format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? GS_CS_709_EXTENDED : GS_CS_SRGB;
  434. }
  435. extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture)
  436. {
  437. if (capture->texture_written) {
  438. const char *tech_name = "Draw";
  439. float multiplier = 1.f;
  440. const gs_color_space current_space = gs_get_color_space();
  441. if (capture->format == DXGI_FORMAT_R16G16B16A16_FLOAT) {
  442. switch (current_space) {
  443. case GS_CS_SRGB:
  444. case GS_CS_SRGB_16F:
  445. tech_name = "DrawMultiplyTonemap";
  446. multiplier = 80.f / obs_get_video_sdr_white_level();
  447. break;
  448. case GS_CS_709_EXTENDED:
  449. tech_name = "DrawMultiply";
  450. multiplier = 80.f / obs_get_video_sdr_white_level();
  451. }
  452. } else if (current_space == GS_CS_709_SCRGB) {
  453. tech_name = "DrawMultiply";
  454. multiplier = obs_get_video_sdr_white_level() / 80.f;
  455. }
  456. gs_effect_t *const effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
  457. gs_technique_t *tech = gs_effect_get_technique(effect, tech_name);
  458. const bool previous = gs_framebuffer_srgb_enabled();
  459. gs_enable_framebuffer_srgb(true);
  460. gs_blend_state_push();
  461. gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
  462. gs_texture_t *const texture = capture->texture;
  463. gs_effect_set_texture_srgb(gs_effect_get_param_by_name(effect, "image"), texture);
  464. gs_effect_set_float(gs_effect_get_param_by_name(effect, "multiplier"), multiplier);
  465. const size_t passes = gs_technique_begin(tech);
  466. for (size_t i = 0; i < passes; i++) {
  467. if (gs_technique_begin_pass(tech, i)) {
  468. gs_draw_sprite(texture, 0, 0, 0);
  469. gs_technique_end_pass(tech);
  470. }
  471. }
  472. gs_technique_end(tech);
  473. gs_blend_state_pop();
  474. gs_enable_framebuffer_srgb(previous);
  475. }
  476. }
  477. extern "C" EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture)
  478. {
  479. return capture ? capture->texture_width : 0;
  480. }
  481. extern "C" EXPORT uint32_t winrt_capture_height(const struct winrt_capture *capture)
  482. {
  483. return capture ? capture->texture_height : 0;
  484. }
  485. extern "C" EXPORT void winrt_capture_thread_start()
  486. {
  487. struct winrt_capture *capture = capture_list;
  488. void *const device = gs_get_device_obj();
  489. while (capture) {
  490. winrt_capture_device_loss_rebuild(device, capture);
  491. capture = capture->next;
  492. }
  493. }
  494. extern "C" EXPORT void winrt_capture_thread_stop()
  495. {
  496. struct winrt_capture *capture = capture_list;
  497. while (capture) {
  498. winrt_capture_device_loss_release(capture);
  499. capture = capture->next;
  500. }
  501. }