From 93fc61fa82b081ea83024f8fbbe96bdef671006c Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 5 Oct 2018 03:15:13 -0700 Subject: [PATCH] libobs/graphics: Add NV12 texture support --- libobs-d3d11/d3d11-stagesurf.cpp | 25 ++++++++++ libobs-d3d11/d3d11-subsystem.cpp | 68 ++++++++++++++++++++++++++- libobs-d3d11/d3d11-subsystem.hpp | 15 +++++- libobs-d3d11/d3d11-texture2d.cpp | 41 ++++++++++++++-- libobs/graphics/graphics-imports.c | 4 ++ libobs/graphics/graphics-internal.h | 8 ++++ libobs/graphics/graphics.c | 72 +++++++++++++++++++++++++++++ libobs/graphics/graphics.h | 12 ++++- 8 files changed, 237 insertions(+), 8 deletions(-) diff --git a/libobs-d3d11/d3d11-stagesurf.cpp b/libobs-d3d11/d3d11-stagesurf.cpp index c08b4620f..efad5dfc3 100644 --- a/libobs-d3d11/d3d11-stagesurf.cpp +++ b/libobs-d3d11/d3d11-stagesurf.cpp @@ -41,3 +41,28 @@ gs_stage_surface::gs_stage_surface(gs_device_t *device, uint32_t width, if (FAILED(hr)) throw HRError("Failed to create staging surface", hr); } + +gs_stage_surface::gs_stage_surface(gs_device_t *device, uint32_t width, + uint32_t height) + : gs_obj (device, gs_type::gs_stage_surface), + width (width), + height (height), + format (GS_UNKNOWN), + dxgiFormat (DXGI_FORMAT_NV12) +{ + HRESULT hr; + + memset(&td, 0, sizeof(td)); + td.Width = width; + td.Height = height; + td.MipLevels = 1; + td.ArraySize = 1; + td.Format = dxgiFormat; + td.SampleDesc.Count = 1; + td.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + td.Usage = D3D11_USAGE_STAGING; + + hr = device->device->CreateTexture2D(&td, NULL, texture.Assign()); + if (FAILED(hr)) + throw HRError("Failed to create staging surface", hr); +} diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index c4a095314..e3d33dcbb 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -258,6 +258,17 @@ void gs_device::InitDevice(uint32_t adapterIdx) if (FAILED(hr)) throw UnsupportedHWError("Failed to create device", hr); + ComQIPtr d3d11_1(device); + if (!!d3d11_1) { + D3D11_FEATURE_DATA_D3D11_OPTIONS opts = {}; + hr = d3d11_1->CheckFeatureSupport( + D3D11_FEATURE_D3D11_OPTIONS, + &opts, sizeof(opts)); + if (SUCCEEDED(hr)) { + nv12Supported = !!opts.ExtendedResourceSharing; + } + } + blog(LOG_INFO, "D3D11 loaded successfully, feature level used: %u", (unsigned int)levelUsed); } @@ -1315,7 +1326,7 @@ void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst, throw "Source texture must be a 2D texture"; if (!dst) throw "Destination surface is NULL"; - if (dst->format != src->format) + if (dst->format != GS_UNKNOWN && dst->format != src->format) throw "Source and destination formats do not match"; if (dst->width != src2d->width || dst->height != src2d->height) @@ -2054,6 +2065,11 @@ extern "C" EXPORT bool device_shared_texture_available(void) return true; } +extern "C" EXPORT bool device_nv12_available(gs_device_t *device) +{ + return device->nv12Supported; +} + extern "C" EXPORT gs_texture_t *device_texture_create_gdi(gs_device_t *device, uint32_t width, uint32_t height) { @@ -2174,3 +2190,53 @@ extern "C" EXPORT int device_texture_release_sync(gs_texture_t *tex, HRESULT hr = keyedMutex->ReleaseSync(key); return hr == S_OK ? 0 : -1; } + +extern "C" EXPORT bool device_texture_create_nv12(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->nv12Supported) + 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_R8, 1, + nullptr, flags, GS_TEXTURE_2D, false, true); + tex_uv = new gs_texture_2d(device, tex_y->texture, flags); + + } catch (HRError error) { + blog(LOG_ERROR, "gs_texture_create_nv12 (D3D11): %s (%08lX)", + error.str, error.hr); + LogD3D11ErrorDetails(error, device); + return false; + + } catch (const char *error) { + blog(LOG_ERROR, "gs_texture_create_nv12 (D3D11): %s", error); + return false; + } + + *p_tex_y = tex_y; + *p_tex_uv = tex_uv; + return true; +} + +extern "C" EXPORT gs_stagesurf_t *device_stagesurface_create_nv12( + gs_device_t *device, uint32_t width, uint32_t height) +{ + gs_stage_surface *surf = NULL; + try { + surf = new gs_stage_surface(device, width, height); + } catch (HRError error) { + blog(LOG_ERROR, "device_stagesurface_create (D3D11): %s " + "(%08lX)", + error.str, error.hr); + LogD3D11ErrorDetails(error, device); + } + + return surf; +} diff --git a/libobs-d3d11/d3d11-subsystem.hpp b/libobs-d3d11/d3d11-subsystem.hpp index 073fd7626..4dea49cea 100644 --- a/libobs-d3d11/d3d11-subsystem.hpp +++ b/libobs-d3d11/d3d11-subsystem.hpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -80,6 +80,7 @@ static inline DXGI_FORMAT ConvertGSTextureFormat(gs_color_format format) case GS_DXT1: return DXGI_FORMAT_BC1_UNORM; case GS_DXT3: return DXGI_FORMAT_BC2_UNORM; case GS_DXT5: return DXGI_FORMAT_BC3_UNORM; + case GS_R8G8: return DXGI_FORMAT_R8G8_UNORM; } return DXGI_FORMAT_UNKNOWN; @@ -90,6 +91,7 @@ static inline gs_color_format ConvertDXGITextureFormat(DXGI_FORMAT format) switch ((unsigned long)format) { case DXGI_FORMAT_A8_UNORM: return GS_A8; case DXGI_FORMAT_R8_UNORM: return GS_R8; + case DXGI_FORMAT_R8G8_UNORM: return GS_R8G8; case DXGI_FORMAT_R8G8B8A8_UNORM: return GS_RGBA; case DXGI_FORMAT_B8G8R8X8_UNORM: return GS_BGRX; case DXGI_FORMAT_B8G8R8A8_UNORM: return GS_BGRA; @@ -353,6 +355,10 @@ struct gs_texture_2d : gs_texture { bool genMipmaps = false; uint32_t sharedHandle = GS_INVALID_HANDLE; + gs_texture_2d *pairedNV12texture = nullptr; + bool nv12 = false; + bool chroma = false; + vector> data; vector srd; D3D11_TEXTURE2D_DESC td = {}; @@ -383,8 +389,11 @@ struct gs_texture_2d : gs_texture { gs_texture_2d(gs_device_t *device, uint32_t width, uint32_t height, gs_color_format colorFormat, uint32_t levels, const uint8_t **data, uint32_t flags, - gs_texture_type type, bool gdiCompatible); + gs_texture_type type, bool gdiCompatible, + bool nv12 = false); + gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12, + uint32_t flags); gs_texture_2d(gs_device_t *device, uint32_t handle); }; @@ -437,6 +446,7 @@ 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); }; struct gs_sampler_state : gs_obj { @@ -780,6 +790,7 @@ struct gs_device { ComPtr device; ComPtr context; uint32_t adpIdx = 0; + bool nv12Supported = 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 20da94bbe..999035749 100644 --- a/libobs-d3d11/d3d11-texture2d.cpp +++ b/libobs-d3d11/d3d11-texture2d.cpp @@ -80,7 +80,7 @@ void gs_texture_2d::InitTexture(const uint8_t **data) td.Height = height; td.MipLevels = genMipmaps ? 0 : levels; td.ArraySize = type == GS_TEXTURE_CUBE ? 6 : 1; - td.Format = dxgiFormat; + td.Format = nv12 ? DXGI_FORMAT_NV12 : dxgiFormat; td.BindFlags = D3D11_BIND_SHADER_RESOURCE; td.SampleDesc.Count = 1; td.CPUAccessFlags = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0; @@ -182,7 +182,12 @@ void gs_texture_2d::InitRenderTargets() { HRESULT hr; if (type == GS_TEXTURE_2D) { - hr = device->device->CreateRenderTargetView(texture, NULL, + D3D11_RENDER_TARGET_VIEW_DESC rtv; + rtv.Format = dxgiFormat; + rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtv.Texture2D.MipSlice = 0; + + hr = device->device->CreateRenderTargetView(texture, &rtv, renderTarget[0].Assign()); if (FAILED(hr)) throw HRError("Failed to create render target view", @@ -210,7 +215,7 @@ void gs_texture_2d::InitRenderTargets() 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 **data, uint32_t flags_, gs_texture_type type, - bool gdiCompatible) + bool gdiCompatible, bool nv12_) : gs_texture (device, gs_type::gs_texture_2d, type, levels, colorFormat), width (width), @@ -222,7 +227,8 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width, isDynamic ((flags_ & GS_DYNAMIC) != 0), isShared ((flags_ & SHARED_FLAGS) != 0), genMipmaps ((flags_ & GS_BUILD_MIPMAPS) != 0), - sharedHandle (GS_INVALID_HANDLE) + sharedHandle (GS_INVALID_HANDLE), + nv12 (nv12_) { InitTexture(data); InitResourceView(); @@ -231,6 +237,33 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width, InitRenderTargets(); } +gs_texture_2d::gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12tex, + uint32_t flags_) + : gs_texture (device, gs_type::gs_texture_2d, GS_TEXTURE_2D), + isRenderTarget ((flags_ & GS_RENDER_TARGET) != 0), + isDynamic ((flags_ & GS_DYNAMIC) != 0), + isShared ((flags_ & SHARED_FLAGS) != 0), + genMipmaps ((flags_ & GS_BUILD_MIPMAPS) != 0), + nv12 (true) +{ + texture = nv12tex; + texture->GetDesc(&td); + + this->type = GS_TEXTURE_2D; + this->format = 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->dxgiFormat = DXGI_FORMAT_R8G8_UNORM; + + InitResourceView(); + if (isRenderTarget) + InitRenderTargets(); +} + gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t handle) : gs_texture (device, gs_type::gs_texture_2d, GS_TEXTURE_2D), diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index 73217ce8f..9eee2b4f8 100644 --- a/libobs/graphics/graphics-imports.c +++ b/libobs/graphics/graphics-imports.c @@ -171,6 +171,8 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT(gs_shader_set_default); GRAPHICS_IMPORT(gs_shader_set_next_sampler); + GRAPHICS_IMPORT_OPTIONAL(device_nv12_available); + /* OSX/Cocoa specific functions */ #ifdef __APPLE__ GRAPHICS_IMPORT_OPTIONAL(device_texture_create_from_iosurface); @@ -192,6 +194,8 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT_OPTIONAL(device_texture_get_shared_handle); 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_stagesurface_create_nv12); #endif return success; diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 5bee4e1dd..472d5bbd8 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -232,6 +232,8 @@ struct gs_exports { void (*gs_shader_set_next_sampler)(gs_sparam_t *param, gs_samplerstate_t *sampler); + bool (*device_nv12_available)(gs_device_t *device); + #ifdef __APPLE__ /* OSX/Cocoa specific functions */ gs_texture_t *(*device_texture_create_from_iosurface)(gs_device_t *dev, @@ -265,6 +267,12 @@ struct gs_exports { int (*device_texture_acquire_sync)(gs_texture_t *tex, uint64_t key, uint32_t ms); int (*device_texture_release_sync)(gs_texture_t *tex, uint64_t key); + bool (*device_texture_create_nv12)(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); #endif }; diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index f987e3a0e..ec0d8823d 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -2545,6 +2545,18 @@ enum gs_index_type gs_indexbuffer_get_type(const gs_indexbuffer_t *indexbuffer) return thread_graphics->exports.gs_indexbuffer_get_type(indexbuffer); } +bool gs_nv12_available(void) +{ + if (!gs_valid("gs_nv12_available")) + return false; + + if (!thread_graphics->exports.device_nv12_available) + return false; + + return thread_graphics->exports.device_nv12_available( + thread_graphics->device); +} + #ifdef __APPLE__ /** Platform specific functions */ @@ -2726,4 +2738,64 @@ int gs_texture_release_sync(gs_texture_t *tex, uint64_t key) return -1; } +bool gs_texture_create_nv12(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_nv12")) + return false; + + if ((width & 1) == 1 || (height & 1) == 1) { + blog(LOG_ERROR, "NV12 textures must have dimensions " + "divisible by 2."); + return false; + } + + if (graphics->exports.device_texture_create_nv12) { + success = graphics->exports.device_texture_create_nv12( + graphics->device, tex_y, tex_uv, + width, height, flags); + if (success) + return true; + } + + *tex_y = gs_texture_create(width, height, GS_R8, 1, NULL, flags); + *tex_uv = gs_texture_create(width / 2, height / 2, GS_R8G8, 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; + + if (!gs_valid("gs_stagesurface_create_nv12")) + return NULL; + + if ((width & 1) == 1 || (height & 1) == 1) { + blog(LOG_ERROR, "NV12 textures must have dimensions " + "divisible by 2."); + return NULL; + } + + if (graphics->exports.device_stagesurface_create_nv12) + return graphics->exports.device_stagesurface_create_nv12( + graphics->device, width, height); + + return NULL; +} + #endif diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 5dc785835..808c16f79 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -71,7 +71,8 @@ enum gs_color_format { GS_R32F, GS_DXT1, GS_DXT3, - GS_DXT5 + GS_DXT5, + GS_R8G8, }; enum gs_zstencil_format { @@ -758,6 +759,8 @@ EXPORT size_t gs_indexbuffer_get_num_indices( EXPORT enum gs_index_type gs_indexbuffer_get_type( const gs_indexbuffer_t *indexbuffer); +EXPORT bool gs_nv12_available(void); + #ifdef __APPLE__ /** platform specific function for creating (GL_TEXTURE_RECTANGLE) textures @@ -814,6 +817,12 @@ EXPORT int gs_texture_acquire_sync(gs_texture_t *tex, uint64_t key, uint32_t ms) */ 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 gs_stagesurf_t *gs_stagesurface_create_nv12( + uint32_t width, uint32_t height); + #endif /* inline functions used by modules */ @@ -838,6 +847,7 @@ static inline uint32_t gs_get_format_bpp(enum gs_color_format format) case GS_DXT1: return 4; case GS_DXT3: return 8; case GS_DXT5: return 8; + case GS_R8G8: return 16; case GS_UNKNOWN: return 0; }