From 739283da13e5da48b9aefebb8e25129181786ea1 Mon Sep 17 00:00:00 2001 From: Sil Vilerino Date: Mon, 2 May 2022 10:14:29 -0700 Subject: [PATCH] d3d12: Improve planar resource support to handle video requirements Reviewed-by: Jesse Natalie Part-of: --- src/gallium/drivers/d3d12/d3d12_blit.cpp | 12 +- src/gallium/drivers/d3d12/d3d12_resource.cpp | 312 +++++++++++++++++++++++++-- src/gallium/drivers/d3d12/d3d12_resource.h | 6 + src/gallium/drivers/d3d12/d3d12_surface.cpp | 2 +- 4 files changed, 306 insertions(+), 26 deletions(-) diff --git a/src/gallium/drivers/d3d12/d3d12_blit.cpp b/src/gallium/drivers/d3d12/d3d12_blit.cpp index 51ea1e0..6ad9a91 100644 --- a/src/gallium/drivers/d3d12/d3d12_blit.cpp +++ b/src/gallium/drivers/d3d12/d3d12_blit.cpp @@ -251,14 +251,14 @@ direct_copy_supported(struct d3d12_screen *screen, inline static unsigned get_subresource_id(enum pipe_texture_target target, unsigned subres, unsigned stride, - unsigned z, unsigned *updated_z) + unsigned z, unsigned *updated_z, unsigned array_size, unsigned plane_slice) { if (d3d12_subresource_id_uses_layer(target)) { subres += stride * z; if (updated_z) *updated_z = 0; } - return subres; + return subres + plane_slice * array_size * stride; } static void @@ -317,12 +317,12 @@ copy_subregion_no_barriers(struct d3d12_context *ctx, continue; src_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - src_loc.SubresourceIndex = get_subresource_id(src->base.b.target, src_level, src_subres_stride, src_z, &src_z) + + src_loc.SubresourceIndex = get_subresource_id(src->base.b.target, src_level, src_subres_stride, src_z, &src_z, src_array_size, src->plane_slice) + subres * stencil_src_res_offset; src_loc.pResource = d3d12_resource_resource(src); dst_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dst_loc.SubresourceIndex = get_subresource_id(dst->base.b.target, dst_level, dst_subres_stride, dstz, &dstz) + + dst_loc.SubresourceIndex = get_subresource_id(dst->base.b.target, dst_level, dst_subres_stride, dstz, &dstz, dst_array_size, dst->plane_slice) + subres * stencil_dst_res_offset; dst_loc.pResource = d3d12_resource_resource(dst); @@ -412,9 +412,9 @@ d3d12_direct_copy(struct d3d12_context *ctx, struct d3d12_batch *batch = d3d12_current_batch(ctx); unsigned src_subres = get_subresource_id(src->base.b.target, src_level, src->base.b.last_level + 1, - psrc_box->z, nullptr); + psrc_box->z, nullptr, src->base.b.array_size, src->plane_slice); unsigned dst_subres = get_subresource_id(dst->base.b.target, dst_level, dst->base.b.last_level + 1, - pdst_box->z, nullptr); + pdst_box->z, nullptr, dst->base.b.array_size, dst->plane_slice); if (D3D12_DEBUG_BLIT & d3d12_debug) debug_printf("BLIT: Direct copy from subres %d to subres %d\n", diff --git a/src/gallium/drivers/d3d12/d3d12_resource.cpp b/src/gallium/drivers/d3d12/d3d12_resource.cpp index 10b55a9..4b6ba78 100644 --- a/src/gallium/drivers/d3d12/d3d12_resource.cpp +++ b/src/gallium/drivers/d3d12/d3d12_resource.cpp @@ -303,6 +303,7 @@ convert_planar_resource(struct d3d12_resource *res) 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); @@ -317,6 +318,7 @@ convert_planar_resource(struct d3d12_resource *res) 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 } } @@ -331,6 +333,8 @@ d3d12_resource_create(struct pipe_screen *pscreen, 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", @@ -550,6 +554,8 @@ d3d12_resource_from_handle(struct pipe_screen *pscreen, 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, res->overall_format, d3d12_permanently_resident); @@ -609,6 +615,151 @@ d3d12_resource_get_handle(struct pipe_screen *pscreen, } } +struct pipe_resource * +d3d12_resource_from_resource(struct pipe_screen *pscreen, + ID3D12Resource* input_res) +{ + D3D12_RESOURCE_DESC input_desc = input_res->GetDesc(); + 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; + + 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; + } +} + +/** + * 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[num_planes]; + unsigned int strides[num_planes]; + unsigned int layer_strides[num_planes]; + unsigned int offsets[num_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]; + } +} + void d3d12_screen_resource_init(struct pipe_screen *pscreen) { @@ -616,6 +767,7 @@ d3d12_screen_resource_init(struct pipe_screen *pscreen) 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; } unsigned int @@ -636,7 +788,7 @@ get_subresource_id(struct d3d12_resource *res, unsigned resid, unsigned layer_stride = res->base.b.last_level + 1; return resid * resource_stride + z * layer_stride + - base_level; + base_level + res->plane_slice * resource_stride; } static D3D12_TEXTURE_COPY_LOCATION @@ -674,6 +826,7 @@ fill_buffer_location(struct d3d12_context *ctx, 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) { @@ -1286,6 +1439,72 @@ d3d12_transfer_map(struct pipe_context *pctx, } 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[num_planes]; + unsigned int strides[num_planes]; + unsigned int layer_strides[num_planes]; + unsigned int offsets[num_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; + } + } + + 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); @@ -1364,6 +1583,7 @@ 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 }; @@ -1373,27 +1593,81 @@ d3d12_transfer_unmap(struct pipe_context *pctx, 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(util_format_is_yuv(res->overall_format)) { + + /* Get planes information*/ + unsigned num_planes = util_format_get_num_planes(res->overall_format); + pipe_resource* planes[num_planes]; + unsigned int strides[num_planes]; + unsigned int layer_strides[num_planes]; + unsigned int offsets[num_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); + } + } - 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_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); + } - 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); } - - pipe_resource_reference(&trans->staging_res, NULL); } else { if (trans->base.b.usage & PIPE_MAP_WRITE) { range.Begin = ptrans->box.x; diff --git a/src/gallium/drivers/d3d12/d3d12_resource.h b/src/gallium/drivers/d3d12/d3d12_resource.h index ed7cb65..54c60fb 100644 --- a/src/gallium/drivers/d3d12/d3d12_resource.h +++ b/src/gallium/drivers/d3d12/d3d12_resource.h @@ -45,6 +45,8 @@ struct d3d12_resource { struct d3d12_bo *bo; DXGI_FORMAT dxgi_format; enum pipe_format overall_format; + unsigned int plane_slice; + struct pipe_resource* first_plane; unsigned mip_levels; struct sw_displaytarget *dt; unsigned dt_stride; @@ -127,4 +129,8 @@ d3d12_screen_resource_init(struct pipe_screen *pscreen); void d3d12_context_resource_init(struct pipe_context *pctx); +struct pipe_resource * +d3d12_resource_from_resource(struct pipe_screen *pscreen, + ID3D12Resource* inputRes); + #endif diff --git a/src/gallium/drivers/d3d12/d3d12_surface.cpp b/src/gallium/drivers/d3d12/d3d12_surface.cpp index 80b96ad..db4dbe6 100644 --- a/src/gallium/drivers/d3d12/d3d12_surface.cpp +++ b/src/gallium/drivers/d3d12/d3d12_surface.cpp @@ -194,7 +194,7 @@ initialize_rtv(struct pipe_context *pctx, tpl->u.tex.first_layer); desc.Texture2D.MipSlice = tpl->u.tex.level; - desc.Texture2D.PlaneSlice = 0; + desc.Texture2D.PlaneSlice = res->plane_slice; break; case D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY: -- 2.7.4