mesa/src/gallium/drivers/d3d12/d3d12_surface.cpp
Jesse Natalie fee83f4fcc
Some checks are pending
macOS-CI / macOS-CI (dri) (push) Waiting to run
macOS-CI / macOS-CI (xlib) (push) Waiting to run
d3d12: Move logicop descriptor initialization to after all blits
The blit code is destructive on the framebuffer state, which means
that it can set new render targets. If you have 2 BGRA surfaces bound
for logic ops, then after setting up the surface for the first one,
the blit for the second will end up destroying + re-creating the
surface for the first one.

Let's be robust to this by putting the blit in a first pass, and
then actually initializing all of the descriptors in a second pass.

This is still woefully inefficient but at least it's correct.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36945>
2025-08-25 17:21:37 +00:00

344 lines
12 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "d3d12_context.h"
#include "d3d12_format.h"
#include "d3d12_resource.h"
#include "d3d12_screen.h"
#include "d3d12_surface.h"
#include "util/format/u_format.h"
#include "util/u_inlines.h"
#include "util/u_memory.h"
static D3D12_DSV_DIMENSION
view_dsv_dimension(enum pipe_texture_target target, unsigned samples)
{
switch (target) {
case PIPE_TEXTURE_1D: return D3D12_DSV_DIMENSION_TEXTURE1D;
case PIPE_TEXTURE_1D_ARRAY: return D3D12_DSV_DIMENSION_TEXTURE1DARRAY;
case PIPE_TEXTURE_2D:
case PIPE_TEXTURE_RECT:
return samples > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS :
D3D12_DSV_DIMENSION_TEXTURE2D;
case PIPE_TEXTURE_2D_ARRAY:
case PIPE_TEXTURE_CUBE:
case PIPE_TEXTURE_CUBE_ARRAY:
return samples > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY :
D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
default:
UNREACHABLE("unexpected target");
}
}
static D3D12_RTV_DIMENSION
view_rtv_dimension(enum pipe_texture_target target, unsigned samples)
{
switch (target) {
case PIPE_BUFFER: return D3D12_RTV_DIMENSION_BUFFER;
case PIPE_TEXTURE_1D: return D3D12_RTV_DIMENSION_TEXTURE1D;
case PIPE_TEXTURE_1D_ARRAY: return D3D12_RTV_DIMENSION_TEXTURE1DARRAY;
case PIPE_TEXTURE_2D:
case PIPE_TEXTURE_RECT:
return samples > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMS :
D3D12_RTV_DIMENSION_TEXTURE2D;
case PIPE_TEXTURE_2D_ARRAY:
case PIPE_TEXTURE_CUBE:
case PIPE_TEXTURE_CUBE_ARRAY:
return samples > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY :
D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
case PIPE_TEXTURE_3D: return D3D12_RTV_DIMENSION_TEXTURE3D;
default:
UNREACHABLE("unexpected target");
}
}
static void
initialize_dsv(struct d3d12_screen *screen,
struct d3d12_resource *res,
const struct pipe_surface *tpl,
struct d3d12_descriptor_handle *handle,
DXGI_FORMAT dxgi_format)
{
D3D12_DEPTH_STENCIL_VIEW_DESC desc;
desc.Format = dxgi_format;
desc.Flags = D3D12_DSV_FLAG_NONE;
desc.ViewDimension = view_dsv_dimension(res->base.b.target, res->base.b.nr_samples);
switch (desc.ViewDimension) {
case D3D12_DSV_DIMENSION_TEXTURE1D:
if (tpl->first_layer > 0)
debug_printf("D3D12: can't create 1D DSV from layer %d\n",
tpl->first_layer);
desc.Texture1D.MipSlice = tpl->level;
break;
case D3D12_DSV_DIMENSION_TEXTURE1DARRAY:
desc.Texture1DArray.MipSlice = tpl->level;
desc.Texture1DArray.FirstArraySlice = tpl->first_layer;
desc.Texture1DArray.ArraySize = tpl->last_layer - tpl->first_layer + 1;
break;
case D3D12_DSV_DIMENSION_TEXTURE2DMS:
if (tpl->first_layer > 0)
debug_printf("D3D12: can't create 2DMS DSV from layer %d\n",
tpl->first_layer);
break;
case D3D12_DSV_DIMENSION_TEXTURE2D:
if (tpl->first_layer > 0)
debug_printf("D3D12: can't create 2D DSV from layer %d\n",
tpl->first_layer);
desc.Texture2D.MipSlice = tpl->level;
break;
case D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY:
desc.Texture2DMSArray.FirstArraySlice = tpl->first_layer;
desc.Texture2DMSArray.ArraySize = tpl->last_layer - tpl->first_layer + 1;
break;
case D3D12_DSV_DIMENSION_TEXTURE2DARRAY:
desc.Texture2DArray.MipSlice = tpl->level;
desc.Texture2DArray.FirstArraySlice = tpl->first_layer;
desc.Texture2DArray.ArraySize = tpl->last_layer - tpl->first_layer + 1;
break;
default:
UNREACHABLE("Unhandled DSV dimension");
}
mtx_lock(&screen->descriptor_pool_mutex);
d3d12_descriptor_pool_alloc_handle(screen->dsv_pool, handle);
mtx_unlock(&screen->descriptor_pool_mutex);
screen->dev->CreateDepthStencilView(d3d12_resource_resource(res), &desc,
handle->cpu_handle);
}
static void
initialize_rtv(struct d3d12_screen *screen,
struct d3d12_resource *res,
const struct pipe_surface *tpl,
struct d3d12_descriptor_handle *handle,
DXGI_FORMAT dxgi_format)
{
D3D12_RENDER_TARGET_VIEW_DESC desc;
desc.Format = dxgi_format;
desc.ViewDimension = view_rtv_dimension(res->base.b.target, res->base.b.nr_samples);
switch (desc.ViewDimension) {
case D3D12_RTV_DIMENSION_BUFFER:
desc.Buffer.FirstElement = 0;
desc.Buffer.NumElements = res->base.b.width0 / util_format_get_blocksize(tpl->format);
break;
case D3D12_RTV_DIMENSION_TEXTURE1D:
if (tpl->first_layer > 0)
debug_printf("D3D12: can't create 1D RTV from layer %d\n",
tpl->first_layer);
desc.Texture1D.MipSlice = tpl->level;
break;
case D3D12_RTV_DIMENSION_TEXTURE1DARRAY:
desc.Texture1DArray.MipSlice = tpl->level;
desc.Texture1DArray.FirstArraySlice = tpl->first_layer;
desc.Texture1DArray.ArraySize = tpl->last_layer - tpl->first_layer + 1;
break;
case D3D12_RTV_DIMENSION_TEXTURE2DMS:
if (tpl->first_layer > 0)
debug_printf("D3D12: can't create 2DMS RTV from layer %d\n",
tpl->first_layer);
break;
case D3D12_RTV_DIMENSION_TEXTURE2D:
if (tpl->first_layer > 0)
debug_printf("D3D12: can't create 2D RTV from layer %d\n",
tpl->first_layer);
desc.Texture2D.MipSlice = tpl->level;
desc.Texture2D.PlaneSlice = res->plane_slice;
break;
case D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY:
desc.Texture2DMSArray.FirstArraySlice = tpl->first_layer;
desc.Texture2DMSArray.ArraySize = tpl->last_layer - tpl->first_layer + 1;
break;
case D3D12_RTV_DIMENSION_TEXTURE2DARRAY:
desc.Texture2DArray.MipSlice = tpl->level;
desc.Texture2DArray.FirstArraySlice = tpl->first_layer;
desc.Texture2DArray.ArraySize = tpl->last_layer - tpl->first_layer + 1;
desc.Texture2DArray.PlaneSlice = 0;
break;
case D3D12_RTV_DIMENSION_TEXTURE3D:
desc.Texture3D.MipSlice = tpl->level;
desc.Texture3D.FirstWSlice = tpl->first_layer;
desc.Texture3D.WSize = tpl->last_layer - tpl->first_layer + 1;
break;
default:
UNREACHABLE("Unhandled RTV dimension");
}
mtx_lock(&screen->descriptor_pool_mutex);
d3d12_descriptor_pool_alloc_handle(screen->rtv_pool, handle);
mtx_unlock(&screen->descriptor_pool_mutex);
screen->dev->CreateRenderTargetView(d3d12_resource_resource(res), &desc,
handle->cpu_handle);
}
struct d3d12_surface *
d3d12_create_surface(struct d3d12_screen *screen,
const struct pipe_surface *tpl)
{
struct d3d12_resource *res = d3d12_resource(tpl->texture);
bool is_depth_or_stencil = util_format_is_depth_or_stencil(tpl->format);
unsigned bind = is_depth_or_stencil ? PIPE_BIND_DEPTH_STENCIL : PIPE_BIND_RENDER_TARGET;
/* Don't bother if we don't support the requested format as RT or DS */
if (!screen->base.is_format_supported(&screen->base, tpl->format, PIPE_TEXTURE_2D,
tpl->nr_samples, tpl->nr_samples,bind))
return NULL;
struct d3d12_surface *surface = CALLOC_STRUCT(d3d12_surface);
if (!surface)
return NULL;
pipe_resource_reference(&surface->base.texture, &res->base.b);
pipe_reference_init(&surface->base.reference, 1);
surface->screen = screen;
surface->base.format = tpl->format;
surface->base.level = tpl->level;
surface->base.first_layer = tpl->first_layer;
surface->base.last_layer = tpl->last_layer;
DXGI_FORMAT dxgi_format = d3d12_get_resource_rt_format(tpl->format);
if (is_depth_or_stencil)
initialize_dsv(screen, res, tpl, &surface->desc_handle, dxgi_format);
else
initialize_rtv(screen, res, tpl, &surface->desc_handle, dxgi_format);
return surface;
}
void
d3d12_surface_destroy(struct d3d12_surface *surface)
{
struct d3d12_screen *screen = surface->screen;
mtx_lock(&screen->descriptor_pool_mutex);
d3d12_descriptor_handle_free(&surface->desc_handle);
if (d3d12_descriptor_handle_is_allocated(&surface->uint_rtv_handle))
d3d12_descriptor_handle_free(&surface->uint_rtv_handle);
mtx_unlock(&screen->descriptor_pool_mutex);
pipe_resource_reference(&surface->base.texture, NULL);
FREE(surface);
}
static void
blit_logicop_surface(struct pipe_context *pctx, struct d3d12_surface *surface, bool pre)
{
struct pipe_blit_info info = {};
struct pipe_resource *logicop_surface = d3d12_resource_get_logicop_texture(d3d12_resource(surface->base.texture));
info.src.resource = pre ? surface->base.texture : logicop_surface;
info.dst.resource = pre ? logicop_surface : surface->base.texture;
info.src.format = pre ? surface->base.texture->format : PIPE_FORMAT_R8G8B8A8_UNORM;
info.dst.format = pre ? PIPE_FORMAT_R8G8B8A8_UNORM : surface->base.texture->format;
info.src.level = info.dst.level = 0;
info.src.box.x = info.dst.box.x = 0;
info.src.box.y = info.dst.box.y = 0;
info.src.box.z = info.dst.box.z = 0;
info.src.box.width = info.dst.box.width = pipe_surface_width(&surface->base);
info.src.box.height = info.dst.box.height = pipe_surface_height(&surface->base);
info.src.box.depth = info.dst.box.depth = 0;
info.mask = PIPE_MASK_RGBA;
d3d12_blit(pctx, &info);
}
enum d3d12_surface_conversion_mode
d3d12_surface_update_pre_draw(struct pipe_context *pctx,
struct d3d12_surface *surface,
DXGI_FORMAT format)
{
struct d3d12_resource *res = d3d12_resource(surface->base.texture);
DXGI_FORMAT dxgi_format = d3d12_get_resource_rt_format(surface->base.format);
enum d3d12_surface_conversion_mode mode;
if (dxgi_format == format)
return D3D12_SURFACE_CONVERSION_NONE;
struct pipe_resource *logicop_resource = d3d12_resource_get_logicop_texture(res);
if (logicop_resource == surface->base.texture) {
mode = D3D12_SURFACE_CONVERSION_RGBA_UINT;
} else {
blit_logicop_surface(pctx, surface, true);
res = d3d12_resource(logicop_resource);
mode = D3D12_SURFACE_CONVERSION_BGRA_UINT;
}
return mode;
}
void
d3d12_surface_update_post_draw(struct pipe_context *pctx,
struct d3d12_surface *surface,
enum d3d12_surface_conversion_mode mode)
{
if (mode == D3D12_SURFACE_CONVERSION_BGRA_UINT)
blit_logicop_surface(pctx, surface, false);
}
D3D12_CPU_DESCRIPTOR_HANDLE
d3d12_surface_get_handle(struct d3d12_surface *surface,
enum d3d12_surface_conversion_mode mode,
DXGI_FORMAT format)
{
if (mode != D3D12_SURFACE_CONVERSION_NONE) {
struct d3d12_resource *base_res = d3d12_resource(surface->base.texture);
if (!d3d12_descriptor_handle_is_allocated(&surface->uint_rtv_handle)) {
initialize_rtv(surface->screen,
d3d12_resource(d3d12_resource_get_logicop_texture(base_res)),
&surface->base, &surface->uint_rtv_handle, format);
}
return surface->uint_rtv_handle.cpu_handle;
}
return surface->desc_handle.cpu_handle;
}