libobs/graphics: Add NV12 texture support
parent
b64d7d71d0
commit
93fc61fa82
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue