Files
third_party_mesa3d/src/gallium/drivers/d3d12/d3d12_resource.cpp
Yonggang Luo 1ac1c0843f treewide: Replace usage of macro DEBUG with MESA_DEBUG when possible
This is achieved by the following steps:

#ifndef DEBUG => #if !MESA_DEBUG
defined(DEBUG) => MESA_DEBUG
#ifdef DEBUG => #if MESA_DEBUG

This is done by replace in vscode

excludes
docs,*.rs,addrlib,src/imgui,*.sh,src/intel/vulkan/grl/gpu

These are safe because those files should keep DEBUG macro is already excluded;
and not directly replace DEBUG, as we have some symbols around it.

Use debug or NDEBUG instead of DEBUG in comments when proper

This for reduce the usage of DEBUG,
so it's easier migrating to MESA_DEBUG

These are found when migrating DEBUG to MESA_DEBUG,
these are all comment update, so it's safe

Replace comment /* DEBUG */ and /* !DEBUG */ with proper /* MESA_DEBUG */ or /* !MESA_DEBUG */ manually

DEBUG || !NDEBUG -> MESA_DEBUG || !NDEBUG
!DEBUG && NDEBUG -> !(MESA_DEBUG || !NDEBUG)

Replace the DEBUG present in comment with proper new MESA_DEBUG manually

Signed-off-by: Yonggang Luo <luoyonggang@gmail.com>
Acked-by: David Heidelberg <david.heidelberg@collabora.com>
Reviewed-by: Eric Engestrom <eric@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/28092>
2024-03-22 18:22:34 +00:00

