libobs/graphics: Add NV12 texture support

master
jp9000 2018-10-05 03:15:13 -07:00
parent b64d7d71d0
commit 93fc61fa82
8 changed files with 237 additions and 8 deletions

View File

@ -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);
}

View File

@ -258,6 +258,17 @@ void gs_device::InitDevice(uint32_t adapterIdx)
if (FAILED(hr))
throw UnsupportedHWError("Failed to create device", hr);
ComQIPtr<ID3D11Device1> 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;
}

View File

@ -27,7 +27,7 @@
#include <windows.h>
#include <dxgi.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#include <d3d11_1.h>
#include <d3dcompiler.h>
#include <util/base.h>
@ -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<vector<uint8_t>> data;
vector<D3D11_SUBRESOURCE_DATA> 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<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
uint32_t adpIdx = 0;
bool nv12Supported = false;
gs_texture_2d *curRenderTarget = nullptr;
gs_zstencil_buffer *curZStencilBuffer = nullptr;

View File

@ -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),

View File

@ -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;

View File

@ -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
};

View File

@ -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

View File

@ -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;
}