Browse Source

libobs/graphics, libobs-d3d11: Add P010 support

jpark37 3 years ago
parent
commit
fee3703f40

+ 7 - 7
libobs-d3d11/d3d11-rebuild.cpp

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

+ 2 - 2
libobs-d3d11/d3d11-stagesurf.cpp

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

+ 80 - 25
libobs-d3d11/d3d11-subsystem.cpp

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

+ 8 - 6
libobs-d3d11/d3d11-subsystem.hpp

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

+ 14 - 8
libobs-d3d11/d3d11-texture2d.cpp

@@ -103,7 +103,9 @@ void gs_texture_2d::InitTexture(const uint8_t *const *data)
 	td.Height = height;
 	td.MipLevels = genMipmaps ? 0 : levels;
 	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.SampleDesc.Count = 1;
 	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 levels, const uint8_t *const *data,
 			     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),
 	  width(width),
 	  height(height),
@@ -280,7 +282,7 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width,
 	  isShared((flags_ & SHARED_FLAGS) != 0),
 	  genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0),
 	  sharedHandle(GS_INVALID_HANDLE),
-	  nv12(nv12_)
+	  twoPlane(twoPlane_)
 {
 	InitTexture(data);
 	InitResourceView();
@@ -296,22 +298,26 @@ gs_texture_2d::gs_texture_2d(gs_device_t *device, ID3D11Texture2D *nv12tex,
 	  isDynamic((flags_ & GS_DYNAMIC) != 0),
 	  isShared((flags_ & SHARED_FLAGS) != 0),
 	  genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0),
-	  nv12(true),
+	  twoPlane(true),
 	  texture(nv12tex)
 {
 	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->format = GS_R8G8;
+	this->format = p010 ? GS_RG16 : 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->dxgiFormatResource = DXGI_FORMAT_R8G8_UNORM;
-	this->dxgiFormatView = DXGI_FORMAT_R8G8_UNORM;
-	this->dxgiFormatViewLinear = DXGI_FORMAT_R8G8_UNORM;
+	this->dxgiFormatResource = dxgi_format;
+	this->dxgiFormatView = dxgi_format;
+	this->dxgiFormatViewLinear = dxgi_format;
 
 	InitResourceView();
 	if (isRenderTarget)

+ 3 - 0
libobs/graphics/graphics-imports.c

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

+ 9 - 0
libobs/graphics/graphics-internal.h

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

+ 71 - 0
libobs/graphics/graphics.c

@@ -2795,6 +2795,18 @@ bool gs_nv12_available(void)
 		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)
 {
 	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;
 }
 
+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)
 {
 	graphics_t *graphics = thread_graphics;
@@ -3138,6 +3189,26 @@ gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height)
 	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)
 {
 	graphics_t *graphics = thread_graphics;

+ 6 - 0
libobs/graphics/graphics.h

@@ -835,6 +835,7 @@ EXPORT bool gs_timer_range_get_data(gs_timer_range_t *range, bool *disjoint,
 				    uint64_t *frequency);
 
 EXPORT bool gs_nv12_available(void);
+EXPORT bool gs_p010_available(void);
 
 #define GS_USE_DEBUG_MARKERS 0
 #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,
 				   uint32_t width, uint32_t height,
 				   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,
 						   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_unregister_loss_callbacks(void *data);