d3d12: Improve planar resource support to handle video requirements
authorSil Vilerino <sivileri@microsoft.com>
Mon, 2 May 2022 17:14:29 +0000 (10:14 -0700)
committerMarge Bot <emma+marge@anholt.net>
Tue, 17 May 2022 21:02:25 +0000 (21:02 +0000)
Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/16286>

src/gallium/drivers/d3d12/d3d12_blit.cpp
src/gallium/drivers/d3d12/d3d12_resource.cpp
src/gallium/drivers/d3d12/d3d12_resource.h
src/gallium/drivers/d3d12/d3d12_surface.cpp

index 51ea1e0..6ad9a91 100644 (file)
@@ -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",
index 10b55a9..4b6ba78 100644 (file)
@@ -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;
index ed7cb65..54c60fb 100644 (file)
@@ -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
index 80b96ad..db4dbe6 100644 (file)
@@ -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: