d3d12: introduce d3d12 gallium driver
This driver will allow running OpenGL and OpenCL on top of Gallium for any hardware supporting Microsoft's Direct3D 12 on Windows 10. This is the combination of a lot of commits from our development branch, containing code from several authors. Co-authored-by: Bill Kristiansen <billkris@microsoft.com> Co-authored-by: Gert Wollny <gert.wollny@collabora.com> Co-authored-by: Jesse Natalie <jenatali@microsoft.com> Co-authored-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com> Reviewed-By: Mike Blumenkrantz <michael.blumenkrantz@gmail.com> Acked-by: Jason Ekstrand <jason@jlekstrand.net> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7477>
This commit is contained in:

committed by
Marge Bot

parent
3f31cf64e4
commit
2ea15cd661
999
src/gallium/drivers/d3d12/d3d12_resource.cpp
Normal file
999
src/gallium/drivers/d3d12/d3d12_resource.cpp
Normal file
@@ -0,0 +1,999 @@
|
||||
/*
|
||||
* 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 <d3d12.h>
|
||||
#include <memory>
|
||||
|
||||
static bool
|
||||
can_map_directly(struct pipe_resource *pres)
|
||||
{
|
||||
return pres->bind & (PIPE_BIND_SCANOUT | PIPE_BIND_SHARED | PIPE_BIND_LINEAR) ||
|
||||
pres->target == PIPE_BUFFER;
|
||||
}
|
||||
|
||||
static void
|
||||
init_valid_range(struct d3d12_resource *res)
|
||||
{
|
||||
if (can_map_directly(&res->base))
|
||||
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);
|
||||
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 busy = false;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(ctx->batches); i++)
|
||||
busy |= d3d12_batch_has_references(&ctx->batches[i], res->bo);
|
||||
|
||||
return busy;
|
||||
}
|
||||
|
||||
void
|
||||
d3d12_resource_wait_idle(struct d3d12_context *ctx,
|
||||
struct d3d12_resource *res)
|
||||
{
|
||||
if (d3d12_batch_has_references(d3d12_current_batch(ctx), res->bo)) {
|
||||
d3d12_flush_cmdlist_and_wait(ctx);
|
||||
} else {
|
||||
d3d12_foreach_submitted_batch(ctx, batch) {
|
||||
d3d12_reset_batch(ctx, batch, PIPE_TIMEOUT_INFINITE);
|
||||
if (!resource_is_busy(ctx, res))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
/* Don't use slab buffer manager for GPU writable buffers */
|
||||
bufmgr = templ->bind & PIPE_BIND_STREAM_OUTPUT ? screen->cache_bufmgr
|
||||
: screen->slab_bufmgr;
|
||||
buf_desc.alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
|
||||
buf_desc.usage = (pb_usage_flags)PB_USAGE_ALL;
|
||||
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(buf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
init_texture(struct d3d12_screen *screen,
|
||||
struct d3d12_resource *res,
|
||||
const struct pipe_resource *templ)
|
||||
{
|
||||
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; /* TODO: figure this one out */
|
||||
|
||||
switch (templ->target) {
|
||||
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:
|
||||
desc.DepthOrArraySize *= 6;
|
||||
/* fall-through */
|
||||
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;
|
||||
}
|
||||
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
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.
|
||||
*/
|
||||
}
|
||||
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
if (templ->bind & (PIPE_BIND_SCANOUT |
|
||||
PIPE_BIND_SHARED | PIPE_BIND_LINEAR))
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
|
||||
D3D12_HEAP_TYPE heap_type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
|
||||
if (templ->bind & (PIPE_BIND_DISPLAY_TARGET |
|
||||
PIPE_BIND_SCANOUT |
|
||||
PIPE_BIND_SHARED))
|
||||
heap_type = D3D12_HEAP_TYPE_READBACK;
|
||||
else if (templ->usage == PIPE_USAGE_STAGING)
|
||||
heap_type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heap_pris = screen->dev->GetCustomHeapProperties(0, heap_type);
|
||||
|
||||
HRESULT hres = screen->dev->CreateCommittedResource(&heap_pris,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&desc,
|
||||
D3D12_RESOURCE_STATE_COMMON,
|
||||
NULL,
|
||||
__uuidof(ID3D12Resource),
|
||||
(void **)&d3d12_res);
|
||||
if (FAILED(hres))
|
||||
return false;
|
||||
|
||||
if (screen->winsys && (templ->bind & (PIPE_BIND_DISPLAY_TARGET |
|
||||
PIPE_BIND_SCANOUT |
|
||||
PIPE_BIND_SHARED))) {
|
||||
struct sw_winsys *winsys = screen->winsys;
|
||||
res->dt = winsys->displaytarget_create(screen->winsys,
|
||||
res->base.bind,
|
||||
res->base.format,
|
||||
templ->width0,
|
||||
templ->height0,
|
||||
64, NULL,
|
||||
&res->dt_stride);
|
||||
}
|
||||
|
||||
res->bo = d3d12_bo_wrap_res(d3d12_res, templ->format);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct pipe_resource *
|
||||
d3d12_resource_create(struct pipe_screen *pscreen,
|
||||
const struct pipe_resource *templ)
|
||||
{
|
||||
struct d3d12_screen *screen = d3d12_screen(pscreen);
|
||||
struct d3d12_resource *res = CALLOC_STRUCT(d3d12_resource);
|
||||
bool ret;
|
||||
|
||||
res->base = *templ;
|
||||
|
||||
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, templ);
|
||||
}
|
||||
|
||||
pipe_reference_init(&res->base.reference, 1);
|
||||
res->base.screen = pscreen;
|
||||
|
||||
if (templ->target == PIPE_BUFFER) {
|
||||
ret = init_buffer(screen, res, templ);
|
||||
} else {
|
||||
ret = init_texture(screen, res, templ);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
FREE(res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
init_valid_range(res);
|
||||
|
||||
return &res->base;
|
||||
}
|
||||
|
||||
static struct pipe_resource *
|
||||
d3d12_resource_from_handle(struct pipe_screen *pscreen,
|
||||
const struct pipe_resource *templ,
|
||||
struct winsys_handle *handle, unsigned usage)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
get_subresource_id(struct d3d12_resource *res, unsigned resid,
|
||||
unsigned z, unsigned base_level)
|
||||
{
|
||||
unsigned resource_stride = res->base.last_level + 1;
|
||||
if (res->base.target == PIPE_TEXTURE_1D_ARRAY ||
|
||||
res->base.target == PIPE_TEXTURE_2D_ARRAY)
|
||||
resource_stride *= res->base.array_size;
|
||||
|
||||
if (res->base.target == PIPE_TEXTURE_CUBE)
|
||||
resource_stride *= 6;
|
||||
|
||||
if (res->base.target == PIPE_TEXTURE_CUBE_ARRAY)
|
||||
resource_stride *= 6 * res->base.array_size;
|
||||
|
||||
unsigned layer_stride = res->base.last_level + 1;
|
||||
|
||||
return resid * resource_stride + z * layer_stride +
|
||||
base_level;
|
||||
}
|
||||
|
||||
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.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 = d3d12_resource_underlying(res, &offset)->GetDesc();
|
||||
ID3D12Device* dev = d3d12_screen(ctx->base.screen)->dev;
|
||||
|
||||
unsigned sub_resid = get_subresource_id(res, resid, z, trans->base.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.Footprint.Width = ALIGN(trans->base.box.width,
|
||||
util_format_get_blockwidth(res->base.format));
|
||||
buf_loc.PlacedFootprint.Footprint.Height = ALIGN(trans->base.box.height,
|
||||
util_format_get_blockheight(res->base.format));
|
||||
buf_loc.PlacedFootprint.Footprint.Depth = ALIGN(depth,
|
||||
util_format_get_blockdepth(res->base.format));
|
||||
|
||||
buf_loc.PlacedFootprint.Footprint.RowPitch = trans->base.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);
|
||||
d3d12_batch_reference_resource(batch, info.dst);
|
||||
|
||||
d3d12_transition_resource_state(ctx, info.src, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||
d3d12_transition_resource_state(ctx, info.dst, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
d3d12_apply_resource_states(ctx);
|
||||
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.box.x, trans->base.box.y, trans->base.box.z,
|
||||
trans->base.box.width, trans->base.box.height, trans->base.box.depth,
|
||||
util_format_name(staging_res->base.format),
|
||||
util_format_name(res->base.format));
|
||||
}
|
||||
|
||||
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.layer_stride;
|
||||
copy_info.src_box = nullptr;
|
||||
copy_info.dst = res;
|
||||
copy_info.dst_loc = fill_texture_location(res, trans, resid, z);
|
||||
copy_info.dst_x = trans->base.box.x;
|
||||
copy_info.dst_y = trans->base.box.y;
|
||||
copy_info.dst_z = res->base.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.target == PIPE_TEXTURE_3D) {
|
||||
assert(resid == 0);
|
||||
transfer_buf_to_image_part(ctx, res, staging_res, trans,
|
||||
0, trans->base.box.depth, 0,
|
||||
trans->base.box.z, 0);
|
||||
} else {
|
||||
int num_layers = trans->base.box.depth;
|
||||
int start_z = trans->base.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.box;
|
||||
D3D12_BOX src_box = {};
|
||||
|
||||
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.layer_stride;
|
||||
copy_info.dst_x = copy_info.dst_y = copy_info.dst_z = 0;
|
||||
|
||||
if (!util_texrange_covers_whole_level(&res->base, trans->base.level,
|
||||
box->x, box->y, start_box_z,
|
||||
box->width, box->height, depth)) {
|
||||
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.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.box.x, trans->base.box.y, trans->base.box.z,
|
||||
trans->base.box.width, trans->base.box.height, trans->base.box.depth,
|
||||
util_format_name(res->base.format), resid,
|
||||
util_format_name(staging_res->base.format));
|
||||
}
|
||||
|
||||
struct pipe_resource *resolved_resource = nullptr;
|
||||
if (res->base.nr_samples > 1) {
|
||||
struct pipe_resource tmpl = res->base;
|
||||
tmpl.nr_samples = 0;
|
||||
resolved_resource = d3d12_resource_create(ctx->base.screen, &tmpl);
|
||||
struct pipe_blit_info resolve_info = {0};
|
||||
struct pipe_box box = {0,0,0, (int)res->base.width0, (int16_t)res->base.height0, (int16_t)res->base.depth0};
|
||||
resolve_info.dst.resource = resolved_resource;
|
||||
resolve_info.dst.box = box;
|
||||
resolve_info.dst.format = res->base.format;
|
||||
resolve_info.src.resource = &res->base;
|
||||
resolve_info.src.box = box;
|
||||
resolve_info.src.format = res->base.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;
|
||||
}
|
||||
|
||||
|
||||
if (res->base.target == PIPE_TEXTURE_3D) {
|
||||
transfer_image_part_to_buf(ctx, res, staging_res, trans, resid,
|
||||
0, 0, trans->base.box.z, trans->base.box.depth);
|
||||
} else {
|
||||
int start_layer = trans->base.box.z;
|
||||
for (int z = start_layer; z < start_layer + trans->base.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 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));
|
||||
|
||||
/* 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)) {
|
||||
if (usage & PIPE_MAP_DONTBLOCK)
|
||||
return false;
|
||||
|
||||
d3d12_resource_wait_idle(ctx, res);
|
||||
}
|
||||
|
||||
if (usage & PIPE_MAP_WRITE)
|
||||
util_range_add(&res->base, &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)
|
||||
{
|
||||
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_resource *res,
|
||||
const struct pipe_box *box,
|
||||
struct d3d12_transfer *trans)
|
||||
{
|
||||
trans->base.stride = align(util_format_get_stride(res->base.format, box->width),
|
||||
D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
trans->base.layer_stride = util_format_get_2d_size(res->base.format,
|
||||
trans->base.stride,
|
||||
box->height);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
prepare_zs_layer_strides(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.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);
|
||||
|
||||
void *depth_ptr = 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->base.layer_stride);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
trans->data = buf;
|
||||
|
||||
switch (res->base.format) {
|
||||
case PIPE_FORMAT_Z24_UNORM_S8_UINT:
|
||||
util_format_z24_unorm_s8_uint_pack_separate(buf, trans->base.stride,
|
||||
(uint32_t *)depth_ptr, trans->base.stride,
|
||||
stencil_ptr, trans->base.stride,
|
||||
trans->base.box.width, trans->base.box.height);
|
||||
break;
|
||||
case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
|
||||
util_format_z32_float_s8x24_uint_pack_z_float(buf, trans->base.stride,
|
||||
(float *)depth_ptr, trans->base.stride,
|
||||
trans->base.box.width, trans->base.box.height);
|
||||
util_format_z32_float_s8x24_uint_pack_s_8uint(buf, trans->base.stride,
|
||||
stencil_ptr, trans->base.stride,
|
||||
trans->base.box.width, trans->base.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)
|
||||
{
|
||||
prepare_zs_layer_strides(res, box, trans);
|
||||
uint32_t *buf = (uint32_t *)malloc(trans->base.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 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.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;
|
||||
}
|
||||
|
||||
void *depth_ptr = 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.format) {
|
||||
case PIPE_FORMAT_Z24_UNORM_S8_UINT:
|
||||
util_format_z32_unorm_unpack_z_32unorm((uint32_t *)depth_ptr, trans->base.stride, (uint8_t*)trans->data,
|
||||
trans->base.stride, trans->base.box.width,
|
||||
trans->base.box.height);
|
||||
util_format_z24_unorm_s8_uint_unpack_s_8uint(stencil_ptr, trans->base.stride, (uint8_t*)trans->data,
|
||||
trans->base.stride, trans->base.box.width,
|
||||
trans->base.box.height);
|
||||
break;
|
||||
case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
|
||||
util_format_z32_float_s8x24_uint_unpack_z_float((float *)depth_ptr, trans->base.stride, (uint8_t*)trans->data,
|
||||
trans->base.stride, trans->base.box.width,
|
||||
trans->base.box.height);
|
||||
util_format_z32_float_s8x24_uint_unpack_s_8uint(stencil_ptr, trans->base.stride, (uint8_t*)trans->data,
|
||||
trans->base.stride, trans->base.box.width,
|
||||
trans->base.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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (usage & PIPE_MAP_DIRECTLY || !res->bo)
|
||||
return NULL;
|
||||
|
||||
struct d3d12_transfer *trans = (struct d3d12_transfer *)slab_alloc(&ctx->transfer_pool);
|
||||
struct pipe_transfer *ptrans = &trans->base;
|
||||
if (!trans)
|
||||
return NULL;
|
||||
|
||||
memset(trans, 0, sizeof(*trans));
|
||||
pipe_resource_reference(&ptrans->resource, pres);
|
||||
|
||||
ptrans->resource = pres;
|
||||
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)) {
|
||||
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))
|
||||
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 {
|
||||
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.target != PIPE_TEXTURE_3D)
|
||||
ptrans->layer_stride = align(ptrans->layer_stride,
|
||||
D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
|
||||
trans->staging_res = pipe_buffer_create(pctx->screen, 0,
|
||||
PIPE_USAGE_STAGING,
|
||||
ptrans->layer_stride * box->depth);
|
||||
if (!trans->staging_res)
|
||||
return NULL;
|
||||
|
||||
struct d3d12_resource *staging_res = d3d12_resource(trans->staging_res);
|
||||
|
||||
if (usage & PIPE_MAP_READ) {
|
||||
bool ret = transfer_image_to_buf(ctx, res, staging_res, trans, 0);
|
||||
if (ret == false)
|
||||
return NULL;
|
||||
d3d12_flush_cmdlist_and_wait(ctx);
|
||||
}
|
||||
|
||||
range.Begin = 0;
|
||||
range.End = ptrans->layer_stride * box->depth;
|
||||
|
||||
ptr = d3d12_bo_map(staging_res->bo, &range);
|
||||
}
|
||||
|
||||
*transfer = ptrans;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void
|
||||
d3d12_transfer_unmap(struct pipe_context *pctx,
|
||||
struct pipe_transfer *ptrans)
|
||||
{
|
||||
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.usage & PIPE_MAP_WRITE)
|
||||
write_zs_surface(pctx, res, trans);
|
||||
free(trans->data);
|
||||
} else if (trans->staging_res) {
|
||||
struct d3d12_resource *staging_res = d3d12_resource(trans->staging_res);
|
||||
|
||||
if (trans->base.usage & PIPE_MAP_WRITE) {
|
||||
range.Begin = 0;
|
||||
range.End = ptrans->layer_stride * ptrans->box.depth;
|
||||
}
|
||||
d3d12_bo_unmap(staging_res->bo, &range);
|
||||
|
||||
if (trans->base.usage & PIPE_MAP_WRITE) {
|
||||
struct d3d12_context *ctx = d3d12_context(pctx);
|
||||
transfer_buf_to_image(ctx, res, staging_res, trans, 0);
|
||||
}
|
||||
|
||||
pipe_resource_reference(&trans->staging_res, NULL);
|
||||
} else {
|
||||
if (trans->base.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_resource_make_writeable(struct pipe_context *pctx,
|
||||
struct pipe_resource *pres)
|
||||
{
|
||||
struct d3d12_context *ctx = d3d12_context(pctx);
|
||||
struct d3d12_resource *res = d3d12_resource(pres);
|
||||
struct d3d12_resource *dup_res;
|
||||
|
||||
if (!res->bo || !d3d12_bo_is_suballocated(res->bo))
|
||||
return;
|
||||
|
||||
dup_res = d3d12_resource(pipe_buffer_create(pres->screen,
|
||||
pres->bind & PIPE_BIND_STREAM_OUTPUT,
|
||||
(pipe_resource_usage) pres->usage,
|
||||
pres->width0));
|
||||
|
||||
if (res->valid_buffer_range.end > res->valid_buffer_range.start) {
|
||||
struct pipe_box box;
|
||||
|
||||
box.x = res->valid_buffer_range.start;
|
||||
box.y = 0;
|
||||
box.z = 0;
|
||||
box.width = res->valid_buffer_range.end - res->valid_buffer_range.start;
|
||||
box.height = 1;
|
||||
box.depth = 1;
|
||||
|
||||
d3d12_direct_copy(ctx, dup_res, 0, &box, res, 0, &box, PIPE_MASK_RGBAZS);
|
||||
}
|
||||
|
||||
/* Move new BO to old resource */
|
||||
d3d12_bo_unreference(res->bo);
|
||||
res->bo = dup_res->bo;
|
||||
d3d12_bo_reference(res->bo);
|
||||
|
||||
d3d12_resource_destroy(dup_res->base.screen, &dup_res->base);
|
||||
}
|
||||
|
||||
void
|
||||
d3d12_context_resource_init(struct pipe_context *pctx)
|
||||
{
|
||||
pctx->transfer_map = d3d12_transfer_map;
|
||||
pctx->transfer_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;
|
||||
}
|
Reference in New Issue
Block a user