2009 lines
76 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_resource.h"
#include "d3d12_blit.h"
#include "d3d12_context.h"
#include "d3d12_format.h"
#include "d3d12_screen.h"
#include "d3d12_debug.h"
#include "pipebuffer/pb_bufmgr.h"
#include "util/slab.h"
#include "util/format/u_format.h"
#include "util/u_inlines.h"
#include "util/u_memory.h"
#include "util/format/u_format_zs.h"
#include "frontend/sw_winsys.h"
#include <dxguids/dxguids.h>
#include <memory>
#ifndef _GAMING_XBOX
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;
#endif
#ifndef GENERIC_ALL
// This is only added to winadapter.h in newer DirectX-Headers
#define GENERIC_ALL 0x10000000L
#endif
static bool
can_map_directly(struct pipe_resource *pres)
{
return pres->target == PIPE_BUFFER &&
pres->usage != PIPE_USAGE_DEFAULT &&
pres->usage != PIPE_USAGE_IMMUTABLE;
}
static void
init_valid_range(struct d3d12_resource *res)
{
if (can_map_directly(&res->base.b))
util_range_init(&res->valid_buffer_range);
}
static void
d3d12_resource_destroy(struct pipe_screen *pscreen,
struct pipe_resource *presource)
{
struct d3d12_resource *resource = d3d12_resource(presource);
// When instanciating a planar d3d12_resource, the same resource->dt pointer
// is copied to all their planes linked list resources
// Different instances of objects like d3d12_surface, can be pointing
// to different planes of the same overall (ie. NV12) planar d3d12_resource
// sharing the same dt, so keep a refcount when destroying them
// and only destroy it on the last plane being destroyed
if (resource->dt_refcount > 0)
resource->dt_refcount--;
if ((resource->dt_refcount == 0) && resource->dt)
{
struct d3d12_screen *screen = d3d12_screen(pscreen);
screen->winsys->displaytarget_destroy(screen->winsys, resource->dt);
}
if (resource->dt_proxy)
pipe_resource_reference(&resource->dt_proxy, NULL);
threaded_resource_deinit(presource);
if (can_map_directly(presource))
util_range_destroy(&resource->valid_buffer_range);
if (resource->bo)
d3d12_bo_unreference(resource->bo);
FREE(resource);
}
static bool
resource_is_busy(struct d3d12_context *ctx,
struct d3d12_resource *res,
bool want_to_write)
{
if (d3d12_batch_has_references(d3d12_current_batch(ctx), res->bo, want_to_write))
return true;
bool busy = false;
d3d12_foreach_submitted_batch(ctx, batch) {
if (!d3d12_reset_batch(ctx, batch, 0))
busy |= d3d12_batch_has_references(batch, res->bo, want_to_write);
}
return busy;
}
void
d3d12_resource_wait_idle(struct d3d12_context *ctx,
struct d3d12_resource *res,
bool want_to_write)
{
if (d3d12_batch_has_references(d3d12_current_batch(ctx), res->bo, want_to_write)) {
d3d12_flush_cmdlist_and_wait(ctx);
} else {
d3d12_foreach_submitted_batch(ctx, batch) {
if (d3d12_batch_has_references(batch, res->bo, want_to_write))
d3d12_reset_batch(ctx, batch, OS_TIMEOUT_INFINITE);
}
}
}
void
d3d12_resource_release(struct d3d12_resource *resource)
{
if (!resource->bo)
return;
d3d12_bo_unreference(resource->bo);
resource->bo = NULL;
}
static bool
init_buffer(struct d3d12_screen *screen,
struct d3d12_resource *res,
const struct pipe_resource *templ)
{
struct pb_desc buf_desc;
struct pb_manager *bufmgr;
struct pb_buffer *buf;
/* Assert that we don't want to create a buffer with one of the emulated
* formats, these are (currently) only supported when passing the vertex
* element state */
assert(templ->format == d3d12_emulated_vtx_format(templ->format));
if ((templ->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT) &&
res->base.b.usage == PIPE_USAGE_DEFAULT)
{
res->base.b.usage = PIPE_USAGE_STAGING;
}
switch (res->base.b.usage) {
case PIPE_USAGE_DEFAULT:
case PIPE_USAGE_IMMUTABLE:
bufmgr = screen->cache_bufmgr;
buf_desc.usage = (pb_usage_flags)PB_USAGE_GPU_READ_WRITE;
break;
case PIPE_USAGE_DYNAMIC:
case PIPE_USAGE_STREAM:
bufmgr = screen->slab_bufmgr;
buf_desc.usage = (pb_usage_flags)(PB_USAGE_CPU_WRITE | PB_USAGE_GPU_READ);
break;
case PIPE_USAGE_STAGING:
bufmgr = screen->readback_slab_bufmgr;
buf_desc.usage = (pb_usage_flags)(PB_USAGE_GPU_WRITE | PB_USAGE_CPU_READ_WRITE);
break;
default:
unreachable("Invalid pipe usage");
}
/* We can't suballocate buffers that might be bound as a sampler view, *only*
* because in the case of R32G32B32 formats (12 bytes per pixel), it's not possible
* to guarantee the offset will be divisible.
*/
if (templ->bind & PIPE_BIND_SAMPLER_VIEW)
bufmgr = screen->cache_bufmgr;
buf_desc.alignment = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT;
res->dxgi_format = DXGI_FORMAT_UNKNOWN;
buf = bufmgr->create_buffer(bufmgr, templ->width0, &buf_desc);
if (!buf)
return false;
res->bo = d3d12_bo_wrap_buffer(screen, buf);
return true;
}
static bool
init_texture(struct d3d12_screen *screen,
struct d3d12_resource *res,
const struct pipe_resource *templ,
ID3D12Heap *heap,
uint64_t placed_offset)
{
ID3D12Resource *d3d12_res;
res->mip_levels = templ->last_level + 1;
res->dxgi_format = d3d12_get_format(templ->format);
D3D12_RESOURCE_DESC desc;
desc.Format = res->dxgi_format;
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
desc.Width = templ->width0;
desc.Height = templ->height0;
desc.DepthOrArraySize = templ->array_size;
desc.MipLevels = templ->last_level + 1;
desc.SampleDesc.Count = MAX2(templ->nr_samples, 1);
desc.SampleDesc.Quality = 0;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
switch (templ->target) {
case PIPE_BUFFER:
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
break;
case PIPE_TEXTURE_1D:
case PIPE_TEXTURE_1D_ARRAY:
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE1D;
break;
case PIPE_TEXTURE_CUBE:
case PIPE_TEXTURE_CUBE_ARRAY:
case PIPE_TEXTURE_2D:
case PIPE_TEXTURE_2D_ARRAY:
case PIPE_TEXTURE_RECT:
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
break;
case PIPE_TEXTURE_3D:
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
desc.DepthOrArraySize = templ->depth0;
break;
default:
unreachable("Invalid texture type");
}
if (templ->bind & PIPE_BIND_SHADER_BUFFER)
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
if (templ->bind & PIPE_BIND_RENDER_TARGET)
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
if (templ->bind & PIPE_BIND_DEPTH_STENCIL) {
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
/* Sadly, we can't set D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE in the
* case where PIPE_BIND_SAMPLER_VIEW isn't set, because that would
* prevent us from using the resource with u_blitter, which requires
* sneaking in sampler-usage throught the back-door.
*/
}
const DXGI_FORMAT *format_cast_list = NULL;
uint32_t num_castable_formats = 0;
if (screen->opts12.RelaxedFormatCastingSupported) {
/* All formats that fall into a cast set need to be castable and accessible as a shader image. */
format_cast_list = d3d12_get_format_cast_list(templ->format, &num_castable_formats);
if (format_cast_list != nullptr && !util_format_is_compressed(templ->format) &&
screen->support_shader_images && templ->nr_samples <= 1) {
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
} else {
/* The VA frontend VaFourccToPipeFormat chooses _UNORM types for RGBx formats as typeless formats
* such as DXGI_R8G8B8A8_TYPELESS are not supported as Video Processor input/output as specified in:
* https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-1-formats
* PIPE_BIND_CUSTOM is used by the video frontend to hint this resource will be used in video and the
* original format must be not converted to _TYPELESS
*/
if (((templ->bind & PIPE_BIND_CUSTOM) == 0) &&
(screen->support_shader_images && templ->nr_samples <= 1)) {
/* Ideally, we'd key off of PIPE_BIND_SHADER_IMAGE for this, but it doesn't
* seem to be set properly. So, all UAV-capable resources need the UAV flag.
*/
D3D12_FEATURE_DATA_FORMAT_SUPPORT support = { desc.Format };
if (SUCCEEDED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support))) &&
(support.Support2 & (D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE)) ==
(D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE)) {
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
desc.Format = d3d12_get_typeless_format(templ->format);
}
}
}
if (templ->bind & (PIPE_BIND_SCANOUT | PIPE_BIND_LINEAR))
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
HRESULT hres = E_FAIL;
enum d3d12_residency_status init_residency;
if (heap && screen->max_feature_level == D3D_FEATURE_LEVEL_1_0_GENERIC) {
D3D12_FEATURE_DATA_PLACED_RESOURCE_SUPPORT_INFO capData;
capData.Dimension = desc.Dimension;
capData.Format = desc.Format;
capData.DestHeapProperties = GetDesc(heap).Properties;
capData.Supported = false;
if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_PLACED_RESOURCE_SUPPORT_INFO, &capData, sizeof(capData))) || !capData.Supported) {
debug_printf("D3D12: d3d12_resource_create_or_place cannot place a resource since D3D12_FEATURE_DATA_PLACED_RESOURCE_SUPPORT_INFO is not supported\n");
return false;
}
}
if (screen->opts12.RelaxedFormatCastingSupported) {
D3D12_RESOURCE_DESC1 desc1 = {
desc.Dimension,
desc.Alignment,
desc.Width,
desc.Height,
desc.DepthOrArraySize,
desc.MipLevels,
desc.Format,
desc.SampleDesc,
desc.Layout,
desc.Flags,
};
if (heap) {
init_residency = d3d12_permanently_resident;
hres = screen->dev10->CreatePlacedResource2(heap,
placed_offset,
&desc1,
D3D12_BARRIER_LAYOUT_COMMON,
nullptr,
num_castable_formats,
format_cast_list,
IID_PPV_ARGS(&d3d12_res));
}
else {
D3D12_HEAP_PROPERTIES heap_pris = GetCustomHeapProperties(screen->dev, D3D12_HEAP_TYPE_DEFAULT);
D3D12_HEAP_FLAGS heap_flags = screen->support_create_not_resident ?
D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT : D3D12_HEAP_FLAG_NONE;
init_residency = screen->support_create_not_resident ? d3d12_evicted : d3d12_resident;
hres = screen->dev10->CreateCommittedResource3(&heap_pris,
heap_flags,
&desc1,
D3D12_BARRIER_LAYOUT_COMMON,
nullptr,
nullptr,
num_castable_formats,
format_cast_list,
IID_PPV_ARGS(&d3d12_res));
}
} else {
if (heap) {
init_residency = d3d12_permanently_resident;
hres = screen->dev->CreatePlacedResource(heap,
placed_offset,
&desc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(&d3d12_res));
} else {
D3D12_HEAP_PROPERTIES heap_pris = GetCustomHeapProperties(screen->dev, D3D12_HEAP_TYPE_DEFAULT);
D3D12_HEAP_FLAGS heap_flags = screen->support_create_not_resident ?
D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT : D3D12_HEAP_FLAG_NONE;
init_residency = screen->support_create_not_resident ? d3d12_evicted : d3d12_resident;
hres = screen->dev->CreateCommittedResource(&heap_pris,
heap_flags,
&desc,
D3D12_RESOURCE_STATE_COMMON,
NULL,
IID_PPV_ARGS(&d3d12_res));
}
}
if (FAILED(hres))
return false;
if (screen->winsys && (templ->bind & PIPE_BIND_DISPLAY_TARGET)) {
struct sw_winsys *winsys = screen->winsys;
if (winsys->is_displaytarget_format_supported(winsys, res->base.b.bind, res->base.b.format)) {
res->dt = winsys->displaytarget_create(screen->winsys,
res->base.b.bind,
res->base.b.format,
templ->width0,
templ->height0,
64, NULL,
&res->dt_stride);
res->dt_refcount = 1;
} else {
assert(res->base.b.format == PIPE_FORMAT_R16G16B16A16_FLOAT); /* The only format we proxy right now */
struct pipe_resource proxy_templ = *templ;
proxy_templ.format = PIPE_FORMAT_R8G8B8A8_UNORM;
res->dt_proxy = screen->base.resource_create(&screen->base, &proxy_templ);
if (!res->dt_proxy)
return false;
assert(d3d12_resource(res->dt_proxy)->dt);
}
}
res->bo = d3d12_bo_wrap_res(screen, d3d12_res, init_residency);
return true;
}
static void
convert_planar_resource(struct d3d12_resource *res)
{
unsigned num_planes = util_format_get_num_planes(res->base.b.format);
if (num_planes <= 1 || res->base.b.next || !res->bo)
return;
struct pipe_resource *next = nullptr;
struct pipe_resource *planes[3] = {
&res->base.b, nullptr, nullptr
};
for (int plane = num_planes - 1; plane >= 0; --plane) {
struct d3d12_resource *plane_res = d3d12_resource(planes[plane]);
if (!plane_res) {
plane_res = CALLOC_STRUCT(d3d12_resource);
*plane_res = *res;
plane_res->dt_refcount = num_planes;
d3d12_bo_reference(plane_res->bo);
pipe_reference_init(&plane_res->base.b.reference, 1);
threaded_resource_init(&plane_res->base.b, false);
}
plane_res->base.b.next = next;
next = &plane_res->base.b;
plane_res->plane_slice = plane;
plane_res->base.b.format = util_format_get_plane_format(res->base.b.format, plane);
plane_res->base.b.width0 = util_format_get_plane_width(res->base.b.format, plane, res->base.b.width0);
plane_res->base.b.height0 = util_format_get_plane_height(res->base.b.format, plane, res->base.b.height0);
#if MESA_DEBUG
struct d3d12_screen *screen = d3d12_screen(res->base.b.screen);
D3D12_RESOURCE_DESC desc = GetDesc(res->bo->res);
desc.Flags &= ~D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT placed_footprint = {};
D3D12_SUBRESOURCE_FOOTPRINT *footprint = &placed_footprint.Footprint;
unsigned subresource = plane * desc.MipLevels * desc.DepthOrArraySize;
screen->dev->GetCopyableFootprints(&desc, subresource, 1, 0, &placed_footprint, nullptr, nullptr, nullptr);
assert(plane_res->base.b.width0 == footprint->Width);
assert(plane_res->base.b.height0 == footprint->Height);
assert(plane_res->base.b.depth0 == footprint->Depth);
assert(plane_res->first_plane == &res->base.b);
#endif
}
}
static struct pipe_resource *
d3d12_resource_create_or_place(struct d3d12_screen *screen,
struct d3d12_resource *res,
const struct pipe_resource *templ,
ID3D12Heap *heap,
uint64_t placed_offset)
{
bool ret;
res->base.b = *templ;
res->overall_format = templ->format;
res->plane_slice = 0;
res->first_plane = &res->base.b;
if (D3D12_DEBUG_RESOURCE & d3d12_debug) {
debug_printf("D3D12: Create %sresource %s@%d %dx%dx%d as:%d mip:%d\n",
templ->usage == PIPE_USAGE_STAGING ? "STAGING " :"",
util_format_name(templ->format), templ->nr_samples,
templ->width0, templ->height0, templ->depth0,
templ->array_size, templ->last_level);
}
pipe_reference_init(&res->base.b.reference, 1);
res->base.b.screen = &screen->base;
if (templ->target == PIPE_BUFFER && !heap) {
ret = init_buffer(screen, res, templ);
} else {
ret = init_texture(screen, res, templ, heap, placed_offset);
}
if (!ret) {
FREE(res);
return NULL;
}
init_valid_range(res);
threaded_resource_init(&res->base.b,
templ->usage == PIPE_USAGE_DEFAULT &&
templ->target == PIPE_BUFFER);
memset(&res->bind_counts, 0, sizeof(d3d12_resource::bind_counts));
convert_planar_resource(res);
return &res->base.b;
}
static struct pipe_resource *
d3d12_resource_create(struct pipe_screen *pscreen,
const struct pipe_resource *templ)
{
struct d3d12_resource *res = CALLOC_STRUCT(d3d12_resource);
if (!res)
return NULL;
return d3d12_resource_create_or_place(d3d12_screen(pscreen), res, templ, nullptr, 0);
}
static struct pipe_resource *
d3d12_resource_from_handle(struct pipe_screen *pscreen,
const struct pipe_resource *templ,
struct winsys_handle *handle, unsigned usage)
{
struct d3d12_screen *screen = d3d12_screen(pscreen);
if (handle->type != WINSYS_HANDLE_TYPE_D3D12_RES &&
handle->type != WINSYS_HANDLE_TYPE_FD &&
handle->type != WINSYS_HANDLE_TYPE_WIN32_NAME)
return NULL;
struct d3d12_resource *res = CALLOC_STRUCT(d3d12_resource);
if (!res)
return NULL;
if (templ && templ->next) {
struct d3d12_resource* next = d3d12_resource(templ->next);
if (next->bo) {
res->base.b = *templ;
res->bo = next->bo;
d3d12_bo_reference(res->bo);
}
}
#ifdef _WIN32
HANDLE d3d_handle = handle->handle;
#else
HANDLE d3d_handle = (HANDLE) (intptr_t) handle->handle;
#endif
#ifndef _GAMING_XBOX
if (handle->type == WINSYS_HANDLE_TYPE_D3D12_RES) {
ComPtr<IUnknown> screen_device;
ComPtr<IUnknown> res_device;
screen->dev->QueryInterface(screen_device.GetAddressOf());
((ID3D12DeviceChild *)handle->com_obj)->GetDevice(IID_PPV_ARGS(res_device.GetAddressOf()));
if (screen_device.Get() != res_device.Get()) {
debug_printf("d3d12: Importing resource - Resource's parent device (%p) does not"
" match d3d12 device (%p) instance from this pipe_screen."
" Attempting to re-import via NT Handle...\n", screen_device.Get(), res_device.Get());
handle->type = WINSYS_HANDLE_TYPE_FD;
HRESULT hr = screen->dev->CreateSharedHandle(((ID3D12DeviceChild *)handle->com_obj),
nullptr,
GENERIC_ALL,
nullptr,
&d3d_handle);
if (FAILED(hr)) {
debug_printf("d3d12: Error %x - Couldn't export incoming resource com_obj "
"(%p) via shared NT handle.\n", hr, handle->com_obj);
return NULL;
}
}
}
#endif
#ifdef _WIN32
HANDLE d3d_handle_to_close = nullptr;
if (handle->type == WINSYS_HANDLE_TYPE_WIN32_NAME) {
screen->dev->OpenSharedHandleByName((LPCWSTR)handle->name, GENERIC_ALL, &d3d_handle_to_close);
d3d_handle = d3d_handle_to_close;
}
#endif
ID3D12Resource *d3d12_res = nullptr;
ID3D12Heap *d3d12_heap = nullptr;
if (res->bo) {
d3d12_res = res->bo->res;
} else if (handle->type == WINSYS_HANDLE_TYPE_D3D12_RES) {
if (handle->modifier == 1) {
d3d12_heap = (ID3D12Heap *) handle->com_obj;
} else {
d3d12_res = (ID3D12Resource *) handle->com_obj;
}
} else {
screen->dev->OpenSharedHandle(d3d_handle, IID_PPV_ARGS(&d3d12_res));
}
#ifdef _WIN32
if (d3d_handle_to_close) {
CloseHandle(d3d_handle_to_close);
}
#endif
D3D12_PLACED_SUBRESOURCE_FOOTPRINT placed_footprint = {};
D3D12_SUBRESOURCE_FOOTPRINT *footprint = &placed_footprint.Footprint;
D3D12_RESOURCE_DESC incoming_res_desc;
if (!d3d12_res && !d3d12_heap)
goto invalid;
if (d3d12_heap) {
assert(templ);
assert(!res->bo);
assert(!d3d12_res);
return d3d12_resource_create_or_place(screen, res, templ, d3d12_heap, handle->offset);
}
pipe_reference_init(&res->base.b.reference, 1);
res->base.b.screen = pscreen;
incoming_res_desc = GetDesc(d3d12_res);
/* Get a description for this plane */
if (templ && handle->format != templ->format) {
unsigned subresource = handle->plane * incoming_res_desc.MipLevels * incoming_res_desc.DepthOrArraySize;
auto temp_desc = incoming_res_desc;
temp_desc.Flags &= ~D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
screen->dev->GetCopyableFootprints(&temp_desc, subresource, 1, 0, &placed_footprint, nullptr, nullptr, nullptr);
} else {
footprint->Format = incoming_res_desc.Format;
footprint->Width = incoming_res_desc.Width;
footprint->Height = incoming_res_desc.Height;
footprint->Depth = incoming_res_desc.DepthOrArraySize;
}
if (footprint->Width > UINT32_MAX ||
footprint->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 = footprint->Depth;
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;
res->base.b.bind |= PIPE_BIND_SHARED;
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 ||
footprint->Width != templ->width0 ||
footprint->Height != templ->height0 ||
footprint->Depth != 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,
footprint->Width, templ->width0,
footprint->Height, templ->height0,
footprint->Depth, 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 (templ->target != PIPE_BUFFER) {
if ((footprint->Format != d3d12_get_format(templ->format) &&
footprint->Format != d3d12_get_typeless_format(templ->format)) ||
(incoming_res_desc.Format != d3d12_get_format((enum pipe_format)handle->format) &&
incoming_res_desc.Format != d3d12_get_typeless_format((enum pipe_format)handle->format))) {
debug_printf("d3d12: Importing resource with mismatched format: "
"plane could be DXGI format %d or %d, but is %d, "
"overall could be DXGI format %d or %d, but is %d\n",
d3d12_get_format(templ->format),
d3d12_get_typeless_format(templ->format),
footprint->Format,
d3d12_get_format((enum pipe_format)handle->format),
d3d12_get_typeless_format((enum pipe_format)handle->format),
incoming_res_desc.Format);
goto invalid;
}
}
/* In an ideal world we'd be able to validate this, but gallium's use of bind
* flags during resource creation is pretty bad: some bind flags are always set
* (like PIPE_BIND_RENDER_TARGET) while others are never set (PIPE_BIND_SHADER_BUFFER)
*
if (templ->bind & ~res->base.b.bind) {
debug_printf("d3d12: Imported resource doesn't have necessary bind flags\n");
goto invalid;
} */
res->base.b.format = templ->format;
res->overall_format = (enum pipe_format)handle->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 */
if (incoming_res_desc.Format == DXGI_FORMAT_UNKNOWN)
res->base.b.format = PIPE_FORMAT_R8_UNORM;
else
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->overall_format = res->base.b.format;
}
if (!templ)
handle->format = res->overall_format;
res->dxgi_format = d3d12_get_format(res->overall_format);
res->plane_slice = handle->plane;
res->first_plane = &res->base.b;
if (!res->bo) {
res->bo = d3d12_bo_wrap_res(screen, d3d12_res, d3d12_permanently_resident);
}
init_valid_range(res);
threaded_resource_init(&res->base.b, false);
convert_planar_resource(res);
return &res->base.b;
invalid:
if (res->bo)
d3d12_bo_unreference(res->bo);
else if (d3d12_res)
d3d12_res->Release();
FREE(res);
return NULL;
}
static bool
d3d12_resource_get_handle(struct pipe_screen *pscreen,
struct pipe_context *pcontext,
struct pipe_resource *pres,
struct winsys_handle *handle,
unsigned usage)
{
struct d3d12_resource *res = d3d12_resource(pres);
struct d3d12_screen *screen = d3d12_screen(pscreen);
switch (handle->type) {
case WINSYS_HANDLE_TYPE_D3D12_RES:
handle->com_obj = d3d12_resource_resource(res);
return true;
case WINSYS_HANDLE_TYPE_FD: {
HANDLE d3d_handle = nullptr;
screen->dev->CreateSharedHandle(d3d12_resource_resource(res),
nullptr,
GENERIC_ALL,
nullptr,
&d3d_handle);
if (!d3d_handle)
return false;
#ifdef _WIN32
handle->handle = d3d_handle;
#else
handle->handle = (int)(intptr_t)d3d_handle;
#endif
handle->format = pres->format;
handle->modifier = ~0ull;
return true;
}
default:
return false;
}
}
struct pipe_resource *
d3d12_resource_from_resource(struct pipe_screen *pscreen,
ID3D12Resource* input_res)
{
D3D12_RESOURCE_DESC input_desc = GetDesc(input_res);
struct winsys_handle handle;
memset(&handle, 0, sizeof(handle));
handle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
handle.format = d3d12_get_pipe_format(input_desc.Format);
handle.com_obj = input_res;
input_res->AddRef();
struct pipe_resource templ;
memset(&templ, 0, sizeof(templ));
if(input_desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) {
templ.target = PIPE_BUFFER;
} else {
templ.target = (input_desc.DepthOrArraySize > 1) ? PIPE_TEXTURE_2D_ARRAY : PIPE_TEXTURE_2D;
}
templ.format = d3d12_get_pipe_format(input_desc.Format);
templ.width0 = input_desc.Width;
templ.height0 = input_desc.Height;
templ.depth0 = input_desc.DepthOrArraySize;
templ.array_size = input_desc.DepthOrArraySize;
templ.flags = 0;
return d3d12_resource_from_handle(
pscreen,
&templ,
&handle,
PIPE_USAGE_DEFAULT
);
}
/**
* On Map/Unmap operations, we readback or flush all the underlying planes
* of planar resources. The map/unmap operation from the caller is
* expected to be done for res->plane_slice plane only, but some
* callers expect adjacent allocations for next contiguous plane access
*
* In this function, we take the res and box the caller passed, and the plane_* properties
* that are currently being readback/flushed, and adjust the d3d12_transfer ptrans
* accordingly for the GPU copy operation between planes.
*/
static void d3d12_adjust_transfer_dimensions_for_plane(const struct d3d12_resource *res,
unsigned plane_slice,
unsigned plane_stride,
unsigned plane_layer_stride,
unsigned plane_offset,
const struct pipe_box* original_box,
struct pipe_transfer *ptrans/*inout*/)
{
/* Adjust strides, offsets to the corresponding plane*/
ptrans->stride = plane_stride;
ptrans->layer_stride = plane_layer_stride;
ptrans->offset = plane_offset;
/* Find multipliers such that:*/
/* first_plane.width = width_multiplier * planes[res->plane_slice].width*/
/* first_plane.height = height_multiplier * planes[res->plane_slice].height*/
float width_multiplier = res->first_plane->width0 / (float) util_format_get_plane_width(res->overall_format, res->plane_slice, res->first_plane->width0);
float height_multiplier = res->first_plane->height0 / (float) util_format_get_plane_height(res->overall_format, res->plane_slice, res->first_plane->height0);
/* Normalize box back to overall dimensions (first plane)*/
ptrans->box.width = width_multiplier * original_box->width;
ptrans->box.height = height_multiplier * original_box->height;
ptrans->box.x = width_multiplier * original_box->x;
ptrans->box.y = height_multiplier * original_box->y;
/* Now adjust dimensions to plane_slice*/
ptrans->box.width = util_format_get_plane_width(res->overall_format, plane_slice, ptrans->box.width);
ptrans->box.height = util_format_get_plane_height(res->overall_format, plane_slice, ptrans->box.height);
ptrans->box.x = util_format_get_plane_width(res->overall_format, plane_slice, ptrans->box.x);
ptrans->box.y = util_format_get_plane_height(res->overall_format, plane_slice, ptrans->box.y);
}
static
void d3d12_resource_get_planes_info(pipe_resource *pres,
unsigned num_planes,
pipe_resource **planes,
unsigned *strides,
unsigned *layer_strides,
unsigned *offsets,
unsigned *staging_res_size)
{
struct d3d12_resource* res = d3d12_resource(pres);
*staging_res_size = 0;
struct pipe_resource *cur_plane_resource = res->first_plane;
for (uint plane_slice = 0; plane_slice < num_planes; ++plane_slice) {
planes[plane_slice] = cur_plane_resource;
int width = util_format_get_plane_width(res->base.b.format, plane_slice, res->first_plane->width0);
int height = util_format_get_plane_height(res->base.b.format, plane_slice, res->first_plane->height0);
strides[plane_slice] = align(util_format_get_stride(cur_plane_resource->format, width),
D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
layer_strides[plane_slice] = align(util_format_get_2d_size(cur_plane_resource->format,
strides[plane_slice],
height),
D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
offsets[plane_slice] = *staging_res_size;
*staging_res_size += layer_strides[plane_slice];
cur_plane_resource = cur_plane_resource->next;
}
}
static constexpr unsigned d3d12_max_planes = 3;
/**
* Get stride and offset for the given pipe resource without the need to get
* a winsys_handle.
*/
void
d3d12_resource_get_info(struct pipe_screen *pscreen,
struct pipe_resource *pres,
unsigned *stride,
unsigned *offset)
{
struct d3d12_resource* res = d3d12_resource(pres);
unsigned num_planes = util_format_get_num_planes(res->overall_format);
pipe_resource *planes[d3d12_max_planes];
unsigned int strides[d3d12_max_planes];
unsigned int layer_strides[d3d12_max_planes];
unsigned int offsets[d3d12_max_planes];
unsigned staging_res_size = 0;
d3d12_resource_get_planes_info(
pres,
num_planes,
planes,
strides,
layer_strides,
offsets,
&staging_res_size
);
if(stride) {
*stride = strides[res->plane_slice];
}
if(offset) {
*offset = offsets[res->plane_slice];
}
}
static struct pipe_memory_object *
d3d12_memobj_create_from_handle(struct pipe_screen *pscreen, struct winsys_handle *handle, bool dedicated)
{
if (handle->type != WINSYS_HANDLE_TYPE_WIN32_HANDLE &&
handle->type != WINSYS_HANDLE_TYPE_WIN32_NAME) {
debug_printf("d3d12: Unsupported memobj handle type\n");
return NULL;
}
struct d3d12_screen *screen = d3d12_screen(pscreen);
#ifdef _GAMING_XBOX
IGraphicsUnknown
#else
IUnknown
#endif
*obj;
#ifdef _WIN32
HANDLE d3d_handle = handle->handle;
#else
HANDLE d3d_handle = (HANDLE)(intptr_t)handle->handle;
#endif
#ifdef _WIN32
HANDLE d3d_handle_to_close = nullptr;
if (handle->type == WINSYS_HANDLE_TYPE_WIN32_NAME) {
screen->dev->OpenSharedHandleByName((LPCWSTR) handle->name, GENERIC_ALL, &d3d_handle_to_close);
d3d_handle = d3d_handle_to_close;
}
#endif
screen->dev->OpenSharedHandle(d3d_handle, IID_PPV_ARGS(&obj));
#ifdef _WIN32
if (d3d_handle_to_close) {
CloseHandle(d3d_handle_to_close);
}
#endif
if (!obj) {
debug_printf("d3d12: Failed to open memobj handle as anything\n");
return NULL;
}
struct d3d12_memory_object *memobj = CALLOC_STRUCT(d3d12_memory_object);
if (!memobj) {
obj->Release();
return NULL;
}
memobj->base.dedicated = dedicated;
obj->AddRef();
if (handle->modifier == 1) {
memobj->heap = (ID3D12Heap *) obj;
} else {
memobj->res = (ID3D12Resource *) obj;
}
obj->Release();
if (!memobj->res && !memobj->heap) {
debug_printf("d3d12: Memory object isn't a resource or heap\n");
free(memobj);
return NULL;
}
bool expect_dedicated = memobj->res != nullptr;
if (dedicated != expect_dedicated)
debug_printf("d3d12: Expected dedicated to be %s for imported %s\n",
expect_dedicated ? "true" : "false",
expect_dedicated ? "resource" : "heap");
return &memobj->base;
}
static void
d3d12_memobj_destroy(struct pipe_screen *pscreen, struct pipe_memory_object *pmemobj)
{
struct d3d12_memory_object *memobj = d3d12_memory_object(pmemobj);
if (memobj->res)
memobj->res->Release();
if (memobj->heap)
memobj->heap->Release();
free(memobj);
}
static pipe_resource *
d3d12_resource_from_memobj(struct pipe_screen *pscreen,
const struct pipe_resource *templ,
struct pipe_memory_object *pmemobj,
uint64_t offset)
{
struct d3d12_memory_object *memobj = d3d12_memory_object(pmemobj);
struct winsys_handle whandle = {};
whandle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
whandle.com_obj = memobj->res ? (void *) memobj->res : (void *) memobj->heap;
whandle.offset = offset;
whandle.format = templ->format;
whandle.modifier = memobj->res ? 0 : 1;
// WINSYS_HANDLE_TYPE_D3D12_RES implies taking ownership of the reference
((IUnknown *)whandle.com_obj)->AddRef();
return d3d12_resource_from_handle(pscreen, templ, &whandle, 0);
}
void
d3d12_screen_resource_init(struct pipe_screen *pscreen)
{
pscreen->resource_create = d3d12_resource_create;
pscreen->resource_from_handle = d3d12_resource_from_handle;
pscreen->resource_get_handle = d3d12_resource_get_handle;
pscreen->resource_destroy = d3d12_resource_destroy;
pscreen->resource_get_info = d3d12_resource_get_info;
pscreen->memobj_create_from_handle = d3d12_memobj_create_from_handle;
pscreen->memobj_destroy = d3d12_memobj_destroy;
pscreen->resource_from_memobj = d3d12_resource_from_memobj;
}
unsigned int
get_subresource_id(struct d3d12_resource *res, unsigned resid,
unsigned z, unsigned base_level)
{
unsigned resource_stride = (res->base.b.last_level + 1) * res->base.b.array_size;
unsigned layer_stride = res->base.b.last_level + 1;
return resid * resource_stride + z * layer_stride +
base_level + res->plane_slice * resource_stride;
}
static D3D12_TEXTURE_COPY_LOCATION
fill_texture_location(struct d3d12_resource *res,
struct d3d12_transfer *trans, unsigned resid, unsigned z)
{
D3D12_TEXTURE_COPY_LOCATION tex_loc = {0};
int subres = get_subresource_id(res, resid, z, trans->base.b.level);
tex_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
tex_loc.SubresourceIndex = subres;
tex_loc.pResource = d3d12_resource_resource(res);
return tex_loc;
}
static D3D12_TEXTURE_COPY_LOCATION
fill_buffer_location(struct d3d12_context *ctx,
struct d3d12_resource *res,
struct d3d12_resource *staging_res,
struct d3d12_transfer *trans,
unsigned depth,
unsigned resid, unsigned z)
{
D3D12_TEXTURE_COPY_LOCATION buf_loc = {0};
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
uint64_t offset = 0;
auto descr = GetDesc(d3d12_resource_underlying(res, &offset));
descr.Flags &= ~D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
struct d3d12_screen *screen = d3d12_screen(ctx->base.screen);
ID3D12Device* dev = screen->dev;
unsigned sub_resid = get_subresource_id(res, resid, z, trans->base.b.level);
dev->GetCopyableFootprints(&descr, sub_resid, 1, 0, &footprint, nullptr, nullptr, nullptr);
buf_loc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
buf_loc.pResource = d3d12_resource_underlying(staging_res, &offset);
buf_loc.PlacedFootprint = footprint;
buf_loc.PlacedFootprint.Offset = offset;
buf_loc.PlacedFootprint.Offset += trans->base.b.offset;
if (util_format_has_depth(util_format_description(res->base.b.format)) &&
screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED) {
buf_loc.PlacedFootprint.Footprint.Width = res->base.b.width0;
buf_loc.PlacedFootprint.Footprint.Height = res->base.b.height0;
buf_loc.PlacedFootprint.Footprint.Depth = res->base.b.depth0;
} else {
buf_loc.PlacedFootprint.Footprint.Width = ALIGN(trans->base.b.box.width,
util_format_get_blockwidth(res->base.b.format));
buf_loc.PlacedFootprint.Footprint.Height = ALIGN(trans->base.b.box.height,
util_format_get_blockheight(res->base.b.format));
buf_loc.PlacedFootprint.Footprint.Depth = ALIGN(depth,
util_format_get_blockdepth(res->base.b.format));
}
buf_loc.PlacedFootprint.Footprint.RowPitch = trans->base.b.stride;
return buf_loc;
}
struct copy_info {
struct d3d12_resource *dst;
D3D12_TEXTURE_COPY_LOCATION dst_loc;
UINT dst_x, dst_y, dst_z;
struct d3d12_resource *src;
D3D12_TEXTURE_COPY_LOCATION src_loc;
D3D12_BOX *src_box;
};
static void
copy_texture_region(struct d3d12_context *ctx,
struct copy_info& info)
{
auto batch = d3d12_current_batch(ctx);
d3d12_batch_reference_resource(batch, info.src, false);
d3d12_batch_reference_resource(batch, info.dst, true);
d3d12_transition_resource_state(ctx, info.src, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
d3d12_transition_resource_state(ctx, info.dst, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
d3d12_apply_resource_states(ctx, false);
ctx->cmdlist->CopyTextureRegion(&info.dst_loc, info.dst_x, info.dst_y, info.dst_z,
&info.src_loc, info.src_box);
}
static void
transfer_buf_to_image_part(struct d3d12_context *ctx,
struct d3d12_resource *res,
struct d3d12_resource *staging_res,
struct d3d12_transfer *trans,
int z, int depth, int start_z, int dest_z,
int resid)
{
if (D3D12_DEBUG_RESOURCE & d3d12_debug) {
debug_printf("D3D12: Copy %dx%dx%d + %dx%dx%d from buffer %s to image %s\n",
trans->base.b.box.x, trans->base.b.box.y, trans->base.b.box.z,
trans->base.b.box.width, trans->base.b.box.height, trans->base.b.box.depth,
util_format_name(staging_res->base.b.format),
util_format_name(res->base.b.format));
}
struct d3d12_screen *screen = d3d12_screen(res->base.b.screen);
struct copy_info copy_info;
copy_info.src = staging_res;
copy_info.src_loc = fill_buffer_location(ctx, res, staging_res, trans, depth, resid, z);
copy_info.src_loc.PlacedFootprint.Offset += (z - start_z) * trans->base.b.layer_stride;
copy_info.src_box = nullptr;
copy_info.dst = res;
copy_info.dst_loc = fill_texture_location(res, trans, resid, z);
if (util_format_has_depth(util_format_description(res->base.b.format)) &&
screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED) {
copy_info.dst_x = 0;
copy_info.dst_y = 0;
} else {
copy_info.dst_x = trans->base.b.box.x;
copy_info.dst_y = trans->base.b.box.y;
}
copy_info.dst_z = res->base.b.target == PIPE_TEXTURE_CUBE ? 0 : dest_z;
copy_info.src_box = nullptr;
copy_texture_region(ctx, copy_info);
}
static bool
transfer_buf_to_image(struct d3d12_context *ctx,
struct d3d12_resource *res,
struct d3d12_resource *staging_res,
struct d3d12_transfer *trans, int resid)
{
if (res->base.b.target == PIPE_TEXTURE_3D) {
assert(resid == 0);
transfer_buf_to_image_part(ctx, res, staging_res, trans,
0, trans->base.b.box.depth, 0,
trans->base.b.box.z, 0);
} else {
int num_layers = trans->base.b.box.depth;
int start_z = trans->base.b.box.z;
for (int z = start_z; z < start_z + num_layers; ++z) {
transfer_buf_to_image_part(ctx, res, staging_res, trans,
z, 1, start_z, 0, resid);
}
}
return true;
}
static void
transfer_image_part_to_buf(struct d3d12_context *ctx,
struct d3d12_resource *res,
struct d3d12_resource *staging_res,
struct d3d12_transfer *trans,
unsigned resid, int z, int start_layer,
int start_box_z, int depth)
{
struct pipe_box *box = &trans->base.b.box;
D3D12_BOX src_box = {};
struct d3d12_screen *screen = d3d12_screen(res->base.b.screen);
struct copy_info copy_info;
copy_info.src_box = nullptr;
copy_info.src = res;
copy_info.src_loc = fill_texture_location(res, trans, resid, z);
copy_info.dst = staging_res;
copy_info.dst_loc = fill_buffer_location(ctx, res, staging_res, trans,
depth, resid, z);
copy_info.dst_loc.PlacedFootprint.Offset += (z - start_layer) * trans->base.b.layer_stride;
copy_info.dst_x = copy_info.dst_y = copy_info.dst_z = 0;
bool whole_resource = util_texrange_covers_whole_level(&res->base.b, trans->base.b.level,
box->x, box->y, start_box_z,
box->width, box->height, depth);
if (util_format_has_depth(util_format_description(res->base.b.format)) &&
screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED)
whole_resource = true;
if (!whole_resource) {
src_box.left = box->x;
src_box.right = box->x + box->width;
src_box.top = box->y;
src_box.bottom = box->y + box->height;
src_box.front = start_box_z;
src_box.back = start_box_z + depth;
copy_info.src_box = &src_box;
}
copy_texture_region(ctx, copy_info);
}
static bool
transfer_image_to_buf(struct d3d12_context *ctx,
struct d3d12_resource *res,
struct d3d12_resource *staging_res,
struct d3d12_transfer *trans,
unsigned resid)
{
/* We only suppport loading from either an texture array
* or a ZS texture, so either resid is zero, or num_layers == 1)
*/
assert(resid == 0 || trans->base.b.box.depth == 1);
if (D3D12_DEBUG_RESOURCE & d3d12_debug) {
debug_printf("D3D12: Copy %dx%dx%d + %dx%dx%d from %s@%d to %s\n",
trans->base.b.box.x, trans->base.b.box.y, trans->base.b.box.z,
trans->base.b.box.width, trans->base.b.box.height, trans->base.b.box.depth,
util_format_name(res->base.b.format), resid,
util_format_name(staging_res->base.b.format));
}
struct pipe_resource *resolved_resource = nullptr;
#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
if (res->base.b.nr_samples > 1) {
struct pipe_resource tmpl = res->base.b;
tmpl.nr_samples = 0;
resolved_resource = d3d12_resource_create(ctx->base.screen, &tmpl);
struct pipe_blit_info resolve_info = {};
struct pipe_box box;
u_box_3d(0,0,0, (int)res->base.b.width0, (int16_t)res->base.b.height0, (int16_t)res->base.b.depth0, &box);
resolve_info.dst.resource = resolved_resource;
resolve_info.dst.box = box;
resolve_info.dst.format = res->base.b.format;
resolve_info.src.resource = &res->base.b;
resolve_info.src.box = box;
resolve_info.src.format = res->base.b.format;
resolve_info.filter = PIPE_TEX_FILTER_NEAREST;
resolve_info.mask = util_format_get_mask(tmpl.format);
d3d12_blit(&ctx->base, &resolve_info);
res = (struct d3d12_resource *)resolved_resource;
}
#endif // HAVE_GALLIUM_D3D12_GRAPHICS
if (res->base.b.target == PIPE_TEXTURE_3D) {
transfer_image_part_to_buf(ctx, res, staging_res, trans, resid,
0, 0, trans->base.b.box.z, trans->base.b.box.depth);
} else {
int start_layer = trans->base.b.box.z;
for (int z = start_layer; z < start_layer + trans->base.b.box.depth; ++z) {
transfer_image_part_to_buf(ctx, res, staging_res, trans, resid,
z, start_layer, 0, 1);
}
}
pipe_resource_reference(&resolved_resource, NULL);
return true;
}
static void
transfer_buf_to_buf(struct d3d12_context *ctx,
struct d3d12_resource *src,
struct d3d12_resource *dst,
uint64_t src_offset,
uint64_t dst_offset,
uint64_t width)
{
auto batch = d3d12_current_batch(ctx);
d3d12_batch_reference_resource(batch, src, false);
d3d12_batch_reference_resource(batch, dst, true);
uint64_t src_offset_suballoc = 0;
uint64_t dst_offset_suballoc = 0;
auto src_d3d12 = d3d12_resource_underlying(src, &src_offset_suballoc);
auto dst_d3d12 = d3d12_resource_underlying(dst, &dst_offset_suballoc);
src_offset += src_offset_suballoc;
dst_offset += dst_offset_suballoc;
// Same-resource copies not supported, since the resource would need to be in both states
assert(src_d3d12 != dst_d3d12);
d3d12_transition_resource_state(ctx, src, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
d3d12_transition_resource_state(ctx, dst, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_TRANSITION_FLAG_INVALIDATE_BINDINGS);
d3d12_apply_resource_states(ctx, false);
ctx->cmdlist->CopyBufferRegion(dst_d3d12, dst_offset,
src_d3d12, src_offset,
width);
}
static unsigned
linear_offset(int x, int y, int z, unsigned stride, unsigned layer_stride)
{
return x +
y * stride +
z * layer_stride;
}
static D3D12_RANGE
linear_range(const struct pipe_box *box, unsigned stride, unsigned layer_stride)
{
D3D12_RANGE range;
range.Begin = linear_offset(box->x, box->y, box->z,
stride, layer_stride);
range.End = linear_offset(box->x + box->width,
box->y + box->height - 1,
box->z + box->depth - 1,
stride, layer_stride);
return range;
}
static bool
synchronize(struct d3d12_context *ctx,
struct d3d12_resource *res,
unsigned usage,
D3D12_RANGE *range)
{
assert(can_map_directly(&res->base.b));
/* Check whether that range contains valid data; if not, we might not need to sync */
if (!(usage & PIPE_MAP_UNSYNCHRONIZED) &&
usage & PIPE_MAP_WRITE &&
!util_ranges_intersect(&res->valid_buffer_range, range->Begin, range->End)) {
usage |= PIPE_MAP_UNSYNCHRONIZED;
}
if (!(usage & PIPE_MAP_UNSYNCHRONIZED) && resource_is_busy(ctx, res, usage & PIPE_MAP_WRITE)) {
if (usage & PIPE_MAP_DONTBLOCK) {
if (d3d12_batch_has_references(d3d12_current_batch(ctx), res->bo, usage & PIPE_MAP_WRITE))
d3d12_flush_cmdlist(ctx);
return false;
}
d3d12_resource_wait_idle(ctx, res, usage & PIPE_MAP_WRITE);
}
if (usage & PIPE_MAP_WRITE)
util_range_add(&res->base.b, &res->valid_buffer_range,
range->Begin, range->End);
return true;
}
/* A wrapper to make sure local resources are freed and unmapped with
* any exit path */
struct local_resource {
local_resource(pipe_screen *s, struct pipe_resource *tmpl) :
mapped(false)
{
res = d3d12_resource(d3d12_resource_create(s, tmpl));
}
~local_resource() {
if (res) {
if (mapped)
d3d12_bo_unmap(res->bo, nullptr);
pipe_resource_reference((struct pipe_resource **)&res, NULL);
}
}
void *
map() {
void *ptr;
ptr = d3d12_bo_map(res->bo, nullptr);
if (ptr)
mapped = true;
return ptr;
}
void unmap()
{
if (mapped)
d3d12_bo_unmap(res->bo, nullptr);
mapped = false;
}
operator struct d3d12_resource *() {
return res;
}
bool operator !() {
return !res;
}
private:
struct d3d12_resource *res;
bool mapped;
};
/* Combined depth-stencil needs a special handling for reading back: DX handled
* depth and stencil parts as separate resources and handles copying them only
* by using seperate texture copy calls with different formats. So create two
* buffers, read back both resources and interleave the data.
*/
static void
prepare_zs_layer_strides(struct d3d12_screen *screen,
struct d3d12_resource *res,
const struct pipe_box *box,
struct d3d12_transfer *trans)
{
bool copy_whole_resource = screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED;
int width = copy_whole_resource ? res->base.b.width0 : box->width;
int height = copy_whole_resource ? res->base.b.height0 : box->height;
trans->base.b.stride = align(util_format_get_stride(res->base.b.format, width),
D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
trans->base.b.layer_stride = util_format_get_2d_size(res->base.b.format,
trans->base.b.stride,
height);
if (copy_whole_resource) {
trans->zs_cpu_copy_stride = align(util_format_get_stride(res->base.b.format, box->width),
D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
trans->zs_cpu_copy_layer_stride = util_format_get_2d_size(res->base.b.format,
trans->base.b.stride,
box->height);
} else {
trans->zs_cpu_copy_stride = trans->base.b.stride;
trans->zs_cpu_copy_layer_stride = trans->base.b.layer_stride;
}
}
static void *
read_zs_surface(struct d3d12_context *ctx, struct d3d12_resource *res,
const struct pipe_box *box,
struct d3d12_transfer *trans)
{
pipe_screen *pscreen = ctx->base.screen;
struct d3d12_screen *screen = d3d12_screen(pscreen);
prepare_zs_layer_strides(screen, res, box, trans);
struct pipe_resource tmpl;
memset(&tmpl, 0, sizeof tmpl);
tmpl.target = PIPE_BUFFER;
tmpl.format = PIPE_FORMAT_R32_UNORM;
tmpl.bind = 0;
tmpl.usage = PIPE_USAGE_STAGING;
tmpl.flags = 0;
tmpl.width0 = trans->base.b.layer_stride;
tmpl.height0 = 1;
tmpl.depth0 = 1;
tmpl.array_size = 1;
local_resource depth_buffer(pscreen, &tmpl);
if (!depth_buffer) {
debug_printf("Allocating staging buffer for depth failed\n");
return NULL;
}
if (!transfer_image_to_buf(ctx, res, depth_buffer, trans, 0))
return NULL;
tmpl.format = PIPE_FORMAT_R8_UINT;
local_resource stencil_buffer(pscreen, &tmpl);
if (!stencil_buffer) {
debug_printf("Allocating staging buffer for stencilfailed\n");
return NULL;
}
if (!transfer_image_to_buf(ctx, res, stencil_buffer, trans, 1))
return NULL;
d3d12_flush_cmdlist_and_wait(ctx);
uint8_t *depth_ptr = (uint8_t *)depth_buffer.map();
if (!depth_ptr) {
debug_printf("Mapping staging depth buffer failed\n");
return NULL;
}
uint8_t *stencil_ptr = (uint8_t *)stencil_buffer.map();
if (!stencil_ptr) {
debug_printf("Mapping staging stencil buffer failed\n");
return NULL;
}
uint8_t *buf = (uint8_t *)malloc(trans->zs_cpu_copy_layer_stride);
if (!buf)
return NULL;
trans->data = buf;
switch (res->base.b.format) {
case PIPE_FORMAT_Z24_UNORM_S8_UINT:
if (screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED) {
depth_ptr += trans->base.b.box.y * trans->base.b.stride + trans->base.b.box.x * 4;
stencil_ptr += trans->base.b.box.y * trans->base.b.stride + trans->base.b.box.x * 4;
}
util_format_z24_unorm_s8_uint_pack_separate(buf, trans->zs_cpu_copy_stride,
(uint32_t *)depth_ptr, trans->base.b.stride,
stencil_ptr, trans->base.b.stride,
trans->base.b.box.width, trans->base.b.box.height);
break;
case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
if (screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED) {
depth_ptr += trans->base.b.box.y * trans->base.b.stride + trans->base.b.box.x * 4;
stencil_ptr += trans->base.b.box.y * trans->base.b.stride + trans->base.b.box.x;
}
util_format_z32_float_s8x24_uint_pack_z_float(buf, trans->zs_cpu_copy_stride,
(float *)depth_ptr, trans->base.b.stride,
trans->base.b.box.width, trans->base.b.box.height);
util_format_z32_float_s8x24_uint_pack_s_8uint(buf, trans->zs_cpu_copy_stride,
stencil_ptr, trans->base.b.stride,
trans->base.b.box.width, trans->base.b.box.height);
break;
default:
unreachable("Unsupported depth steancil format");
};
return trans->data;
}
static void *
prepare_write_zs_surface(struct d3d12_resource *res,
const struct pipe_box *box,
struct d3d12_transfer *trans)
{
struct d3d12_screen *screen = d3d12_screen(res->base.b.screen);
prepare_zs_layer_strides(screen, res, box, trans);
uint32_t *buf = (uint32_t *)malloc(trans->base.b.layer_stride);
if (!buf)
return NULL;
trans->data = buf;
return trans->data;
}
static void
write_zs_surface(struct pipe_context *pctx, struct d3d12_resource *res,
struct d3d12_transfer *trans)
{
struct d3d12_screen *screen = d3d12_screen(res->base.b.screen);
struct pipe_resource tmpl;
memset(&tmpl, 0, sizeof tmpl);
tmpl.target = PIPE_BUFFER;
tmpl.format = PIPE_FORMAT_R32_UNORM;
tmpl.bind = 0;
tmpl.usage = PIPE_USAGE_STAGING;
tmpl.flags = 0;
tmpl.width0 = trans->base.b.layer_stride;
tmpl.height0 = 1;
tmpl.depth0 = 1;
tmpl.array_size = 1;
local_resource depth_buffer(pctx->screen, &tmpl);
if (!depth_buffer) {
debug_printf("Allocating staging buffer for depth failed\n");
return;
}
local_resource stencil_buffer(pctx->screen, &tmpl);
if (!stencil_buffer) {
debug_printf("Allocating staging buffer for depth failed\n");
return;
}
uint8_t *depth_ptr = (uint8_t *)depth_buffer.map();
if (!depth_ptr) {
debug_printf("Mapping staging depth buffer failed\n");
return;
}
uint8_t *stencil_ptr = (uint8_t *)stencil_buffer.map();
if (!stencil_ptr) {
debug_printf("Mapping staging stencil buffer failed\n");
return;
}
switch (res->base.b.format) {
case PIPE_FORMAT_Z24_UNORM_S8_UINT:
if (screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED) {
depth_ptr += trans->base.b.box.y * trans->base.b.stride + trans->base.b.box.x * 4;
stencil_ptr += trans->base.b.box.y * trans->base.b.stride + trans->base.b.box.x * 4;
}
util_format_z32_unorm_unpack_z_32unorm((uint32_t *)depth_ptr, trans->base.b.stride, (uint8_t*)trans->data,
trans->zs_cpu_copy_stride, trans->base.b.box.width,
trans->base.b.box.height);
util_format_z24_unorm_s8_uint_unpack_s_8uint(stencil_ptr, trans->base.b.stride, (uint8_t*)trans->data,
trans->zs_cpu_copy_stride, trans->base.b.box.width,
trans->base.b.box.height);
break;
case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
if (screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED) {
depth_ptr += trans->base.b.box.y * trans->base.b.stride + trans->base.b.box.x * 4;
stencil_ptr += trans->base.b.box.y * trans->base.b.stride + trans->base.b.box.x;
}
util_format_z32_float_s8x24_uint_unpack_z_float((float *)depth_ptr, trans->base.b.stride, (uint8_t*)trans->data,
trans->zs_cpu_copy_stride, trans->base.b.box.width,
trans->base.b.box.height);
util_format_z32_float_s8x24_uint_unpack_s_8uint(stencil_ptr, trans->base.b.stride, (uint8_t*)trans->data,
trans->zs_cpu_copy_stride, trans->base.b.box.width,
trans->base.b.box.height);
break;
default:
unreachable("Unsupported depth steancil format");
};
stencil_buffer.unmap();
depth_buffer.unmap();
transfer_buf_to_image(d3d12_context(pctx), res, depth_buffer, trans, 0);
transfer_buf_to_image(d3d12_context(pctx), res, stencil_buffer, trans, 1);
}
#define BUFFER_MAP_ALIGNMENT 64
static void *
d3d12_transfer_map(struct pipe_context *pctx,
struct pipe_resource *pres,
unsigned level,
unsigned usage,
const struct pipe_box *box,
struct pipe_transfer **transfer)
{
struct d3d12_context *ctx = d3d12_context(pctx);
struct d3d12_resource *res = d3d12_resource(pres);
struct d3d12_screen *screen = d3d12_screen(pres->screen);
if (usage & PIPE_MAP_DIRECTLY || !res->bo)
return NULL;
slab_child_pool* transfer_pool = (usage & TC_TRANSFER_MAP_THREADED_UNSYNC) ?
&ctx->transfer_pool_unsync : &ctx->transfer_pool;
struct d3d12_transfer *trans = (struct d3d12_transfer *)slab_zalloc(transfer_pool);
struct pipe_transfer *ptrans = &trans->base.b;
if (!trans)
return NULL;
ptrans->level = level;
ptrans->usage = (enum pipe_map_flags)usage;
ptrans->box = *box;
D3D12_RANGE range;
range.Begin = 0;
void *ptr;
if (can_map_directly(&res->base.b)) {
if (pres->target == PIPE_BUFFER) {
ptrans->stride = 0;
ptrans->layer_stride = 0;
} else {
ptrans->stride = util_format_get_stride(pres->format, box->width);
ptrans->layer_stride = util_format_get_2d_size(pres->format,
ptrans->stride,
box->height);
}
range = linear_range(box, ptrans->stride, ptrans->layer_stride);
if (!synchronize(ctx, res, usage, &range)) {
slab_free(transfer_pool, trans);
return NULL;
}
ptr = d3d12_bo_map(res->bo, &range);
} else if (unlikely(pres->format == PIPE_FORMAT_Z24_UNORM_S8_UINT ||
pres->format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT)) {
if (usage & PIPE_MAP_READ) {
ptr = read_zs_surface(ctx, res, box, trans);
} else if (usage & PIPE_MAP_WRITE){
ptr = prepare_write_zs_surface(res, box, trans);
} else {
ptr = nullptr;
}
} else if(util_format_is_yuv(res->overall_format)) {
/* Get planes information*/
unsigned num_planes = util_format_get_num_planes(res->overall_format);
pipe_resource *planes[d3d12_max_planes];
unsigned int strides[d3d12_max_planes];
unsigned int layer_strides[d3d12_max_planes];
unsigned int offsets[d3d12_max_planes];
unsigned staging_res_size = 0;
d3d12_resource_get_planes_info(
pres,
num_planes,
planes,
strides,
layer_strides,
offsets,
&staging_res_size
);
/* Allocate a buffer for all the planes to fit in adjacent memory*/
pipe_resource_usage staging_usage = (usage & (PIPE_MAP_READ | PIPE_MAP_READ_WRITE)) ?
PIPE_USAGE_STAGING : PIPE_USAGE_STREAM;
trans->staging_res = pipe_buffer_create(pctx->screen, 0,
staging_usage,
staging_res_size);
if (!trans->staging_res)
return NULL;
struct d3d12_resource *staging_res = d3d12_resource(trans->staging_res);
/* Readback contents into the buffer allocation now if map was intended for read*/
/* Read all planes if readback needed*/
if (usage & PIPE_MAP_READ) {
pipe_box original_box = ptrans->box;
for (uint plane_slice = 0; plane_slice < num_planes; ++plane_slice) {
/* Adjust strides, offsets, box to the corresponding plane for the copytexture operation*/
d3d12_adjust_transfer_dimensions_for_plane(res,
plane_slice,
strides[plane_slice],
layer_strides[plane_slice],
offsets[plane_slice],
&original_box,
ptrans/*inout*/);
/* Perform the readback*/
if(!transfer_image_to_buf(ctx, d3d12_resource(planes[plane_slice]), staging_res, trans, 0)){
return NULL;
}
}
ptrans->box = original_box;
d3d12_flush_cmdlist_and_wait(ctx);
}
/* Map the whole staging buffer containing all the planes contiguously*/
/* Just offset the resulting ptr to the according plane offset*/
range.End = staging_res_size - range.Begin;
uint8_t* all_planes_map = (uint8_t*) d3d12_bo_map(staging_res->bo, &range);
ptrans->stride = strides[res->plane_slice];
ptrans->layer_stride = layer_strides[res->plane_slice];
ptr = all_planes_map + offsets[res->plane_slice];
} else {
ptrans->stride = align(util_format_get_stride(pres->format, box->width),
D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
ptrans->layer_stride = util_format_get_2d_size(pres->format,
ptrans->stride,
box->height);
if (res->base.b.target != PIPE_TEXTURE_3D)
ptrans->layer_stride = align(ptrans->layer_stride,
D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
if (util_format_has_depth(util_format_description(pres->format)) &&
screen->opts2.ProgrammableSamplePositionsTier == D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED) {
trans->zs_cpu_copy_stride = ptrans->stride;
trans->zs_cpu_copy_layer_stride = ptrans->layer_stride;
ptrans->stride = align(util_format_get_stride(pres->format, pres->width0),
D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
ptrans->layer_stride = util_format_get_2d_size(pres->format,
ptrans->stride,
pres->height0);
range.Begin = box->y * ptrans->stride +
box->x * util_format_get_blocksize(pres->format);
}
unsigned staging_res_size = ptrans->layer_stride * box->depth;
if (res->base.b.target == PIPE_BUFFER) {
/* To properly support ARB_map_buffer_alignment, we need to return a pointer
* that's appropriately offset from a 64-byte-aligned base address.
*/
assert(box->x >= 0);
unsigned aligned_x = (unsigned)box->x % BUFFER_MAP_ALIGNMENT;
staging_res_size = align(box->width + aligned_x,
D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
range.Begin = aligned_x;
}
pipe_resource_usage staging_usage = (usage & (PIPE_MAP_DISCARD_RANGE | PIPE_MAP_DISCARD_WHOLE_RESOURCE)) ?
PIPE_USAGE_STREAM : PIPE_USAGE_STAGING;
trans->staging_res = pipe_buffer_create(pctx->screen, 0,
staging_usage,
staging_res_size);
if (!trans->staging_res) {
slab_free(transfer_pool, trans);
return NULL;
}
struct d3d12_resource *staging_res = d3d12_resource(trans->staging_res);
if ((usage & (PIPE_MAP_DISCARD_RANGE | PIPE_MAP_DISCARD_WHOLE_RESOURCE | TC_TRANSFER_MAP_THREADED_UNSYNC)) == 0) {
bool ret = true;
if (pres->target == PIPE_BUFFER) {
uint64_t src_offset = box->x;
uint64_t dst_offset = src_offset % BUFFER_MAP_ALIGNMENT;
transfer_buf_to_buf(ctx, res, staging_res, src_offset, dst_offset, box->width);
} else
ret = transfer_image_to_buf(ctx, res, staging_res, trans, 0);
if (!ret)
return NULL;
d3d12_flush_cmdlist_and_wait(ctx);
}
range.End = staging_res_size - range.Begin;
ptr = d3d12_bo_map(staging_res->bo, &range);
}
pipe_resource_reference(&ptrans->resource, pres);
*transfer = ptrans;
return ptr;
}
static void
d3d12_transfer_unmap(struct pipe_context *pctx,
struct pipe_transfer *ptrans)
{
struct d3d12_context *ctx = d3d12_context(pctx);
struct d3d12_resource *res = d3d12_resource(ptrans->resource);
struct d3d12_transfer *trans = (struct d3d12_transfer *)ptrans;
D3D12_RANGE range = { 0, 0 };
if (trans->data != nullptr) {
if (trans->base.b.usage & PIPE_MAP_WRITE)
write_zs_surface(pctx, res, trans);
free(trans->data);
} else if (trans->staging_res) {
if(util_format_is_yuv(res->overall_format)) {
/* Get planes information*/
unsigned num_planes = util_format_get_num_planes(res->overall_format);
pipe_resource *planes[d3d12_max_planes];
unsigned int strides[d3d12_max_planes];
unsigned int layer_strides[d3d12_max_planes];
unsigned int offsets[d3d12_max_planes];
unsigned staging_res_size = 0;
d3d12_resource_get_planes_info(
ptrans->resource,
num_planes,
planes,
strides,
layer_strides,
offsets,
&staging_res_size
);
/* Flush the changed contents into the GPU texture*/
/* In theory we should just flush only the contents for the plane*/
/* requested in res->plane_slice, but the VAAPI frontend has this*/
/* behaviour in which they assume that mapping the first plane of*/
/* NV12, P010, etc resources will will give them a buffer containing*/
/* both Y and UV planes contigously in vaDeriveImage and then vaMapBuffer*/
/* so, flush them all*/
struct d3d12_resource *staging_res = d3d12_resource(trans->staging_res);
if (trans->base.b.usage & PIPE_MAP_WRITE) {
assert(ptrans->box.x >= 0);
range.Begin = res->base.b.target == PIPE_BUFFER ?
(unsigned)ptrans->box.x % BUFFER_MAP_ALIGNMENT : 0;
range.End = staging_res->base.b.width0 - range.Begin;
d3d12_bo_unmap(staging_res->bo, &range);
pipe_box original_box = ptrans->box;
for (uint plane_slice = 0; plane_slice < num_planes; ++plane_slice) {
/* Adjust strides, offsets to the corresponding plane for the copytexture operation*/
d3d12_adjust_transfer_dimensions_for_plane(res,
plane_slice,
strides[plane_slice],
layer_strides[plane_slice],
offsets[plane_slice],
&original_box,
ptrans/*inout*/);
transfer_buf_to_image(ctx, d3d12_resource(planes[plane_slice]), staging_res, trans, 0);
}
ptrans->box = original_box;
}
pipe_resource_reference(&trans->staging_res, NULL);
} else {
struct d3d12_resource *staging_res = d3d12_resource(trans->staging_res);
if (trans->base.b.usage & PIPE_MAP_WRITE) {
assert(ptrans->box.x >= 0);
range.Begin = res->base.b.target == PIPE_BUFFER ?
(unsigned)ptrans->box.x % BUFFER_MAP_ALIGNMENT : 0;
range.End = staging_res->base.b.width0 - range.Begin;
}
d3d12_bo_unmap(staging_res->bo, &range);
if (trans->base.b.usage & PIPE_MAP_WRITE) {
struct d3d12_context *ctx = d3d12_context(pctx);
if (res->base.b.target == PIPE_BUFFER) {
uint64_t dst_offset = trans->base.b.box.x;
uint64_t src_offset = dst_offset % BUFFER_MAP_ALIGNMENT;
transfer_buf_to_buf(ctx, staging_res, res, src_offset, dst_offset, ptrans->box.width);
} else
transfer_buf_to_image(ctx, res, staging_res, trans, 0);
}
pipe_resource_reference(&trans->staging_res, NULL);
}
} else {
if (trans->base.b.usage & PIPE_MAP_WRITE) {
range.Begin = ptrans->box.x;
range.End = ptrans->box.x + ptrans->box.width;
}
d3d12_bo_unmap(res->bo, &range);
}
pipe_resource_reference(&ptrans->resource, NULL);
slab_free(&d3d12_context(pctx)->transfer_pool, ptrans);
}
void
d3d12_context_resource_init(struct pipe_context *pctx)
{
pctx->buffer_map = d3d12_transfer_map;
pctx->buffer_unmap = d3d12_transfer_unmap;
pctx->texture_map = d3d12_transfer_map;
pctx->texture_unmap = d3d12_transfer_unmap;
pctx->transfer_flush_region = u_default_transfer_flush_region;
pctx->buffer_subdata = u_default_buffer_subdata;
pctx->texture_subdata = u_default_texture_subdata;
}