From fee3703f40812d74218b059da91ad5dfd70e2dca Mon Sep 17 00:00:00 2001 From: jpark37 Date: Wed, 2 Mar 2022 23:15:02 -0800 Subject: [PATCH] libobs/graphics, libobs-d3d11: Add P010 support --- libobs-d3d11/d3d11-rebuild.cpp | 14 ++-- libobs-d3d11/d3d11-stagesurf.cpp | 4 +- libobs-d3d11/d3d11-subsystem.cpp | 105 +++++++++++++++++++++------- libobs-d3d11/d3d11-subsystem.hpp | 14 ++-- libobs-d3d11/d3d11-texture2d.cpp | 22 +++--- libobs/graphics/graphics-imports.c | 3 + libobs/graphics/graphics-internal.h | 9 +++ libobs/graphics/graphics.c | 71 +++++++++++++++++++ libobs/graphics/graphics.h | 6 ++ 9 files changed, 200 insertions(+), 48 deletions(-) diff --git a/libobs-d3d11/d3d11-rebuild.cpp b/libobs-d3d11/d3d11-rebuild.cpp index 0aa7fe314..2cee84041 100644 --- a/libobs-d3d11/d3d11-rebuild.cpp +++ b/libobs-d3d11/d3d11-rebuild.cpp @@ -122,9 +122,9 @@ void gs_texture_2d::Rebuild(ID3D11Device *dev) } } -void gs_texture_2d::RebuildNV12_Y(ID3D11Device *dev) +void gs_texture_2d::RebuildPaired_Y(ID3D11Device *dev) { - gs_texture_2d *tex_uv = pairedNV12texture; + gs_texture_2d *tex_uv = pairedTexture; HRESULT hr; hr = dev->CreateTexture2D(&td, nullptr, &texture); @@ -147,7 +147,7 @@ void gs_texture_2d::RebuildNV12_Y(ID3D11Device *dev) if (isRenderTarget) InitRenderTargets(); - tex_uv->RebuildNV12_UV(dev); + tex_uv->RebuildPaired_UV(dev); acquired = false; @@ -159,9 +159,9 @@ void gs_texture_2d::RebuildNV12_Y(ID3D11Device *dev) } } -void gs_texture_2d::RebuildNV12_UV(ID3D11Device *dev) +void gs_texture_2d::RebuildPaired_UV(ID3D11Device *dev) { - gs_texture_2d *tex_y = pairedNV12texture; + gs_texture_2d *tex_y = pairedTexture; HRESULT hr; texture = tex_y->texture; @@ -501,10 +501,10 @@ try { break; case gs_type::gs_texture_2d: { gs_texture_2d *tex = (gs_texture_2d *)obj; - if (!tex->nv12) { + if (!tex->pairedTexture) { tex->Rebuild(dev); } else if (!tex->chroma) { - tex->RebuildNV12_Y(dev); + tex->RebuildPaired_Y(dev); } } break; case gs_type::gs_zstencil_buffer: diff --git a/libobs-d3d11/d3d11-stagesurf.cpp b/libobs-d3d11/d3d11-stagesurf.cpp index 00517527a..3e533f3cd 100644 --- a/libobs-d3d11/d3d11-stagesurf.cpp +++ b/libobs-d3d11/d3d11-stagesurf.cpp @@ -43,12 +43,12 @@ gs_stage_surface::gs_stage_surface(gs_device_t *device, uint32_t width, } gs_stage_surface::gs_stage_surface(gs_device_t *device, uint32_t width, - uint32_t height) + uint32_t height, bool p010) : gs_obj(device, gs_type::gs_stage_surface), width(width), height(height), format(GS_UNKNOWN), - dxgiFormat(DXGI_FORMAT_NV12) + dxgiFormat(p010 ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12) { HRESULT hr; diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 83fb7004e..40d52627b 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -375,7 +375,7 @@ try { gs_vertex_shader nv12_vs(this, "", NV12_VS); gs_pixel_shader nv12_y_ps(this, "", NV12_Y_PS); gs_pixel_shader nv12_uv_ps(this, "", NV12_UV_PS); - gs_stage_surface nv12_stage(this, NV12_CX, NV12_CY); + gs_stage_surface nv12_stage(this, NV12_CX, NV12_CY, false); gs_vb_data *vbd = gs_vbdata_create(); vbd->num = 4; @@ -516,6 +516,16 @@ static bool set_priority(ID3D11Device *device) #endif +static bool CheckFormat(ID3D11Device *device, DXGI_FORMAT format) +{ + constexpr UINT required = D3D11_FORMAT_SUPPORT_TEXTURE2D | + D3D11_FORMAT_SUPPORT_RENDER_TARGET; + + UINT support = 0; + return SUCCEEDED(device->CheckFormatSupport(format, &support)) && + ((support & required) == required); +} + void gs_device::InitDevice(uint32_t adapterIdx) { wstring adapterName; @@ -567,6 +577,7 @@ void gs_device::InitDevice(uint32_t adapterIdx) /* check for nv12 texture output support */ nv12Supported = false; + p010Supported = false; /* WARP NV12 support is suspected to be buggy on older Windows */ if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) { @@ -586,27 +597,9 @@ void gs_device::InitDevice(uint32_t adapterIdx) return; } - /* needs to support the actual format */ - UINT support = 0; - hr = device->CheckFormatSupport(DXGI_FORMAT_NV12, &support); - if (FAILED(hr)) { - return; - } - - if ((support & D3D11_FORMAT_SUPPORT_TEXTURE2D) == 0) { - return; - } - - /* must be usable as a render target */ - if ((support & D3D11_FORMAT_SUPPORT_RENDER_TARGET) == 0) { - return; - } - - if (HasBadNV12Output()) { - return; - } - - nv12Supported = true; + nv12Supported = CheckFormat(device, DXGI_FORMAT_NV12) && + !HasBadNV12Output(); + p010Supported = nv12Supported && CheckFormat(device, DXGI_FORMAT_P010); } static inline void ConvertStencilSide(D3D11_DEPTH_STENCILOP_DESC &desc, @@ -2924,6 +2917,11 @@ extern "C" EXPORT bool device_nv12_available(gs_device_t *device) return device->nv12Supported; } +extern "C" EXPORT bool device_p010_available(gs_device_t *device) +{ + return device->p010Supported; +} + extern "C" EXPORT void device_debug_marker_begin(gs_device_t *, const char *markername, const float color[4]) @@ -3143,8 +3141,47 @@ device_texture_create_nv12(gs_device_t *device, gs_texture_t **p_tex_y, return false; } - tex_y->pairedNV12texture = tex_uv; - tex_uv->pairedNV12texture = tex_y; + tex_y->pairedTexture = tex_uv; + tex_uv->pairedTexture = tex_y; + + *p_tex_y = tex_y; + *p_tex_uv = tex_uv; + return true; +} + +extern "C" EXPORT bool +device_texture_create_p010(gs_device_t *device, gs_texture_t **p_tex_y, + gs_texture_t **p_tex_uv, uint32_t width, + uint32_t height, uint32_t flags) +{ + if (!device->p010Supported) + return false; + + *p_tex_y = nullptr; + *p_tex_uv = nullptr; + + gs_texture_2d *tex_y; + gs_texture_2d *tex_uv; + + try { + tex_y = new gs_texture_2d(device, width, height, GS_R16, 1, + nullptr, flags, GS_TEXTURE_2D, false, + true); + tex_uv = new gs_texture_2d(device, tex_y->texture, flags); + + } catch (const HRError &error) { + blog(LOG_ERROR, "gs_texture_create_p010 (D3D11): %s (%08lX)", + error.str, error.hr); + LogD3D11ErrorDetails(error, device); + return false; + + } catch (const char *error) { + blog(LOG_ERROR, "gs_texture_create_p010 (D3D11): %s", error); + return false; + } + + tex_y->pairedTexture = tex_uv; + tex_uv->pairedTexture = tex_y; *p_tex_y = tex_y; *p_tex_uv = tex_uv; @@ -3157,7 +3194,25 @@ device_stagesurface_create_nv12(gs_device_t *device, uint32_t width, { gs_stage_surface *surf = NULL; try { - surf = new gs_stage_surface(device, width, height); + surf = new gs_stage_surface(device, width, height, false); + } catch (const HRError &error) { + blog(LOG_ERROR, + "device_stagesurface_create (D3D11): %s " + "(%08lX)", + error.str, error.hr); + LogD3D11ErrorDetails(error, device); + } + + return surf; +} + +extern "C" EXPORT gs_stagesurf_t * +device_stagesurface_create_p010(gs_device_t *device, uint32_t width, + uint32_t height) +{ + gs_stage_surface *surf = NULL; + try { + surf = new gs_stage_surface(device, width, height, true); } catch (const HRError &error) { blog(LOG_ERROR, "device_stagesurface_create (D3D11): %s " diff --git a/libobs-d3d11/d3d11-subsystem.hpp b/libobs-d3d11/d3d11-subsystem.hpp index c098dc183..f693e52f4 100644 --- a/libobs-d3d11/d3d11-subsystem.hpp +++ b/libobs-d3d11/d3d11-subsystem.hpp @@ -515,8 +515,8 @@ struct gs_texture_2d : gs_texture { bool genMipmaps = false; uint32_t sharedHandle = GS_INVALID_HANDLE; - gs_texture_2d *pairedNV12texture = nullptr; - bool nv12 = false; + gs_texture_2d *pairedTexture = nullptr; + bool twoPlane = false; bool chroma = false; bool acquired = false; @@ -533,8 +533,8 @@ struct gs_texture_2d : gs_texture { void RebuildSharedTextureFallback(); void Rebuild(ID3D11Device *dev); - void RebuildNV12_Y(ID3D11Device *dev); - void RebuildNV12_UV(ID3D11Device *dev); + void RebuildPaired_Y(ID3D11Device *dev); + void RebuildPaired_UV(ID3D11Device *dev); inline void Release() { @@ -554,7 +554,7 @@ struct gs_texture_2d : gs_texture { gs_color_format colorFormat, uint32_t levels, const uint8_t *const *data, uint32_t flags, gs_texture_type type, bool gdiCompatible, - bool nv12 = false); + bool twoPlane = false); gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12, uint32_t flags); @@ -654,7 +654,8 @@ struct gs_stage_surface : gs_obj { gs_stage_surface(gs_device_t *device, uint32_t width, uint32_t height, gs_color_format colorFormat); - gs_stage_surface(gs_device_t *device, uint32_t width, uint32_t height); + gs_stage_surface(gs_device_t *device, uint32_t width, uint32_t height, + bool p010); }; struct gs_sampler_state : gs_obj { @@ -985,6 +986,7 @@ struct gs_device { ComPtr context; uint32_t adpIdx = 0; bool nv12Supported = false; + bool p010Supported = false; gs_texture_2d *curRenderTarget = nullptr; gs_zstencil_buffer *curZStencilBuffer = nullptr; diff --git a/libobs-d3d11/d3d11-texture2d.cpp b/libobs-d3d11/d3d11-texture2d.cpp index 7a4876acc..bc1596e06 100644 --- a/libobs-d3d11/d3d11-texture2d.cpp +++ b/libobs-d3d11/d3d11-texture2d.cpp @@ -103,7 +103,9 @@ void gs_texture_2d::InitTexture(const uint8_t *const *data) td.Height = height; td.MipLevels = genMipmaps ? 0 : levels; td.ArraySize = type == GS_TEXTURE_CUBE ? 6 : 1; - td.Format = nv12 ? DXGI_FORMAT_NV12 : dxgiFormatResource; + td.Format = twoPlane ? ((format == GS_R16) ? DXGI_FORMAT_P010 + : DXGI_FORMAT_NV12) + : dxgiFormatResource; td.BindFlags = D3D11_BIND_SHADER_RESOURCE; td.SampleDesc.Count = 1; td.CPUAccessFlags = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0; @@ -266,7 +268,7 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width, uint32_t height, gs_color_format colorFormat, uint32_t levels, const uint8_t *const *data, uint32_t flags_, gs_texture_type type, - bool gdiCompatible, bool nv12_) + bool gdiCompatible, bool twoPlane_) : gs_texture(device, gs_type::gs_texture_2d, type, levels, colorFormat), width(width), height(height), @@ -280,7 +282,7 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width, isShared((flags_ & SHARED_FLAGS) != 0), genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0), sharedHandle(GS_INVALID_HANDLE), - nv12(nv12_) + twoPlane(twoPlane_) { InitTexture(data); InitResourceView(); @@ -296,22 +298,26 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12tex, isDynamic((flags_ & GS_DYNAMIC) != 0), isShared((flags_ & SHARED_FLAGS) != 0), genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0), - nv12(true), + twoPlane(true), texture(nv12tex) { texture->GetDesc(&td); + const bool p010 = td.Format == DXGI_FORMAT_P010; + const DXGI_FORMAT dxgi_format = p010 ? DXGI_FORMAT_R16G16_UNORM + : DXGI_FORMAT_R8G8_UNORM; + this->type = GS_TEXTURE_2D; - this->format = GS_R8G8; + this->format = p010 ? GS_RG16 : GS_R8G8; this->flags = flags_; this->levels = 1; this->device = device; this->chroma = true; this->width = td.Width / 2; this->height = td.Height / 2; - this->dxgiFormatResource = DXGI_FORMAT_R8G8_UNORM; - this->dxgiFormatView = DXGI_FORMAT_R8G8_UNORM; - this->dxgiFormatViewLinear = DXGI_FORMAT_R8G8_UNORM; + this->dxgiFormatResource = dxgi_format; + this->dxgiFormatView = dxgi_format; + this->dxgiFormatViewLinear = dxgi_format; InitResourceView(); if (isRenderTarget) diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index 16e30192a..7b92b2994 100644 --- a/libobs/graphics/graphics-imports.c +++ b/libobs/graphics/graphics-imports.c @@ -190,6 +190,7 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT(gs_shader_set_next_sampler); GRAPHICS_IMPORT_OPTIONAL(device_nv12_available); + GRAPHICS_IMPORT_OPTIONAL(device_p010_available); GRAPHICS_IMPORT(device_debug_marker_begin); GRAPHICS_IMPORT(device_debug_marker_end); @@ -222,7 +223,9 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT_OPTIONAL(device_texture_acquire_sync); GRAPHICS_IMPORT_OPTIONAL(device_texture_release_sync); GRAPHICS_IMPORT_OPTIONAL(device_texture_create_nv12); + GRAPHICS_IMPORT_OPTIONAL(device_texture_create_p010); GRAPHICS_IMPORT_OPTIONAL(device_stagesurface_create_nv12); + GRAPHICS_IMPORT_OPTIONAL(device_stagesurface_create_p010); GRAPHICS_IMPORT_OPTIONAL(device_register_loss_callbacks); GRAPHICS_IMPORT_OPTIONAL(device_unregister_loss_callbacks); #elif __linux__ diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 59998b405..706632478 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -266,6 +266,7 @@ struct gs_exports { gs_samplerstate_t *sampler); bool (*device_nv12_available)(gs_device_t *device); + bool (*device_p010_available)(gs_device_t *device); void (*device_debug_marker_begin)(gs_device_t *device, const char *markername, @@ -323,10 +324,18 @@ struct gs_exports { gs_texture_t **tex_uv, uint32_t width, uint32_t height, uint32_t flags); + bool (*device_texture_create_p010)(gs_device_t *device, + gs_texture_t **tex_y, + gs_texture_t **tex_uv, + uint32_t width, uint32_t height, + uint32_t flags); gs_stagesurf_t *(*device_stagesurface_create_nv12)(gs_device_t *device, uint32_t width, uint32_t height); + gs_stagesurf_t *(*device_stagesurface_create_p010)(gs_device_t *device, + uint32_t width, + uint32_t height); void (*device_register_loss_callbacks)( gs_device_t *device, const struct gs_device_loss *callbacks); void (*device_unregister_loss_callbacks)(gs_device_t *device, diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index 79adc06d1..e931fe8d5 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -2795,6 +2795,18 @@ bool gs_nv12_available(void) thread_graphics->device); } +bool gs_p010_available(void) +{ + if (!gs_valid("gs_p010_available")) + return false; + + if (!thread_graphics->exports.device_p010_available) + return false; + + return thread_graphics->exports.device_p010_available( + thread_graphics->device); +} + void gs_debug_marker_begin(const float color[4], const char *markername) { if (!gs_valid("gs_debug_marker_begin")) @@ -3118,6 +3130,45 @@ bool gs_texture_create_nv12(gs_texture_t **tex_y, gs_texture_t **tex_uv, return true; } +bool gs_texture_create_p010(gs_texture_t **tex_y, gs_texture_t **tex_uv, + uint32_t width, uint32_t height, uint32_t flags) +{ + graphics_t *graphics = thread_graphics; + bool success = false; + + if (!gs_valid("gs_texture_create_p010")) + return false; + + if ((width & 1) == 1 || (height & 1) == 1) { + blog(LOG_ERROR, "P010 textures must have dimensions " + "divisible by 2."); + return false; + } + + if (graphics->exports.device_texture_create_p010) { + success = graphics->exports.device_texture_create_p010( + graphics->device, tex_y, tex_uv, width, height, flags); + if (success) + return true; + } + + *tex_y = gs_texture_create(width, height, GS_R16, 1, NULL, flags); + *tex_uv = gs_texture_create(width / 2, height / 2, GS_RG16, 1, NULL, + flags); + + if (!*tex_y || !*tex_uv) { + if (*tex_y) + gs_texture_destroy(*tex_y); + if (*tex_uv) + gs_texture_destroy(*tex_uv); + *tex_y = NULL; + *tex_uv = NULL; + return false; + } + + return true; +} + gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height) { graphics_t *graphics = thread_graphics; @@ -3138,6 +3189,26 @@ gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height) return NULL; } +gs_stagesurf_t *gs_stagesurface_create_p010(uint32_t width, uint32_t height) +{ + graphics_t *graphics = thread_graphics; + + if (!gs_valid("gs_stagesurface_create_p010")) + return NULL; + + if ((width & 1) == 1 || (height & 1) == 1) { + blog(LOG_ERROR, "P010 textures must have dimensions " + "divisible by 2."); + return NULL; + } + + if (graphics->exports.device_stagesurface_create_p010) + return graphics->exports.device_stagesurface_create_p010( + graphics->device, width, height); + + return NULL; +} + void gs_register_loss_callbacks(const struct gs_device_loss *callbacks) { graphics_t *graphics = thread_graphics; diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 0eae8dd57..35d2c8687 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -835,6 +835,7 @@ EXPORT bool gs_timer_range_get_data(gs_timer_range_t *range, bool *disjoint, uint64_t *frequency); EXPORT bool gs_nv12_available(void); +EXPORT bool gs_p010_available(void); #define GS_USE_DEBUG_MARKERS 0 #if GS_USE_DEBUG_MARKERS @@ -931,9 +932,14 @@ EXPORT int gs_texture_release_sync(gs_texture_t *tex, uint64_t key); EXPORT bool gs_texture_create_nv12(gs_texture_t **tex_y, gs_texture_t **tex_uv, uint32_t width, uint32_t height, uint32_t flags); +EXPORT bool gs_texture_create_p010(gs_texture_t **tex_y, gs_texture_t **tex_uv, + uint32_t width, uint32_t height, + uint32_t flags); EXPORT gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height); +EXPORT gs_stagesurf_t *gs_stagesurface_create_p010(uint32_t width, + uint32_t height); EXPORT void gs_register_loss_callbacks(const struct gs_device_loss *callbacks); EXPORT void gs_unregister_loss_callbacks(void *data);