libobs/graphics: Add NV12 texture support
This commit is contained in:
@@ -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),
|
||||
|
Reference in New Issue
Block a user