diff --git a/libobs-winrt/CMakeLists.txt b/libobs-winrt/CMakeLists.txt index fe08063237028b..70f0c39205363e 100644 --- a/libobs-winrt/CMakeLists.txt +++ b/libobs-winrt/CMakeLists.txt @@ -7,10 +7,12 @@ add_definitions(-DLIBOBS_EXPORTS) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(libobs-winrt_SOURCES - winrt-capture.cpp) + winrt-capture.cpp + winrt-dispatch.cpp) set(libobs-winrt_HEADERS - winrt-capture.h) + winrt-capture.h + winrt-dispatch.h) add_library(libobs-winrt MODULE ${libobs-winrt_SOURCES} @@ -25,10 +27,12 @@ target_precompile_headers(libobs-winrt [["../libobs/util/windows/ComPtr.hpp"]] + - ) + + ) target_link_libraries(libobs-winrt libobs Dwmapi diff --git a/libobs-winrt/winrt-capture.cpp b/libobs-winrt/winrt-capture.cpp index 47c613706be446..c05787a04efa3d 100644 --- a/libobs-winrt/winrt-capture.cpp +++ b/libobs-winrt/winrt-capture.cpp @@ -138,7 +138,6 @@ struct winrt_capture { uint32_t texture_height; D3D11_BOX client_box; - bool thread_changed; bool active; struct winrt_capture *next; @@ -283,7 +282,7 @@ struct winrt_capture { } }; -struct winrt_capture *capture_list; +static struct winrt_capture *capture_list; static void winrt_capture_device_loss_release(void *data) { @@ -296,12 +295,32 @@ static void winrt_capture_device_loss_release(void *data) capture->frame_pool = nullptr; capture->context = nullptr; capture->device = nullptr; + capture->item = nullptr; } static void winrt_capture_device_loss_rebuild(void *device_void, void *data) { winrt_capture *capture = static_cast(data); + auto activation_factory = winrt::get_activation_factory< + winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); + auto interop_factory = + activation_factory.as(); + winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; + try { + interop_factory->CreateForWindow( + capture->window, + winrt::guid_of(), + reinterpret_cast(winrt::put_abi(item))); + } catch (winrt::hresult_error &err) { + blog(LOG_ERROR, "CreateForWindow (0x%08X): %ls", err.to_abi(), + err.message().c_str()); + } catch (...) { + blog(LOG_ERROR, "CreateForWindow (0x%08X)", + winrt::to_hresult()); + } + ID3D11Device *const d3d_device = (ID3D11Device *)device_void; ComPtr dxgi_device; if (FAILED(d3d_device->QueryInterface(&dxgi_device))) @@ -323,7 +342,7 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data) DirectXPixelFormat::B8G8R8A8UIntNormalized, 2, capture->last_size); const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session = - frame_pool.CreateCaptureSession(capture->item); + frame_pool.CreateCaptureSession(item); /* disable cursor capture if possible since ours performs better */ #ifdef NTDDI_WIN10_VB @@ -331,6 +350,7 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data) session.IsCursorCaptureEnabled(false); #endif + capture->item = item; capture->device = device; d3d_device->GetImmediateContext(&capture->context); capture->frame_pool = frame_pool; @@ -342,8 +362,6 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data) session.StartCapture(); } -thread_local bool initialized_tls; - extern "C" EXPORT struct winrt_capture * winrt_capture_init(BOOL cursor, HWND window, BOOL client_area) try { @@ -407,9 +425,6 @@ try { session.IsCursorCaptureEnabled(false); #endif - if (capture_list == nullptr) - initialized_tls = true; - struct winrt_capture *capture = new winrt_capture{}; capture->window = window; capture->client_area = client_area; @@ -512,28 +527,8 @@ extern "C" EXPORT void winrt_capture_show_cursor(struct winrt_capture *capture, extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture, gs_effect_t *effect) { - if (capture->texture_written) { - if (!initialized_tls) { - struct winrt_capture *current = capture_list; - while (current) { - current->thread_changed = true; - current = current->next; - } - - initialized_tls = true; - } - - if (capture->thread_changed) { - /* new graphics thread. treat like device loss. */ - winrt_capture_device_loss_release(capture); - winrt_capture_device_loss_rebuild(gs_get_device_obj(), - capture); - - capture->thread_changed = false; - } - + if (capture->texture_written) draw_texture(capture, effect); - } } extern "C" EXPORT uint32_t @@ -547,3 +542,22 @@ winrt_capture_height(const struct winrt_capture *capture) { return capture ? capture->texture_height : 0; } + +extern "C" EXPORT void winrt_capture_thread_start() +{ + struct winrt_capture *capture = capture_list; + void *const device = gs_get_device_obj(); + while (capture) { + winrt_capture_device_loss_rebuild(device, capture); + capture = capture->next; + } +} + +extern "C" EXPORT void winrt_capture_thread_stop() +{ + struct winrt_capture *capture = capture_list; + while (capture) { + winrt_capture_device_loss_release(capture); + capture = capture->next; + } +} diff --git a/libobs-winrt/winrt-capture.h b/libobs-winrt/winrt-capture.h index 8153959466000c..67904fe0e83da6 100644 --- a/libobs-winrt/winrt-capture.h +++ b/libobs-winrt/winrt-capture.h @@ -23,6 +23,9 @@ EXPORT void winrt_capture_render(struct winrt_capture *capture, EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture); EXPORT uint32_t winrt_capture_height(const struct winrt_capture *capture); +EXPORT void winrt_capture_thread_start(); +EXPORT void winrt_capture_thread_stop(); + #ifdef __cplusplus } #endif diff --git a/libobs-winrt/winrt-dispatch.cpp b/libobs-winrt/winrt-dispatch.cpp new file mode 100644 index 00000000000000..02b7a9fa0c95f0 --- /dev/null +++ b/libobs-winrt/winrt-dispatch.cpp @@ -0,0 +1,64 @@ +extern "C" EXPORT void winrt_initialize() +{ + winrt::init_apartment(winrt::apartment_type::single_threaded); +} + +extern "C" EXPORT void winrt_uninitialize() +{ + winrt::uninit_apartment(); +} + +static winrt::Windows::System::DispatcherQueueController +CreateDispatcherQueueController() +{ + DispatcherQueueOptions options{sizeof(DispatcherQueueOptions), + DQTYPE_THREAD_CURRENT, DQTAT_COM_STA}; + + winrt::Windows::System::DispatcherQueueController controller{nullptr}; + winrt::check_hresult(CreateDispatcherQueueController( + options, + reinterpret_cast< + ABI::Windows::System::IDispatcherQueueController **>( + winrt::put_abi(controller)))); + return controller; +} + +struct winrt_disaptcher { + winrt::Windows::System::DispatcherQueueController controller{nullptr}; +}; + +extern "C" EXPORT struct winrt_disaptcher *winrt_dispatcher_init() +{ + struct winrt_disaptcher *dispatcher = NULL; + try { + if (winrt::Windows::Foundation::Metadata::ApiInformation:: + IsApiContractPresent( + L"Windows.Foundation.UniversalApiContract", + 5)) { + winrt::Windows::System::DispatcherQueueController + controller = CreateDispatcherQueueController(); + + dispatcher = new struct winrt_disaptcher; + dispatcher->controller = std::move(controller); + } + } catch (const winrt::hresult_error &err) { + blog(LOG_ERROR, "winrt_dispatcher_init (0x%08X): %ls", + err.to_abi(), err.message().c_str()); + } catch (...) { + blog(LOG_ERROR, "winrt_dispatcher_init (0x%08X)", + winrt::to_hresult()); + } + + return dispatcher; +} + +extern "C" EXPORT void +winrt_dispatcher_free(struct winrt_disaptcher *dispatcher) +try { + delete dispatcher; +} catch (const winrt::hresult_error &err) { + blog(LOG_ERROR, "winrt_dispatcher_free (0x%08X): %ls", err.to_abi(), + err.message().c_str()); +} catch (...) { + blog(LOG_ERROR, "winrt_dispatcher_free (0x%08X)", winrt::to_hresult()); +} diff --git a/libobs-winrt/winrt-dispatch.h b/libobs-winrt/winrt-dispatch.h new file mode 100644 index 00000000000000..e2d01b8a111062 --- /dev/null +++ b/libobs-winrt/winrt-dispatch.h @@ -0,0 +1,19 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +EXPORT void winrt_initialize(); +EXPORT void winrt_uninitialize(); +EXPORT struct winrt_disaptcher *winrt_dispatcher_init(); +EXPORT void winrt_dispatcher_free(struct winrt_disaptcher *dispatcher); + +#ifdef __cplusplus +} +#endif diff --git a/libobs/obs-video.c b/libobs/obs-video.c index 894a9fcf64338a..82b37b492bd889 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -539,7 +539,7 @@ static const uint8_t *set_gpu_converted_plane(uint32_t width, uint32_t height, const uint8_t *in, uint8_t *out) { if ((width == linesize_input) && (width == linesize_output)) { - size_t total = width * height; + size_t total = (size_t)width * (size_t)height; memcpy(out, in, total); in += total; } else { @@ -672,10 +672,12 @@ static inline void copy_rgbx_frame(struct video_frame *output, /* if the line sizes match, do a single copy */ if (input->linesize[0] == output->linesize[0]) { - memcpy(out_ptr, in_ptr, input->linesize[0] * info->height); + memcpy(out_ptr, in_ptr, + (size_t)input->linesize[0] * (size_t)info->height); } else { + const size_t copy_size = (size_t)info->width * 4; for (size_t y = 0; y < info->height; y++) { - memcpy(out_ptr, in_ptr, info->width * 4); + memcpy(out_ptr, in_ptr, copy_size); in_ptr += input->linesize[0]; out_ptr += output->linesize[0]; } @@ -835,11 +837,97 @@ static void execute_graphics_tasks(void) } } +#ifdef _WIN32 + +struct winrt_exports { + void (*winrt_initialize)(); + void (*winrt_uninitialize)(); + struct winrt_disaptcher *(*winrt_dispatcher_init)(); + void (*winrt_dispatcher_free)(struct winrt_disaptcher *dispatcher); + void (*winrt_capture_thread_start)(); + void (*winrt_capture_thread_stop)(); +}; + +#define WINRT_IMPORT(func) \ + do { \ + exports->func = os_dlsym(module, #func); \ + if (!exports->func) { \ + success = false; \ + blog(LOG_ERROR, \ + "Could not load function '%s' from " \ + "module '%s'", \ + #func, module_name); \ + } \ + } while (false) + +static bool load_winrt_imports(struct winrt_exports *exports, void *module, + const char *module_name) +{ + bool success = true; + + WINRT_IMPORT(winrt_initialize); + WINRT_IMPORT(winrt_uninitialize); + WINRT_IMPORT(winrt_dispatcher_init); + WINRT_IMPORT(winrt_dispatcher_free); + WINRT_IMPORT(winrt_capture_thread_start); + WINRT_IMPORT(winrt_capture_thread_stop); + + return success; +} + +struct winrt_state { + bool loaded; + void *winrt_module; + struct winrt_exports exports; + struct winrt_disaptcher *dispatcher; +}; + +static void init_winrt_state(struct winrt_state *winrt) +{ + static const char *const module_name = "libobs-winrt"; + + winrt->winrt_module = os_dlopen(module_name); + winrt->loaded = winrt->winrt_module && + load_winrt_imports(&winrt->exports, winrt->winrt_module, + module_name); + winrt->dispatcher = NULL; + if (winrt->loaded) { + winrt->exports.winrt_initialize(); + winrt->dispatcher = winrt->exports.winrt_dispatcher_init(); + + gs_enter_context(obs->video.graphics); + winrt->exports.winrt_capture_thread_start(); + gs_leave_context(); + } +} + +static void uninit_winrt_state(struct winrt_state *winrt) +{ + if (winrt->winrt_module) { + if (winrt->loaded) { + winrt->exports.winrt_capture_thread_stop(); + if (winrt->dispatcher) + winrt->exports.winrt_dispatcher_free( + winrt->dispatcher); + winrt->exports.winrt_uninitialize(); + } + + os_dlclose(winrt->winrt_module); + } +} + +#endif // #ifdef _WIN32 + static const char *tick_sources_name = "tick_sources"; static const char *render_displays_name = "render_displays"; static const char *output_frame_name = "output_frame"; void *obs_graphics_thread(void *param) { +#ifdef _WIN32 + struct winrt_state winrt; + init_winrt_state(&winrt); +#endif // #ifdef _WIN32 + uint64_t last_time = 0; uint64_t interval = video_output_get_frame_time(obs->video.video); uint64_t frame_time_total_ns = 0; @@ -952,6 +1040,10 @@ void *obs_graphics_thread(void *param) break; } +#ifdef _WIN32 + uninit_winrt_state(&winrt); +#endif + UNUSED_PARAMETER(param); return NULL; }