Merge pull request #2964 from jpark37/winrt-dispatcher
Set up dispatcher queue for WGC stabilitymaster
commit
dedc0c01d5
|
@ -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"]]
|
||||
<obs-module.h>
|
||||
<d3d11.h>
|
||||
<DispatcherQueue.h>
|
||||
<dwmapi.h>
|
||||
<Windows.Graphics.Capture.Interop.h>
|
||||
<winrt/Windows.Foundation.Metadata.h>
|
||||
<winrt/Windows.Graphics.Capture.h>)
|
||||
<winrt/Windows.Graphics.Capture.h>
|
||||
<winrt/Windows.System.h>)
|
||||
target_link_libraries(libobs-winrt
|
||||
libobs
|
||||
Dwmapi
|
||||
|
|
|
@ -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<winrt_capture *>(data);
|
||||
|
||||
auto activation_factory = winrt::get_activation_factory<
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
|
||||
auto interop_factory =
|
||||
activation_factory.as<IGraphicsCaptureItemInterop>();
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
|
||||
try {
|
||||
interop_factory->CreateForWindow(
|
||||
capture->window,
|
||||
winrt::guid_of<ABI::Windows::Graphics::Capture::
|
||||
IGraphicsCaptureItem>(),
|
||||
reinterpret_cast<void **>(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<IDXGIDevice> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
#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
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue