libobs/graphics, libobs-d3d11: Add P010 support

master
jpark37 2022-03-02 23:15:02 -08:00 committed by Jim
parent a147315f33
commit fee3703f40
9 changed files with 200 additions and 48 deletions

View File

@ -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; HRESULT hr;
hr = dev->CreateTexture2D(&td, nullptr, &texture); hr = dev->CreateTexture2D(&td, nullptr, &texture);
@ -147,7 +147,7 @@ void gs_texture_2d::RebuildNV12_Y(ID3D11Device *dev)
if (isRenderTarget) if (isRenderTarget)
InitRenderTargets(); InitRenderTargets();
tex_uv->RebuildNV12_UV(dev); tex_uv->RebuildPaired_UV(dev);
acquired = false; 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; HRESULT hr;
texture = tex_y->texture; texture = tex_y->texture;
@ -501,10 +501,10 @@ try {
break; break;
case gs_type::gs_texture_2d: { case gs_type::gs_texture_2d: {
gs_texture_2d *tex = (gs_texture_2d *)obj; gs_texture_2d *tex = (gs_texture_2d *)obj;
if (!tex->nv12) { if (!tex->pairedTexture) {
tex->Rebuild(dev); tex->Rebuild(dev);
} else if (!tex->chroma) { } else if (!tex->chroma) {
tex->RebuildNV12_Y(dev); tex->RebuildPaired_Y(dev);
} }
} break; } break;
case gs_type::gs_zstencil_buffer: case gs_type::gs_zstencil_buffer:

View File

@ -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, 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), : gs_obj(device, gs_type::gs_stage_surface),
width(width), width(width),
height(height), height(height),
format(GS_UNKNOWN), format(GS_UNKNOWN),
dxgiFormat(DXGI_FORMAT_NV12) dxgiFormat(p010 ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12)
{ {
HRESULT hr; HRESULT hr;

View File

@ -375,7 +375,7 @@ try {
gs_vertex_shader nv12_vs(this, "", NV12_VS); gs_vertex_shader nv12_vs(this, "", NV12_VS);
gs_pixel_shader nv12_y_ps(this, "", NV12_Y_PS); gs_pixel_shader nv12_y_ps(this, "", NV12_Y_PS);
gs_pixel_shader nv12_uv_ps(this, "", NV12_UV_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(); gs_vb_data *vbd = gs_vbdata_create();
vbd->num = 4; vbd->num = 4;
@ -516,6 +516,16 @@ static bool set_priority(ID3D11Device *device)
#endif #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) void gs_device::InitDevice(uint32_t adapterIdx)
{ {
wstring adapterName; wstring adapterName;
@ -567,6 +577,7 @@ void gs_device::InitDevice(uint32_t adapterIdx)
/* check for nv12 texture output support */ /* check for nv12 texture output support */
nv12Supported = false; nv12Supported = false;
p010Supported = false;
/* WARP NV12 support is suspected to be buggy on older Windows */ /* WARP NV12 support is suspected to be buggy on older Windows */
if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) { if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) {
@ -586,27 +597,9 @@ void gs_device::InitDevice(uint32_t adapterIdx)
return; return;
} }
/* needs to support the actual format */ nv12Supported = CheckFormat(device, DXGI_FORMAT_NV12) &&
UINT support = 0; !HasBadNV12Output();
hr = device->CheckFormatSupport(DXGI_FORMAT_NV12, &support); p010Supported = nv12Supported && CheckFormat(device, DXGI_FORMAT_P010);
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;
} }
static inline void ConvertStencilSide(D3D11_DEPTH_STENCILOP_DESC &desc, 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; 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 *, extern "C" EXPORT void device_debug_marker_begin(gs_device_t *,
const char *markername, const char *markername,
const float color[4]) const float color[4])
@ -3143,8 +3141,47 @@ device_texture_create_nv12(gs_device_t *device, gs_texture_t **p_tex_y,
return false; return false;
} }
tex_y->pairedNV12texture = tex_uv; tex_y->pairedTexture = tex_uv;
tex_uv->pairedNV12texture = tex_y; 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_y = tex_y;
*p_tex_uv = tex_uv; *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; gs_stage_surface *surf = NULL;
try { 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) { } catch (const HRError &error) {
blog(LOG_ERROR, blog(LOG_ERROR,
"device_stagesurface_create (D3D11): %s " "device_stagesurface_create (D3D11): %s "

View File

@ -515,8 +515,8 @@ struct gs_texture_2d : gs_texture {
bool genMipmaps = false; bool genMipmaps = false;
uint32_t sharedHandle = GS_INVALID_HANDLE; uint32_t sharedHandle = GS_INVALID_HANDLE;
gs_texture_2d *pairedNV12texture = nullptr; gs_texture_2d *pairedTexture = nullptr;
bool nv12 = false; bool twoPlane = false;
bool chroma = false; bool chroma = false;
bool acquired = false; bool acquired = false;
@ -533,8 +533,8 @@ struct gs_texture_2d : gs_texture {
void RebuildSharedTextureFallback(); void RebuildSharedTextureFallback();
void Rebuild(ID3D11Device *dev); void Rebuild(ID3D11Device *dev);
void RebuildNV12_Y(ID3D11Device *dev); void RebuildPaired_Y(ID3D11Device *dev);
void RebuildNV12_UV(ID3D11Device *dev); void RebuildPaired_UV(ID3D11Device *dev);
inline void Release() inline void Release()
{ {
@ -554,7 +554,7 @@ struct gs_texture_2d : gs_texture {
gs_color_format colorFormat, uint32_t levels, gs_color_format colorFormat, uint32_t levels,
const uint8_t *const *data, uint32_t flags, const uint8_t *const *data, uint32_t flags,
gs_texture_type type, bool gdiCompatible, gs_texture_type type, bool gdiCompatible,
bool nv12 = false); bool twoPlane = false);
gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12, gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12,
uint32_t flags); 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_stage_surface(gs_device_t *device, uint32_t width, uint32_t height,
gs_color_format colorFormat); 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 { struct gs_sampler_state : gs_obj {
@ -985,6 +986,7 @@ struct gs_device {
ComPtr<ID3D11DeviceContext> context; ComPtr<ID3D11DeviceContext> context;
uint32_t adpIdx = 0; uint32_t adpIdx = 0;
bool nv12Supported = false; bool nv12Supported = false;
bool p010Supported = false;
gs_texture_2d *curRenderTarget = nullptr; gs_texture_2d *curRenderTarget = nullptr;
gs_zstencil_buffer *curZStencilBuffer = nullptr; gs_zstencil_buffer *curZStencilBuffer = nullptr;

View File

@ -103,7 +103,9 @@ void gs_texture_2d::InitTexture(const uint8_t *const *data)
td.Height = height; td.Height = height;
td.MipLevels = genMipmaps ? 0 : levels; td.MipLevels = genMipmaps ? 0 : levels;
td.ArraySize = type == GS_TEXTURE_CUBE ? 6 : 1; 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.BindFlags = D3D11_BIND_SHADER_RESOURCE;
td.SampleDesc.Count = 1; td.SampleDesc.Count = 1;
td.CPUAccessFlags = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0; 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 height, gs_color_format colorFormat,
uint32_t levels, const uint8_t *const *data, uint32_t levels, const uint8_t *const *data,
uint32_t flags_, gs_texture_type type, 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), : gs_texture(device, gs_type::gs_texture_2d, type, levels, colorFormat),
width(width), width(width),
height(height), height(height),
@ -280,7 +282,7 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width,
isShared((flags_ & SHARED_FLAGS) != 0), isShared((flags_ & SHARED_FLAGS) != 0),
genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0), genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0),
sharedHandle(GS_INVALID_HANDLE), sharedHandle(GS_INVALID_HANDLE),
nv12(nv12_) twoPlane(twoPlane_)
{ {
InitTexture(data); InitTexture(data);
InitResourceView(); InitResourceView();
@ -296,22 +298,26 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12tex,
isDynamic((flags_ & GS_DYNAMIC) != 0), isDynamic((flags_ & GS_DYNAMIC) != 0),
isShared((flags_ & SHARED_FLAGS) != 0), isShared((flags_ & SHARED_FLAGS) != 0),
genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0), genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0),
nv12(true), twoPlane(true),
texture(nv12tex) texture(nv12tex)
{ {
texture->GetDesc(&td); 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->type = GS_TEXTURE_2D;
this->format = GS_R8G8; this->format = p010 ? GS_RG16 : GS_R8G8;
this->flags = flags_; this->flags = flags_;
this->levels = 1; this->levels = 1;
this->device = device; this->device = device;
this->chroma = true; this->chroma = true;
this->width = td.Width / 2; this->width = td.Width / 2;
this->height = td.Height / 2; this->height = td.Height / 2;
this->dxgiFormatResource = DXGI_FORMAT_R8G8_UNORM; this->dxgiFormatResource = dxgi_format;
this->dxgiFormatView = DXGI_FORMAT_R8G8_UNORM; this->dxgiFormatView = dxgi_format;
this->dxgiFormatViewLinear = DXGI_FORMAT_R8G8_UNORM; this->dxgiFormatViewLinear = dxgi_format;
InitResourceView(); InitResourceView();
if (isRenderTarget) if (isRenderTarget)

View File

@ -190,6 +190,7 @@ bool load_graphics_imports(struct gs_exports *exports, void *module,
GRAPHICS_IMPORT(gs_shader_set_next_sampler); GRAPHICS_IMPORT(gs_shader_set_next_sampler);
GRAPHICS_IMPORT_OPTIONAL(device_nv12_available); GRAPHICS_IMPORT_OPTIONAL(device_nv12_available);
GRAPHICS_IMPORT_OPTIONAL(device_p010_available);
GRAPHICS_IMPORT(device_debug_marker_begin); GRAPHICS_IMPORT(device_debug_marker_begin);
GRAPHICS_IMPORT(device_debug_marker_end); 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_acquire_sync);
GRAPHICS_IMPORT_OPTIONAL(device_texture_release_sync); GRAPHICS_IMPORT_OPTIONAL(device_texture_release_sync);
GRAPHICS_IMPORT_OPTIONAL(device_texture_create_nv12); 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_nv12);
GRAPHICS_IMPORT_OPTIONAL(device_stagesurface_create_p010);
GRAPHICS_IMPORT_OPTIONAL(device_register_loss_callbacks); GRAPHICS_IMPORT_OPTIONAL(device_register_loss_callbacks);
GRAPHICS_IMPORT_OPTIONAL(device_unregister_loss_callbacks); GRAPHICS_IMPORT_OPTIONAL(device_unregister_loss_callbacks);
#elif __linux__ #elif __linux__

View File

@ -266,6 +266,7 @@ struct gs_exports {
gs_samplerstate_t *sampler); gs_samplerstate_t *sampler);
bool (*device_nv12_available)(gs_device_t *device); 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, void (*device_debug_marker_begin)(gs_device_t *device,
const char *markername, const char *markername,
@ -323,10 +324,18 @@ struct gs_exports {
gs_texture_t **tex_uv, gs_texture_t **tex_uv,
uint32_t width, uint32_t height, uint32_t width, uint32_t height,
uint32_t flags); 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, gs_stagesurf_t *(*device_stagesurface_create_nv12)(gs_device_t *device,
uint32_t width, uint32_t width,
uint32_t height); 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)( void (*device_register_loss_callbacks)(
gs_device_t *device, const struct gs_device_loss *callbacks); gs_device_t *device, const struct gs_device_loss *callbacks);
void (*device_unregister_loss_callbacks)(gs_device_t *device, void (*device_unregister_loss_callbacks)(gs_device_t *device,

View File

@ -2795,6 +2795,18 @@ bool gs_nv12_available(void)
thread_graphics->device); 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) void gs_debug_marker_begin(const float color[4], const char *markername)
{ {
if (!gs_valid("gs_debug_marker_begin")) 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; 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) gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height)
{ {
graphics_t *graphics = thread_graphics; graphics_t *graphics = thread_graphics;
@ -3138,6 +3189,26 @@ gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height)
return NULL; 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) void gs_register_loss_callbacks(const struct gs_device_loss *callbacks)
{ {
graphics_t *graphics = thread_graphics; graphics_t *graphics = thread_graphics;

View File

@ -835,6 +835,7 @@ EXPORT bool gs_timer_range_get_data(gs_timer_range_t *range, bool *disjoint,
uint64_t *frequency); uint64_t *frequency);
EXPORT bool gs_nv12_available(void); EXPORT bool gs_nv12_available(void);
EXPORT bool gs_p010_available(void);
#define GS_USE_DEBUG_MARKERS 0 #define GS_USE_DEBUG_MARKERS 0
#if GS_USE_DEBUG_MARKERS #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, EXPORT bool gs_texture_create_nv12(gs_texture_t **tex_y, gs_texture_t **tex_uv,
uint32_t width, uint32_t height, uint32_t width, uint32_t height,
uint32_t flags); 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, EXPORT gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width,
uint32_t height); 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_register_loss_callbacks(const struct gs_device_loss *callbacks);
EXPORT void gs_unregister_loss_callbacks(void *data); EXPORT void gs_unregister_loss_callbacks(void *data);