From 7c386fe18def018322a697d489b86c2f1b2af2ee Mon Sep 17 00:00:00 2001 From: univrsal Date: Sat, 11 Nov 2023 20:55:33 +0100 Subject: [PATCH] Source: Add support for WGL_NV_DX_interop on windows Allows us to share textures between OpenGL and Direct3D saving us the reading into ram and then copying back into vram. On a 720p video it saved ~1ms in Debug build. --- src/mpv-backend.c | 36 ++++++++++++++++++++++++------------ src/mpv-source.h | 4 ++++ src/wgl.c | 33 +++++++++++++++++++++++++++++---- src/wgl.h | 41 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 97 insertions(+), 17 deletions(-) diff --git a/src/mpv-backend.c b/src/mpv-backend.c index 3c8537c..847ebc3 100644 --- a/src/mpv-backend.c +++ b/src/mpv-backend.c @@ -208,13 +208,18 @@ void mpvs_handle_events(struct mpv_source* context) void mpvs_render(struct mpv_source* context) { -#if !defined(WIN32) +#if defined(WIN32) + if (wgl_have_NV_DX_interop) + wgl_lock_shared_texture(context); +#else // make sure that we restore the current program after mpv is done // as obs will not load the progam because it internally keeps track // of the current program and only loads it if it has changed GLuint currentProgram; context->_glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)¤tProgram); #endif + + mpv_render_frame_info info; mpv_render_param params[] = { @@ -234,20 +239,23 @@ void mpvs_render(struct mpv_source* context) obs_log(LOG_ERROR, "mpv render error: %s", mpv_error_string(result)); #if defined(WIN32) - if (context->media_state == OBS_MEDIA_STATE_PLAYING) { - uint8_t* ptr; - uint32_t linesize; - if (gs_texture_map(context->video_buffer, &ptr, &linesize)) { - context->_glBindFramebuffer(GL_FRAMEBUFFER, context->fbo); - context->_glReadPixels(0, 0, context->d3d_width, context->d3d_height, GL_RGBA, GL_UNSIGNED_BYTE, ptr); + if (wgl_have_NV_DX_interop) { + wgl_unlock_shared_texture(context); + } else { + if (context->media_state == OBS_MEDIA_STATE_PLAYING) { + uint8_t* ptr; + uint32_t linesize; + if (gs_texture_map(context->video_buffer, &ptr, &linesize)) { + context->_glBindFramebuffer(GL_FRAMEBUFFER, context->fbo); + context->_glReadPixels(0, 0, context->d3d_width, context->d3d_height, GL_RGBA, GL_UNSIGNED_BYTE, ptr); + } + gs_texture_unmap(context->video_buffer); } - gs_texture_unmap(context->video_buffer); } -#endif - -#if !defined(WIN32) +#else context->_glUseProgram(currentProgram); #endif + } void mpvs_init(struct mpv_source* context) @@ -461,7 +469,7 @@ void mpvs_generate_texture(struct mpv_source* context) #if defined(WIN32) if (context->video_buffer) gs_texture_destroy(context->video_buffer); - context->video_buffer = gs_texture_create(context->d3d_width, context->d3d_height, GS_RGBA, 1, NULL, GS_DYNAMIC); + context->video_buffer = gs_texture_create(context->d3d_width, context->d3d_height, GS_RGBA, 1, NULL, wgl_have_NV_DX_interop ? 0 : GS_DYNAMIC); context->_glBindTexture(GL_TEXTURE_2D, 0); @@ -481,6 +489,10 @@ void mpvs_generate_texture(struct mpv_source* context) context->_glGenFramebuffers(1, &context->fbo); context->_glBindFramebuffer(GL_FRAMEBUFFER, context->fbo); context->_glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context->wgl_texture, 0); + + if (wgl_have_NV_DX_interop) { + wgl_init_shared_gl_texture(context); + } #else if (context->video_buffer) { gs_texture_destroy(context->video_buffer); diff --git a/src/mpv-source.h b/src/mpv-source.h index d1e5c98..3407a19 100644 --- a/src/mpv-source.h +++ b/src/mpv-source.h @@ -161,4 +161,8 @@ struct mpv_source { obs_source_t* jack_source; char* jack_port_name; // name of the jack capture source char* jack_client_name; // name of the jack client mpv opens for audio output + +#if defined(WIN32) + HANDLE gl_shared_texture_handle; +#endif }; diff --git a/src/wgl.c b/src/wgl.c index dccd827..28dbea2 100644 --- a/src/wgl.c +++ b/src/wgl.c @@ -1,17 +1,16 @@ #include "wgl.h" - -#include #include #include -#include WNDCLASSEX wc; HWND hwnd; HGLRC hrc; HDC hdc; +HANDLE wgl_dx_device; static const char* dummy_window_class = "GLDummyWindow-obs-mpv"; static bool registered_dummy_window_class = false; +bool wgl_have_NV_DX_interop = false; struct dummy_context { HWND hwnd; @@ -389,6 +388,13 @@ bool wgl_init() if (glVersion) { obs_log(LOG_INFO, "OpenGL Version: %s\n", glVersion); } + + wgl_dx_device = wglDXOpenDeviceNV(gs_get_device_obj()); + if (wgl_dx_device) { + wgl_have_NV_DX_interop = true; + obs_log(LOG_INFO, "NV_DX_interop extension is supported, sharing texture between OpenGL and Direct3D"); + } + init_result = true; return true; fail: @@ -398,8 +404,9 @@ bool wgl_init() void wgl_deinit() { - // Cleanup wglMakeCurrent(NULL, NULL); + if (wgl_dx_device) + wglDXCloseDeviceNV(wgl_dx_device); wglDeleteContext(hrc); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); @@ -415,3 +422,21 @@ void wgl_exit_context() { wglMakeCurrent(NULL, NULL); } + +void wgl_init_shared_gl_texture(void* context) +{ + struct mpv_source* src = context; + src->gl_shared_texture_handle = wglDXRegisterObjectNV(wgl_dx_device, + gs_texture_get_obj(src->video_buffer), + src->wgl_texture, + GL_TEXTURE_2D, + WGL_ACCESS_WRITE_DISCARD_NV); +} + +void wgl_free_shared_gl_texture(void* context) +{ + struct mpv_source* src = context; + if (src->gl_shared_texture_handle) + wglDXUnregisterObjectNV(wgl_dx_device, src->gl_shared_texture_handle); + src->gl_shared_texture_handle = 0; +} diff --git a/src/wgl.h b/src/wgl.h index bba4d56..212c572 100644 --- a/src/wgl.h +++ b/src/wgl.h @@ -1,10 +1,49 @@ #pragma once #include +#if defined(WIN32) +#include +#include +#include "mpv-source.h" + +extern bool wgl_have_NV_DX_interop; +extern HANDLE wgl_dx_device; + +static inline void wgl_lock_shared_texture(void* context) +{ + struct mpv_source* src = context; + wglDXLockObjectsNV(wgl_dx_device, 1, &src->gl_shared_texture_handle); +} + +static inline void wgl_unlock_shared_texture(void* context) +{ + struct mpv_source* src = context; + wglDXUnlockObjectsNV(wgl_dx_device, 1, &src->gl_shared_texture_handle); +} + +#else +# define wgl_have_NV_DX_interop 0 + +static inline void wgl_lock_shared_texture(void* context) +{ + UNUSED_PARAMETER(context); +} + +static inline void wgl_unlock_shared_texture(void* context) +{ + UNUSED_PARAMETER(context); +} +#endif + bool wgl_init(); void wgl_deinit(); bool wgl_enter_context(); -void wgl_exit_context(); \ No newline at end of file +void wgl_exit_context(); + +void wgl_init_shared_gl_texture(void* context); + +void wgl_free_shared_gl_texture(void* context); +