d3d12: Validate opened D3D12 resource matches pipe template

Unlike Linux dma-bufs, D3D12 resources are strongly typed, and
can't necessarily just reinterpret the memory arbitrarily.

Allow importing resources with no description coming from the frontend,
and populate the resource desc from the driver instead. If there was
a template, make sure that it matches the incoming resource.

Reviewed-by: Bill Kristiansen <billkris@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/13054>
This commit is contained in:
Jesse Natalie
2021-09-22 14:47:50 -07:00
committed by Marge Bot
parent 9740141b2e
commit b8f41c5c4e
3 changed files with 178 additions and 9 deletions

View File

@@ -199,6 +199,52 @@ d3d12_get_typeless_format(enum pipe_format format)
return typeless_formats[format];
}
enum pipe_format
d3d12_get_pipe_format(DXGI_FORMAT format)
{
for (unsigned i = 0; i < ARRAY_SIZE(formats); ++i) {
if (formats[i] == format) {
return (enum pipe_format)i;
}
}
return PIPE_FORMAT_NONE;
}
enum pipe_format
d3d12_get_default_pipe_format(DXGI_FORMAT format)
{
#define TYPELESS_TO(channels, suffix) \
case DXGI_FORMAT_##channels##_TYPELESS: \
return PIPE_FORMAT_##channels##_##suffix
switch (format) {
TYPELESS_TO(R8, UNORM);
TYPELESS_TO(R8G8, UNORM);
TYPELESS_TO(R8G8B8A8, UNORM);
TYPELESS_TO(B8G8R8X8, UNORM);
TYPELESS_TO(B8G8R8A8, UNORM);
TYPELESS_TO(R16, FLOAT);
TYPELESS_TO(R16G16, FLOAT);
TYPELESS_TO(R16G16B16A16, FLOAT);
TYPELESS_TO(R32, FLOAT);
TYPELESS_TO(R32G32, FLOAT);
TYPELESS_TO(R32G32B32, FLOAT);
TYPELESS_TO(R32G32B32A32, FLOAT);
case DXGI_FORMAT_BC1_TYPELESS:
return PIPE_FORMAT_DXT1_RGBA;
case DXGI_FORMAT_BC2_TYPELESS:
return PIPE_FORMAT_DXT3_RGBA;
case DXGI_FORMAT_BC3_TYPELESS:
return PIPE_FORMAT_DXT5_RGBA;
case DXGI_FORMAT_BC4_TYPELESS:
return PIPE_FORMAT_RGTC1_UNORM;
case DXGI_FORMAT_BC5_TYPELESS:
return PIPE_FORMAT_RGTC2_UNORM;
default:
return PIPE_FORMAT_NONE;
}
}
DXGI_FORMAT
d3d12_get_resource_rt_format(enum pipe_format f)
{

View File

@@ -39,6 +39,13 @@ d3d12_get_format(enum pipe_format format);
DXGI_FORMAT
d3d12_get_typeless_format(enum pipe_format format);
/* These two are only used for importing external resources without a provided template */
enum pipe_format
d3d12_get_pipe_format(DXGI_FORMAT format);
enum pipe_format
d3d12_get_default_pipe_format(DXGI_FORMAT format);
DXGI_FORMAT
d3d12_get_resource_srv_format(enum pipe_format f, enum pipe_texture_target target);

View File

@@ -306,16 +306,14 @@ d3d12_resource_from_handle(struct pipe_screen *pscreen,
if (!res)
return NULL;
res->base.b = *templ;
pipe_reference_init(&res->base.b.reference, 1);
res->base.b.screen = pscreen;
res->dxgi_format = templ->target == PIPE_BUFFER ? DXGI_FORMAT_UNKNOWN :
d3d12_get_format(templ->format);
ID3D12Resource *d3d12_res = nullptr;
if (handle->type == WINSYS_HANDLE_TYPE_D3D12_RES) {
res->bo = d3d12_bo_wrap_res((ID3D12Resource *)handle->com_obj, templ->format);
d3d12_res = (ID3D12Resource *)handle->com_obj;
} else {
struct d3d12_screen *screen = d3d12_screen(pscreen);
ID3D12Resource *d3d12_res = nullptr;
#ifdef _WIN32
HANDLE d3d_handle = handle->handle;
@@ -323,16 +321,134 @@ d3d12_resource_from_handle(struct pipe_screen *pscreen,
HANDLE d3d_handle = (HANDLE)(intptr_t)handle->handle;
#endif
screen->dev->OpenSharedHandle(d3d_handle, IID_PPV_ARGS(&d3d12_res));
if (!d3d12_res) {
FREE(res);
return NULL;
}
D3D12_RESOURCE_DESC incoming_res_desc;
if (!d3d12_res)
goto invalid;
incoming_res_desc = d3d12_res->GetDesc();
if (incoming_res_desc.Width > UINT32_MAX ||
incoming_res_desc.Height > UINT16_MAX) {
debug_printf("d3d12: Importing resource too large\n");
goto invalid;
}
res->base.b.width0 = incoming_res_desc.Width;
res->base.b.height0 = incoming_res_desc.Height;
res->base.b.depth0 = 1;
res->base.b.array_size = 1;
switch (incoming_res_desc.Dimension) {
case D3D12_RESOURCE_DIMENSION_BUFFER:
res->base.b.target = PIPE_BUFFER;
res->base.b.bind = PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_CONSTANT_BUFFER |
PIPE_BIND_INDEX_BUFFER | PIPE_BIND_STREAM_OUTPUT | PIPE_BIND_SHADER_BUFFER |
PIPE_BIND_COMMAND_ARGS_BUFFER | PIPE_BIND_QUERY_BUFFER;
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
res->base.b.target = incoming_res_desc.DepthOrArraySize > 1 ?
PIPE_TEXTURE_1D_ARRAY : PIPE_TEXTURE_1D;
res->base.b.array_size = incoming_res_desc.DepthOrArraySize;
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
res->base.b.target = incoming_res_desc.DepthOrArraySize > 1 ?
PIPE_TEXTURE_2D_ARRAY : PIPE_TEXTURE_2D;
res->base.b.array_size = incoming_res_desc.DepthOrArraySize;
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
res->base.b.target = PIPE_TEXTURE_3D;
res->base.b.depth0 = incoming_res_desc.DepthOrArraySize;
break;
default:
unreachable("Invalid dimension");
break;
}
res->base.b.nr_samples = incoming_res_desc.SampleDesc.Count;
res->base.b.last_level = incoming_res_desc.MipLevels - 1;
res->base.b.usage = PIPE_USAGE_DEFAULT;
if (incoming_res_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
res->base.b.bind |= PIPE_BIND_RENDER_TARGET | PIPE_BIND_BLENDABLE | PIPE_BIND_DISPLAY_TARGET;
if (incoming_res_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
res->base.b.bind |= PIPE_BIND_DEPTH_STENCIL;
if (incoming_res_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
res->base.b.bind |= PIPE_BIND_SHADER_IMAGE;
if ((incoming_res_desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) == D3D12_RESOURCE_FLAG_NONE)
res->base.b.bind |= PIPE_BIND_SAMPLER_VIEW;
if (templ) {
if (res->base.b.target == PIPE_TEXTURE_2D_ARRAY &&
(templ->target == PIPE_TEXTURE_CUBE ||
templ->target == PIPE_TEXTURE_CUBE_ARRAY)) {
if (res->base.b.array_size < 6) {
debug_printf("d3d12: Importing cube resource with too few array layers\n");
goto invalid;
}
res->base.b.target = templ->target;
res->base.b.array_size /= 6;
}
unsigned templ_samples = MAX2(templ->nr_samples, 1);
if (res->base.b.target != templ->target ||
res->base.b.width0 != templ->width0 ||
res->base.b.height0 != templ->height0 ||
res->base.b.depth0 != templ->depth0 ||
res->base.b.array_size != templ->array_size ||
incoming_res_desc.SampleDesc.Count != templ_samples ||
res->base.b.last_level != templ->last_level) {
debug_printf("d3d12: Importing resource with mismatched dimensions: "
"plane: %d, target: %d vs %d, width: %d vs %d, height: %d vs %d, "
"depth: %d vs %d, array_size: %d vs %d, samples: %d vs %d, mips: %d vs %d\n",
handle->plane,
res->base.b.target, templ->target,
res->base.b.width0, templ->width0,
res->base.b.height0, templ->height0,
res->base.b.depth0, templ->depth0,
res->base.b.array_size, templ->array_size,
incoming_res_desc.SampleDesc.Count, templ_samples,
res->base.b.last_level + 1, templ->last_level + 1);
goto invalid;
}
if (incoming_res_desc.Format != d3d12_get_format(templ->format) &&
incoming_res_desc.Format != d3d12_get_typeless_format(templ->format)) {
debug_printf("d3d12: Importing resource with mismatched format: "
"could be DXGI format %d or %d, but is %d\n",
d3d12_get_format(templ->format),
d3d12_get_typeless_format(templ->format),
incoming_res_desc.Format);
goto invalid;
}
if (templ->bind & ~res->base.b.bind) {
debug_printf("d3d12: Imported resource doesn't have necessary bind flags\n");
goto invalid;
}
res->bo = d3d12_bo_wrap_res(d3d12_res, templ->format);
res->base.b.format = templ->format;
} else {
/* Search the pipe format lookup table for an entry */
res->base.b.format = d3d12_get_pipe_format(incoming_res_desc.Format);
if (res->base.b.format == PIPE_FORMAT_NONE) {
/* Convert from typeless to a reasonable default */
res->base.b.format = d3d12_get_default_pipe_format(incoming_res_desc.Format);
if (res->base.b.format == PIPE_FORMAT_NONE) {
debug_printf("d3d12: Unable to deduce non-typeless resource format %d\n", incoming_res_desc.Format);
goto invalid;
}
}
}
res->dxgi_format = d3d12_get_format(res->base.b.format);
res->bo = d3d12_bo_wrap_res(d3d12_res, res->base.b.format);
init_valid_range(res);
threaded_resource_init(&res->base.b, false, 0);
return &res->base.b;
invalid:
if (d3d12_res)
d3d12_res->Release();
FREE(res);
return NULL;
}
static bool