• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © Microsoft Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include "d3d12_resource.h"
25 
26 #include "d3d12_blit.h"
27 #include "d3d12_context.h"
28 #include "d3d12_format.h"
29 #include "d3d12_screen.h"
30 #include "d3d12_debug.h"
31 
32 #include "pipebuffer/pb_bufmgr.h"
33 #include "util/slab.h"
34 #include "util/format/u_format.h"
35 #include "util/u_inlines.h"
36 #include "util/u_memory.h"
37 #include "util/format/u_format_zs.h"
38 
39 #include "frontend/sw_winsys.h"
40 
41 #include <directx/d3d12.h>
42 #include <dxguids/dxguids.h>
43 #include <memory>
44 
45 static bool
can_map_directly(struct pipe_resource * pres)46 can_map_directly(struct pipe_resource *pres)
47 {
48    return pres->target == PIPE_BUFFER &&
49           pres->usage != PIPE_USAGE_DEFAULT &&
50           pres->usage != PIPE_USAGE_IMMUTABLE;
51 }
52 
53 static void
init_valid_range(struct d3d12_resource * res)54 init_valid_range(struct d3d12_resource *res)
55 {
56    if (can_map_directly(&res->base))
57       util_range_init(&res->valid_buffer_range);
58 }
59 
60 static void
d3d12_resource_destroy(struct pipe_screen * pscreen,struct pipe_resource * presource)61 d3d12_resource_destroy(struct pipe_screen *pscreen,
62                        struct pipe_resource *presource)
63 {
64    struct d3d12_resource *resource = d3d12_resource(presource);
65    if (can_map_directly(presource))
66       util_range_destroy(&resource->valid_buffer_range);
67    if (resource->bo)
68       d3d12_bo_unreference(resource->bo);
69    FREE(resource);
70 }
71 
72 static bool
resource_is_busy(struct d3d12_context * ctx,struct d3d12_resource * res)73 resource_is_busy(struct d3d12_context *ctx,
74                  struct d3d12_resource *res)
75 {
76    bool busy = false;
77 
78    for (unsigned i = 0; i < ARRAY_SIZE(ctx->batches); i++)
79       busy |= d3d12_batch_has_references(&ctx->batches[i], res->bo);
80 
81    return busy;
82 }
83 
84 void
d3d12_resource_wait_idle(struct d3d12_context * ctx,struct d3d12_resource * res)85 d3d12_resource_wait_idle(struct d3d12_context *ctx,
86                          struct d3d12_resource *res)
87 {
88    if (d3d12_batch_has_references(d3d12_current_batch(ctx), res->bo)) {
89       d3d12_flush_cmdlist_and_wait(ctx);
90    } else {
91       d3d12_foreach_submitted_batch(ctx, batch) {
92          d3d12_reset_batch(ctx, batch, PIPE_TIMEOUT_INFINITE);
93          if (!resource_is_busy(ctx, res))
94             break;
95       }
96    }
97 }
98 
99 void
d3d12_resource_release(struct d3d12_resource * resource)100 d3d12_resource_release(struct d3d12_resource *resource)
101 {
102    if (!resource->bo)
103       return;
104    d3d12_bo_unreference(resource->bo);
105    resource->bo = NULL;
106 }
107 
108 static bool
init_buffer(struct d3d12_screen * screen,struct d3d12_resource * res,const struct pipe_resource * templ)109 init_buffer(struct d3d12_screen *screen,
110             struct d3d12_resource *res,
111             const struct pipe_resource *templ)
112 {
113    struct pb_desc buf_desc;
114    struct pb_manager *bufmgr;
115    struct pb_buffer *buf;
116 
117    /* Assert that we don't want to create a buffer with one of the emulated
118     * formats, these are (currently) only supported when passing the vertex
119     * element state */
120    assert(templ->format == d3d12_emulated_vtx_format(templ->format));
121 
122    switch (templ->usage) {
123    case PIPE_USAGE_DEFAULT:
124    case PIPE_USAGE_IMMUTABLE:
125       bufmgr = screen->cache_bufmgr;
126       buf_desc.usage = (pb_usage_flags)PB_USAGE_GPU_READ_WRITE;
127       break;
128    case PIPE_USAGE_DYNAMIC:
129    case PIPE_USAGE_STREAM:
130       bufmgr = screen->slab_bufmgr;
131       buf_desc.usage = (pb_usage_flags)(PB_USAGE_CPU_WRITE | PB_USAGE_GPU_READ);
132       break;
133    case PIPE_USAGE_STAGING:
134       bufmgr = screen->readback_slab_bufmgr;
135       buf_desc.usage = (pb_usage_flags)(PB_USAGE_GPU_WRITE | PB_USAGE_CPU_READ_WRITE);
136       break;
137    default:
138       unreachable("Invalid pipe usage");
139    }
140    buf_desc.alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
141    res->dxgi_format = DXGI_FORMAT_UNKNOWN;
142    buf = bufmgr->create_buffer(bufmgr, templ->width0, &buf_desc);
143    if (!buf)
144       return false;
145    res->bo = d3d12_bo_wrap_buffer(buf);
146 
147    return true;
148 }
149 
150 static bool
init_texture(struct d3d12_screen * screen,struct d3d12_resource * res,const struct pipe_resource * templ)151 init_texture(struct d3d12_screen *screen,
152              struct d3d12_resource *res,
153              const struct pipe_resource *templ)
154 {
155    ID3D12Resource *d3d12_res;
156 
157    res->mip_levels = templ->last_level + 1;
158    res->dxgi_format = d3d12_get_format(templ->format);
159 
160    D3D12_RESOURCE_DESC desc;
161    desc.Format = res->dxgi_format;
162    desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
163    desc.Width = templ->width0;
164    desc.Height = templ->height0;
165    desc.DepthOrArraySize = templ->array_size;
166    desc.MipLevels = templ->last_level + 1;
167 
168    desc.SampleDesc.Count = MAX2(templ->nr_samples, 1);
169    desc.SampleDesc.Quality = 0; /* TODO: figure this one out */
170 
171    switch (templ->target) {
172    case PIPE_TEXTURE_1D:
173    case PIPE_TEXTURE_1D_ARRAY:
174       desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE1D;
175       break;
176 
177    case PIPE_TEXTURE_CUBE:
178    case PIPE_TEXTURE_CUBE_ARRAY:
179       desc.DepthOrArraySize *= 6;
180       FALLTHROUGH;
181    case PIPE_TEXTURE_2D:
182    case PIPE_TEXTURE_2D_ARRAY:
183    case PIPE_TEXTURE_RECT:
184       desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
185       break;
186 
187    case PIPE_TEXTURE_3D:
188       desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
189       desc.DepthOrArraySize = templ->depth0;
190       break;
191 
192    default:
193       unreachable("Invalid texture type");
194    }
195 
196    desc.Flags = D3D12_RESOURCE_FLAG_NONE;
197 
198    if (templ->bind & PIPE_BIND_SHADER_BUFFER)
199       desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
200 
201    if (templ->bind & PIPE_BIND_RENDER_TARGET)
202       desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
203 
204    if (templ->bind & PIPE_BIND_DEPTH_STENCIL) {
205       desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
206 
207       /* Sadly, we can't set D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE in the
208        * case where PIPE_BIND_SAMPLER_VIEW isn't set, because that would
209        * prevent us from using the resource with u_blitter, which requires
210        * sneaking in sampler-usage throught the back-door.
211        */
212    }
213 
214    desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
215    if (templ->bind & (PIPE_BIND_SCANOUT |
216                       PIPE_BIND_SHARED | PIPE_BIND_LINEAR))
217       desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
218 
219    D3D12_HEAP_PROPERTIES heap_pris = screen->dev->GetCustomHeapProperties(0, D3D12_HEAP_TYPE_DEFAULT);
220 
221    HRESULT hres = screen->dev->CreateCommittedResource(&heap_pris,
222                                                    D3D12_HEAP_FLAG_NONE,
223                                                    &desc,
224                                                    D3D12_RESOURCE_STATE_COMMON,
225                                                    NULL,
226                                                    IID_PPV_ARGS(&d3d12_res));
227    if (FAILED(hres))
228       return false;
229 
230    if (screen->winsys && (templ->bind & PIPE_BIND_DISPLAY_TARGET)) {
231       struct sw_winsys *winsys = screen->winsys;
232       res->dt = winsys->displaytarget_create(screen->winsys,
233                                              res->base.bind,
234                                              res->base.format,
235                                              templ->width0,
236                                              templ->height0,
237                                              64, NULL,
238                                              &res->dt_stride);
239    }
240 
241    res->bo = d3d12_bo_wrap_res(d3d12_res, templ->format);
242 
243    return true;
244 }
245 
246 static struct pipe_resource *
d3d12_resource_create(struct pipe_screen * pscreen,const struct pipe_resource * templ)247 d3d12_resource_create(struct pipe_screen *pscreen,
248                       const struct pipe_resource *templ)
249 {
250    struct d3d12_screen *screen = d3d12_screen(pscreen);
251    struct d3d12_resource *res = CALLOC_STRUCT(d3d12_resource);
252    bool ret;
253 
254    res->base = *templ;
255 
256    if (D3D12_DEBUG_RESOURCE & d3d12_debug) {
257       debug_printf("D3D12: Create %sresource %s@%d %dx%dx%d as:%d mip:%d\n",
258                    templ->usage == PIPE_USAGE_STAGING ? "STAGING " :"",
259                    util_format_name(templ->format), templ->nr_samples,
260                    templ->width0, templ->height0, templ->depth0,
261                    templ->array_size, templ->last_level);
262    }
263 
264    pipe_reference_init(&res->base.reference, 1);
265    res->base.screen = pscreen;
266 
267    if (templ->target == PIPE_BUFFER) {
268       ret = init_buffer(screen, res, templ);
269    } else {
270       ret = init_texture(screen, res, templ);
271    }
272 
273    if (!ret) {
274       FREE(res);
275       return NULL;
276    }
277 
278    init_valid_range(res);
279 
280    memset(&res->bind_counts, 0, sizeof(d3d12_resource::bind_counts));
281 
282    return &res->base;
283 }
284 
285 static struct pipe_resource *
d3d12_resource_from_handle(struct pipe_screen * pscreen,const struct pipe_resource * templ,struct winsys_handle * handle,unsigned usage)286 d3d12_resource_from_handle(struct pipe_screen *pscreen,
287                           const struct pipe_resource *templ,
288                           struct winsys_handle *handle, unsigned usage)
289 {
290    if (handle->type != WINSYS_HANDLE_TYPE_D3D12_RES)
291       return NULL;
292 
293    struct d3d12_resource *res = CALLOC_STRUCT(d3d12_resource);
294    if (!res)
295       return NULL;
296 
297    res->base = *templ;
298    pipe_reference_init(&res->base.reference, 1);
299    res->base.screen = pscreen;
300    res->dxgi_format = templ->target == PIPE_BUFFER ? DXGI_FORMAT_UNKNOWN :
301                  d3d12_get_format(templ->format);
302    res->bo = d3d12_bo_wrap_res((ID3D12Resource *)handle->com_obj, templ->format);
303    init_valid_range(res);
304    return &res->base;
305 }
306 
307 static bool
d3d12_resource_get_handle(struct pipe_screen * pscreen,struct pipe_context * pcontext,struct pipe_resource * pres,struct winsys_handle * handle,unsigned usage)308 d3d12_resource_get_handle(struct pipe_screen *pscreen,
309                           struct pipe_context *pcontext,
310                           struct pipe_resource *pres,
311                           struct winsys_handle *handle,
312                           unsigned usage)
313 {
314    struct d3d12_resource *res = d3d12_resource(pres);
315 
316    if (handle->type != WINSYS_HANDLE_TYPE_D3D12_RES)
317       return false;
318 
319    handle->com_obj = d3d12_resource_resource(res);
320    return true;
321 }
322 
323 void
d3d12_screen_resource_init(struct pipe_screen * pscreen)324 d3d12_screen_resource_init(struct pipe_screen *pscreen)
325 {
326    pscreen->resource_create = d3d12_resource_create;
327    pscreen->resource_from_handle = d3d12_resource_from_handle;
328    pscreen->resource_get_handle = d3d12_resource_get_handle;
329    pscreen->resource_destroy = d3d12_resource_destroy;
330 }
331 
332 unsigned int
get_subresource_id(struct d3d12_resource * res,unsigned resid,unsigned z,unsigned base_level)333 get_subresource_id(struct d3d12_resource *res, unsigned resid,
334                    unsigned z, unsigned base_level)
335 {
336    unsigned resource_stride = res->base.last_level + 1;
337    if (res->base.target == PIPE_TEXTURE_1D_ARRAY ||
338        res->base.target == PIPE_TEXTURE_2D_ARRAY)
339       resource_stride *= res->base.array_size;
340 
341    if (res->base.target == PIPE_TEXTURE_CUBE)
342       resource_stride *= 6;
343 
344    if (res->base.target == PIPE_TEXTURE_CUBE_ARRAY)
345       resource_stride *= 6 * res->base.array_size;
346 
347    unsigned layer_stride = res->base.last_level + 1;
348 
349    return resid * resource_stride + z * layer_stride +
350          base_level;
351 }
352 
353 static D3D12_TEXTURE_COPY_LOCATION
fill_texture_location(struct d3d12_resource * res,struct d3d12_transfer * trans,unsigned resid,unsigned z)354 fill_texture_location(struct d3d12_resource *res,
355                       struct d3d12_transfer *trans, unsigned resid, unsigned z)
356 {
357    D3D12_TEXTURE_COPY_LOCATION tex_loc = {0};
358    int subres = get_subresource_id(res, resid, z, trans->base.level);
359 
360    tex_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
361    tex_loc.SubresourceIndex = subres;
362    tex_loc.pResource = d3d12_resource_resource(res);
363    return tex_loc;
364 }
365 
366 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)367 fill_buffer_location(struct d3d12_context *ctx,
368                      struct d3d12_resource *res,
369                      struct d3d12_resource *staging_res,
370                      struct d3d12_transfer *trans,
371                      unsigned depth,
372                      unsigned resid, unsigned z)
373 {
374    D3D12_TEXTURE_COPY_LOCATION buf_loc = {0};
375    D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
376    uint64_t offset = 0;
377    auto descr = d3d12_resource_underlying(res, &offset)->GetDesc();
378    ID3D12Device* dev = d3d12_screen(ctx->base.screen)->dev;
379 
380    unsigned sub_resid = get_subresource_id(res, resid, z, trans->base.level);
381    dev->GetCopyableFootprints(&descr, sub_resid, 1, 0, &footprint, nullptr, nullptr, nullptr);
382 
383    buf_loc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
384    buf_loc.pResource = d3d12_resource_underlying(staging_res, &offset);
385    buf_loc.PlacedFootprint = footprint;
386    buf_loc.PlacedFootprint.Offset += offset;
387 
388    buf_loc.PlacedFootprint.Footprint.Width = ALIGN(trans->base.box.width,
389                                                    util_format_get_blockwidth(res->base.format));
390    buf_loc.PlacedFootprint.Footprint.Height = ALIGN(trans->base.box.height,
391                                                     util_format_get_blockheight(res->base.format));
392    buf_loc.PlacedFootprint.Footprint.Depth = ALIGN(depth,
393                                                    util_format_get_blockdepth(res->base.format));
394 
395    buf_loc.PlacedFootprint.Footprint.RowPitch = trans->base.stride;
396 
397    return buf_loc;
398 }
399 
400 struct copy_info {
401    struct d3d12_resource *dst;
402    D3D12_TEXTURE_COPY_LOCATION dst_loc;
403    UINT dst_x, dst_y, dst_z;
404    struct d3d12_resource *src;
405    D3D12_TEXTURE_COPY_LOCATION src_loc;
406    D3D12_BOX *src_box;
407 };
408 
409 
410 static void
copy_texture_region(struct d3d12_context * ctx,struct copy_info & info)411 copy_texture_region(struct d3d12_context *ctx,
412                     struct copy_info& info)
413 {
414    auto batch = d3d12_current_batch(ctx);
415 
416    d3d12_batch_reference_resource(batch, info.src);
417    d3d12_batch_reference_resource(batch, info.dst);
418    d3d12_transition_resource_state(ctx, info.src, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_BIND_INVALIDATE_FULL);
419    d3d12_transition_resource_state(ctx, info.dst, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_BIND_INVALIDATE_FULL);
420    d3d12_apply_resource_states(ctx);
421    ctx->cmdlist->CopyTextureRegion(&info.dst_loc, info.dst_x, info.dst_y, info.dst_z,
422                                    &info.src_loc, info.src_box);
423 }
424 
425 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)426 transfer_buf_to_image_part(struct d3d12_context *ctx,
427                            struct d3d12_resource *res,
428                            struct d3d12_resource *staging_res,
429                            struct d3d12_transfer *trans,
430                            int z, int depth, int start_z, int dest_z,
431                            int resid)
432 {
433    if (D3D12_DEBUG_RESOURCE & d3d12_debug) {
434       debug_printf("D3D12: Copy %dx%dx%d + %dx%dx%d from buffer %s to image %s\n",
435                    trans->base.box.x, trans->base.box.y, trans->base.box.z,
436                    trans->base.box.width, trans->base.box.height, trans->base.box.depth,
437                    util_format_name(staging_res->base.format),
438                    util_format_name(res->base.format));
439    }
440 
441    struct copy_info copy_info;
442    copy_info.src = staging_res;
443    copy_info.src_loc = fill_buffer_location(ctx, res, staging_res, trans, depth, resid, z);
444    copy_info.src_loc.PlacedFootprint.Offset = (z  - start_z) * trans->base.layer_stride;
445    copy_info.src_box = nullptr;
446    copy_info.dst = res;
447    copy_info.dst_loc = fill_texture_location(res, trans, resid, z);
448    copy_info.dst_x = trans->base.box.x;
449    copy_info.dst_y = trans->base.box.y;
450    copy_info.dst_z = res->base.target == PIPE_TEXTURE_CUBE ? 0 : dest_z;
451    copy_info.src_box = nullptr;
452 
453    copy_texture_region(ctx, copy_info);
454 }
455 
456 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)457 transfer_buf_to_image(struct d3d12_context *ctx,
458                       struct d3d12_resource *res,
459                       struct d3d12_resource *staging_res,
460                       struct d3d12_transfer *trans, int resid)
461 {
462    if (res->base.target == PIPE_TEXTURE_3D) {
463       assert(resid == 0);
464       transfer_buf_to_image_part(ctx, res, staging_res, trans,
465                                  0, trans->base.box.depth, 0,
466                                  trans->base.box.z, 0);
467    } else {
468       int num_layers = trans->base.box.depth;
469       int start_z = trans->base.box.z;
470 
471       for (int z = start_z; z < start_z + num_layers; ++z) {
472          transfer_buf_to_image_part(ctx, res, staging_res, trans,
473                                            z, 1, start_z, 0, resid);
474       }
475    }
476    return true;
477 }
478 
479 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)480 transfer_image_part_to_buf(struct d3d12_context *ctx,
481                            struct d3d12_resource *res,
482                            struct d3d12_resource *staging_res,
483                            struct d3d12_transfer *trans,
484                            unsigned resid, int z, int start_layer,
485                            int start_box_z, int depth)
486 {
487    struct pipe_box *box = &trans->base.box;
488    D3D12_BOX src_box = {};
489 
490    struct copy_info copy_info;
491    copy_info.src_box = nullptr;
492    copy_info.src = res;
493    copy_info.src_loc = fill_texture_location(res, trans, resid, z);
494    copy_info.dst = staging_res;
495    copy_info.dst_loc = fill_buffer_location(ctx, res, staging_res, trans,
496                                             depth, resid, z);
497    copy_info.dst_loc.PlacedFootprint.Offset = (z  - start_layer) * trans->base.layer_stride;
498    copy_info.dst_x = copy_info.dst_y = copy_info.dst_z = 0;
499 
500    if (!util_texrange_covers_whole_level(&res->base, trans->base.level,
501                                          box->x, box->y, start_box_z,
502                                          box->width, box->height, depth)) {
503       src_box.left = box->x;
504       src_box.right = box->x + box->width;
505       src_box.top = box->y;
506       src_box.bottom = box->y + box->height;
507       src_box.front = start_box_z;
508       src_box.back = start_box_z + depth;
509       copy_info.src_box = &src_box;
510    }
511 
512    copy_texture_region(ctx, copy_info);
513 }
514 
515 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)516 transfer_image_to_buf(struct d3d12_context *ctx,
517                             struct d3d12_resource *res,
518                             struct d3d12_resource *staging_res,
519                             struct d3d12_transfer *trans,
520                             unsigned resid)
521 {
522 
523    /* We only suppport loading from either an texture array
524     * or a ZS texture, so either resid is zero, or num_layers == 1)
525     */
526    assert(resid == 0 || trans->base.box.depth == 1);
527 
528    if (D3D12_DEBUG_RESOURCE & d3d12_debug) {
529       debug_printf("D3D12: Copy %dx%dx%d + %dx%dx%d from %s@%d to %s\n",
530                    trans->base.box.x, trans->base.box.y, trans->base.box.z,
531                    trans->base.box.width, trans->base.box.height, trans->base.box.depth,
532                    util_format_name(res->base.format), resid,
533                    util_format_name(staging_res->base.format));
534    }
535 
536    struct pipe_resource *resolved_resource = nullptr;
537    if (res->base.nr_samples > 1) {
538       struct pipe_resource tmpl = res->base;
539       tmpl.nr_samples = 0;
540       resolved_resource = d3d12_resource_create(ctx->base.screen, &tmpl);
541       struct pipe_blit_info resolve_info = {};
542       struct pipe_box box = {0,0,0, (int)res->base.width0, (int16_t)res->base.height0, (int16_t)res->base.depth0};
543       resolve_info.dst.resource = resolved_resource;
544       resolve_info.dst.box = box;
545       resolve_info.dst.format = res->base.format;
546       resolve_info.src.resource = &res->base;
547       resolve_info.src.box = box;
548       resolve_info.src.format = res->base.format;
549       resolve_info.filter = PIPE_TEX_FILTER_NEAREST;
550       resolve_info.mask = util_format_get_mask(tmpl.format);
551 
552 
553 
554       d3d12_blit(&ctx->base, &resolve_info);
555       res = (struct d3d12_resource *)resolved_resource;
556    }
557 
558 
559    if (res->base.target == PIPE_TEXTURE_3D) {
560       transfer_image_part_to_buf(ctx, res, staging_res, trans, resid,
561                                  0, 0, trans->base.box.z, trans->base.box.depth);
562    } else {
563       int start_layer = trans->base.box.z;
564       for (int z = start_layer; z < start_layer + trans->base.box.depth; ++z) {
565          transfer_image_part_to_buf(ctx, res, staging_res, trans, resid,
566                                     z, start_layer, 0, 1);
567       }
568    }
569 
570    pipe_resource_reference(&resolved_resource, NULL);
571 
572    return true;
573 }
574 
575 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)576 transfer_buf_to_buf(struct d3d12_context *ctx,
577                     struct d3d12_resource *src,
578                     struct d3d12_resource *dst,
579                     uint64_t src_offset,
580                     uint64_t dst_offset,
581                     uint64_t width)
582 {
583    auto batch = d3d12_current_batch(ctx);
584 
585    d3d12_batch_reference_resource(batch, src);
586    d3d12_batch_reference_resource(batch, dst);
587 
588    uint64_t src_offset_suballoc = 0;
589    uint64_t dst_offset_suballoc = 0;
590    auto src_d3d12 = d3d12_resource_underlying(src, &src_offset_suballoc);
591    auto dst_d3d12 = d3d12_resource_underlying(dst, &dst_offset_suballoc);
592    src_offset += src_offset_suballoc;
593    dst_offset += dst_offset_suballoc;
594 
595    // Same-resource copies not supported, since the resource would need to be in both states
596    assert(src_d3d12 != dst_d3d12);
597    d3d12_transition_resource_state(ctx, src, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_BIND_INVALIDATE_FULL);
598    d3d12_transition_resource_state(ctx, dst, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_BIND_INVALIDATE_FULL);
599    d3d12_apply_resource_states(ctx);
600    ctx->cmdlist->CopyBufferRegion(dst_d3d12, dst_offset,
601                                   src_d3d12, src_offset,
602                                   width);
603 }
604 
605 static unsigned
linear_offset(int x,int y,int z,unsigned stride,unsigned layer_stride)606 linear_offset(int x, int y, int z, unsigned stride, unsigned layer_stride)
607 {
608    return x +
609           y * stride +
610           z * layer_stride;
611 }
612 
613 static D3D12_RANGE
linear_range(const struct pipe_box * box,unsigned stride,unsigned layer_stride)614 linear_range(const struct pipe_box *box, unsigned stride, unsigned layer_stride)
615 {
616    D3D12_RANGE range;
617 
618    range.Begin = linear_offset(box->x, box->y, box->z,
619                                stride, layer_stride);
620    range.End = linear_offset(box->x + box->width,
621                              box->y + box->height - 1,
622                              box->z + box->depth - 1,
623                              stride, layer_stride);
624 
625    return range;
626 }
627 
628 static bool
synchronize(struct d3d12_context * ctx,struct d3d12_resource * res,unsigned usage,D3D12_RANGE * range)629 synchronize(struct d3d12_context *ctx,
630             struct d3d12_resource *res,
631             unsigned usage,
632             D3D12_RANGE *range)
633 {
634    assert(can_map_directly(&res->base));
635 
636    /* Check whether that range contains valid data; if not, we might not need to sync */
637    if (!(usage & PIPE_MAP_UNSYNCHRONIZED) &&
638        usage & PIPE_MAP_WRITE &&
639        !util_ranges_intersect(&res->valid_buffer_range, range->Begin, range->End)) {
640       usage |= PIPE_MAP_UNSYNCHRONIZED;
641    }
642 
643    if (!(usage & PIPE_MAP_UNSYNCHRONIZED) && resource_is_busy(ctx, res)) {
644       if (usage & PIPE_MAP_DONTBLOCK)
645          return false;
646 
647       d3d12_resource_wait_idle(ctx, res);
648    }
649 
650    if (usage & PIPE_MAP_WRITE)
651       util_range_add(&res->base, &res->valid_buffer_range,
652                      range->Begin, range->End);
653 
654    return true;
655 }
656 
657 /* A wrapper to make sure local resources are freed and unmapped with
658  * any exit path */
659 struct local_resource {
local_resourcelocal_resource660    local_resource(pipe_screen *s, struct pipe_resource *tmpl) :
661       mapped(false)
662    {
663       res = d3d12_resource(d3d12_resource_create(s, tmpl));
664    }
665 
~local_resourcelocal_resource666    ~local_resource() {
667       if (res) {
668          if (mapped)
669             d3d12_bo_unmap(res->bo, nullptr);
670          pipe_resource_reference((struct pipe_resource **)&res, NULL);
671       }
672    }
673 
674    void *
maplocal_resource675    map() {
676       void *ptr;
677       ptr = d3d12_bo_map(res->bo, nullptr);
678       if (ptr)
679          mapped = true;
680       return ptr;
681    }
682 
unmaplocal_resource683    void unmap()
684    {
685       if (mapped)
686          d3d12_bo_unmap(res->bo, nullptr);
687       mapped = false;
688    }
689 
operator struct d3d12_resource*local_resource690    operator struct d3d12_resource *() {
691       return res;
692    }
693 
operator !local_resource694    bool operator !() {
695       return !res;
696    }
697 private:
698    struct d3d12_resource *res;
699    bool mapped;
700 };
701 
702 /* Combined depth-stencil needs a special handling for reading back: DX handled
703  * depth and stencil parts as separate resources and handles copying them only
704  * by using seperate texture copy calls with different formats. So create two
705  * buffers, read back both resources and interleave the data.
706  */
707 static void
prepare_zs_layer_strides(struct d3d12_resource * res,const struct pipe_box * box,struct d3d12_transfer * trans)708 prepare_zs_layer_strides(struct d3d12_resource *res,
709                          const struct pipe_box *box,
710                          struct d3d12_transfer *trans)
711 {
712    trans->base.stride = align(util_format_get_stride(res->base.format, box->width),
713                               D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
714    trans->base.layer_stride = util_format_get_2d_size(res->base.format,
715                                                       trans->base.stride,
716                                                       box->height);
717 }
718 
719 static void *
read_zs_surface(struct d3d12_context * ctx,struct d3d12_resource * res,const struct pipe_box * box,struct d3d12_transfer * trans)720 read_zs_surface(struct d3d12_context *ctx, struct d3d12_resource *res,
721                 const struct pipe_box *box,
722                 struct d3d12_transfer *trans)
723 {
724    pipe_screen *pscreen = ctx->base.screen;
725 
726    prepare_zs_layer_strides(res, box, trans);
727 
728    struct pipe_resource tmpl;
729    memset(&tmpl, 0, sizeof tmpl);
730    tmpl.target = PIPE_BUFFER;
731    tmpl.format = PIPE_FORMAT_R32_UNORM;
732    tmpl.bind = 0;
733    tmpl.usage = PIPE_USAGE_STAGING;
734    tmpl.flags = 0;
735    tmpl.width0 = trans->base.layer_stride;
736    tmpl.height0 = 1;
737    tmpl.depth0 = 1;
738    tmpl.array_size = 1;
739 
740    local_resource depth_buffer(pscreen, &tmpl);
741    if (!depth_buffer) {
742       debug_printf("Allocating staging buffer for depth failed\n");
743       return NULL;
744    }
745 
746    if (!transfer_image_to_buf(ctx, res, depth_buffer, trans, 0))
747       return NULL;
748 
749    tmpl.format = PIPE_FORMAT_R8_UINT;
750 
751    local_resource stencil_buffer(pscreen, &tmpl);
752    if (!stencil_buffer) {
753       debug_printf("Allocating staging buffer for stencilfailed\n");
754       return NULL;
755    }
756 
757    if (!transfer_image_to_buf(ctx, res, stencil_buffer, trans, 1))
758       return NULL;
759 
760    d3d12_flush_cmdlist_and_wait(ctx);
761 
762    void *depth_ptr = depth_buffer.map();
763    if (!depth_ptr) {
764       debug_printf("Mapping staging depth buffer failed\n");
765       return NULL;
766    }
767 
768    uint8_t *stencil_ptr =  (uint8_t *)stencil_buffer.map();
769    if (!stencil_ptr) {
770       debug_printf("Mapping staging stencil buffer failed\n");
771       return NULL;
772    }
773 
774    uint8_t *buf = (uint8_t *)malloc(trans->base.layer_stride);
775    if (!buf)
776       return NULL;
777 
778    trans->data = buf;
779 
780    switch (res->base.format) {
781    case PIPE_FORMAT_Z24_UNORM_S8_UINT:
782       util_format_z24_unorm_s8_uint_pack_separate(buf, trans->base.stride,
783                                                   (uint32_t *)depth_ptr, trans->base.stride,
784                                                   stencil_ptr, trans->base.stride,
785                                                   trans->base.box.width, trans->base.box.height);
786       break;
787    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
788       util_format_z32_float_s8x24_uint_pack_z_float(buf, trans->base.stride,
789                                                     (float *)depth_ptr, trans->base.stride,
790                                                     trans->base.box.width, trans->base.box.height);
791       util_format_z32_float_s8x24_uint_pack_s_8uint(buf, trans->base.stride,
792                                                     stencil_ptr, trans->base.stride,
793                                                     trans->base.box.width, trans->base.box.height);
794       break;
795    default:
796       unreachable("Unsupported depth steancil format");
797    };
798 
799    return trans->data;
800 }
801 
802 static void *
prepare_write_zs_surface(struct d3d12_resource * res,const struct pipe_box * box,struct d3d12_transfer * trans)803 prepare_write_zs_surface(struct d3d12_resource *res,
804                          const struct pipe_box *box,
805                          struct d3d12_transfer *trans)
806 {
807    prepare_zs_layer_strides(res, box, trans);
808    uint32_t *buf = (uint32_t *)malloc(trans->base.layer_stride);
809    if (!buf)
810       return NULL;
811 
812    trans->data = buf;
813    return trans->data;
814 }
815 
816 static void
write_zs_surface(struct pipe_context * pctx,struct d3d12_resource * res,struct d3d12_transfer * trans)817 write_zs_surface(struct pipe_context *pctx, struct d3d12_resource *res,
818                  struct d3d12_transfer *trans)
819 {
820    struct pipe_resource tmpl;
821    memset(&tmpl, 0, sizeof tmpl);
822    tmpl.target = PIPE_BUFFER;
823    tmpl.format = PIPE_FORMAT_R32_UNORM;
824    tmpl.bind = 0;
825    tmpl.usage = PIPE_USAGE_STAGING;
826    tmpl.flags = 0;
827    tmpl.width0 = trans->base.layer_stride;
828    tmpl.height0 = 1;
829    tmpl.depth0 = 1;
830    tmpl.array_size = 1;
831 
832    local_resource depth_buffer(pctx->screen, &tmpl);
833    if (!depth_buffer) {
834       debug_printf("Allocating staging buffer for depth failed\n");
835       return;
836    }
837 
838    local_resource stencil_buffer(pctx->screen, &tmpl);
839    if (!stencil_buffer) {
840       debug_printf("Allocating staging buffer for depth failed\n");
841       return;
842    }
843 
844    void *depth_ptr = depth_buffer.map();
845    if (!depth_ptr) {
846       debug_printf("Mapping staging depth buffer failed\n");
847       return;
848    }
849 
850    uint8_t *stencil_ptr =  (uint8_t *)stencil_buffer.map();
851    if (!stencil_ptr) {
852       debug_printf("Mapping staging stencil buffer failed\n");
853       return;
854    }
855 
856    switch (res->base.format) {
857    case PIPE_FORMAT_Z24_UNORM_S8_UINT:
858       util_format_z32_unorm_unpack_z_32unorm((uint32_t *)depth_ptr, trans->base.stride, (uint8_t*)trans->data,
859                                              trans->base.stride, trans->base.box.width,
860                                              trans->base.box.height);
861       util_format_z24_unorm_s8_uint_unpack_s_8uint(stencil_ptr, trans->base.stride, (uint8_t*)trans->data,
862                                                    trans->base.stride, trans->base.box.width,
863                                                    trans->base.box.height);
864       break;
865    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
866       util_format_z32_float_s8x24_uint_unpack_z_float((float *)depth_ptr, trans->base.stride, (uint8_t*)trans->data,
867                                                       trans->base.stride, trans->base.box.width,
868                                                       trans->base.box.height);
869       util_format_z32_float_s8x24_uint_unpack_s_8uint(stencil_ptr, trans->base.stride, (uint8_t*)trans->data,
870                                                       trans->base.stride, trans->base.box.width,
871                                                       trans->base.box.height);
872       break;
873    default:
874       unreachable("Unsupported depth steancil format");
875    };
876 
877    stencil_buffer.unmap();
878    depth_buffer.unmap();
879 
880    transfer_buf_to_image(d3d12_context(pctx), res, depth_buffer, trans, 0);
881    transfer_buf_to_image(d3d12_context(pctx), res, stencil_buffer, trans, 1);
882 }
883 
884 #define BUFFER_MAP_ALIGNMENT 64
885 
886 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)887 d3d12_transfer_map(struct pipe_context *pctx,
888                    struct pipe_resource *pres,
889                    unsigned level,
890                    unsigned usage,
891                    const struct pipe_box *box,
892                    struct pipe_transfer **transfer)
893 {
894    struct d3d12_context *ctx = d3d12_context(pctx);
895    struct d3d12_resource *res = d3d12_resource(pres);
896 
897    if (usage & PIPE_MAP_DIRECTLY || !res->bo)
898       return NULL;
899 
900    struct d3d12_transfer *trans = (struct d3d12_transfer *)slab_alloc(&ctx->transfer_pool);
901    struct pipe_transfer *ptrans = &trans->base;
902    if (!trans)
903       return NULL;
904 
905    memset(trans, 0, sizeof(*trans));
906    pipe_resource_reference(&ptrans->resource, pres);
907 
908    ptrans->resource = pres;
909    ptrans->level = level;
910    ptrans->usage = (enum pipe_map_flags)usage;
911    ptrans->box = *box;
912 
913    D3D12_RANGE range;
914    range.Begin = 0;
915 
916    void *ptr;
917    if (can_map_directly(&res->base)) {
918       if (pres->target == PIPE_BUFFER) {
919          ptrans->stride = 0;
920          ptrans->layer_stride = 0;
921       } else {
922          ptrans->stride = util_format_get_stride(pres->format, box->width);
923          ptrans->layer_stride = util_format_get_2d_size(pres->format,
924                                                         ptrans->stride,
925                                                         box->height);
926       }
927 
928       range = linear_range(box, ptrans->stride, ptrans->layer_stride);
929       if (!synchronize(ctx, res, usage, &range))
930          return NULL;
931       ptr = d3d12_bo_map(res->bo, &range);
932    } else if (unlikely(pres->format == PIPE_FORMAT_Z24_UNORM_S8_UINT ||
933                        pres->format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT)) {
934       if (usage & PIPE_MAP_READ) {
935          ptr = read_zs_surface(ctx, res, box, trans);
936       } else if (usage & PIPE_MAP_WRITE){
937          ptr = prepare_write_zs_surface(res, box, trans);
938       } else {
939          ptr = nullptr;
940       }
941    } else {
942       ptrans->stride = align(util_format_get_stride(pres->format, box->width),
943                               D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
944       ptrans->layer_stride = util_format_get_2d_size(pres->format,
945                                                      ptrans->stride,
946                                                      box->height);
947 
948       if (res->base.target != PIPE_TEXTURE_3D)
949          ptrans->layer_stride = align(ptrans->layer_stride,
950                                       D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
951 
952       unsigned staging_res_size = ptrans->layer_stride * box->depth;
953       if (res->base.target == PIPE_BUFFER) {
954          /* To properly support ARB_map_buffer_alignment, we need to return a pointer
955           * that's appropriately offset from a 64-byte-aligned base address.
956           */
957          assert(box->x >= 0);
958          unsigned aligned_x = (unsigned)box->x % BUFFER_MAP_ALIGNMENT;
959          staging_res_size = align(box->width + aligned_x,
960                                   D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
961          range.Begin = aligned_x;
962       }
963 
964       pipe_resource_usage staging_usage = (usage & (PIPE_MAP_READ | PIPE_MAP_READ_WRITE)) ?
965          PIPE_USAGE_STAGING : PIPE_USAGE_STREAM;
966 
967       trans->staging_res = pipe_buffer_create(pctx->screen, 0,
968                                               staging_usage,
969                                               staging_res_size);
970       if (!trans->staging_res)
971          return NULL;
972 
973       struct d3d12_resource *staging_res = d3d12_resource(trans->staging_res);
974 
975       if (usage & PIPE_MAP_READ) {
976          bool ret = true;
977          if (pres->target == PIPE_BUFFER) {
978             uint64_t src_offset = box->x;
979             uint64_t dst_offset = src_offset % BUFFER_MAP_ALIGNMENT;
980             transfer_buf_to_buf(ctx, res, staging_res, src_offset, dst_offset, box->width);
981          } else
982             ret = transfer_image_to_buf(ctx, res, staging_res, trans, 0);
983          if (!ret)
984             return NULL;
985          d3d12_flush_cmdlist_and_wait(ctx);
986       }
987 
988       range.End = staging_res_size - range.Begin;
989 
990       ptr = d3d12_bo_map(staging_res->bo, &range);
991    }
992 
993    *transfer = ptrans;
994    return ptr;
995 }
996 
997 static void
d3d12_transfer_unmap(struct pipe_context * pctx,struct pipe_transfer * ptrans)998 d3d12_transfer_unmap(struct pipe_context *pctx,
999                      struct pipe_transfer *ptrans)
1000 {
1001    struct d3d12_resource *res = d3d12_resource(ptrans->resource);
1002    struct d3d12_transfer *trans = (struct d3d12_transfer *)ptrans;
1003    D3D12_RANGE range = { 0, 0 };
1004 
1005    if (trans->data != nullptr) {
1006       if (trans->base.usage & PIPE_MAP_WRITE)
1007          write_zs_surface(pctx, res, trans);
1008       free(trans->data);
1009    } else if (trans->staging_res) {
1010       struct d3d12_resource *staging_res = d3d12_resource(trans->staging_res);
1011 
1012       if (trans->base.usage & PIPE_MAP_WRITE) {
1013          assert(ptrans->box.x >= 0);
1014          range.Begin = res->base.target == PIPE_BUFFER ?
1015             (unsigned)ptrans->box.x % BUFFER_MAP_ALIGNMENT : 0;
1016          range.End = staging_res->base.width0 - range.Begin;
1017       }
1018       d3d12_bo_unmap(staging_res->bo, &range);
1019 
1020       if (trans->base.usage & PIPE_MAP_WRITE) {
1021          struct d3d12_context *ctx = d3d12_context(pctx);
1022          if (res->base.target == PIPE_BUFFER) {
1023             uint64_t dst_offset = trans->base.box.x;
1024             uint64_t src_offset = dst_offset % BUFFER_MAP_ALIGNMENT;
1025             transfer_buf_to_buf(ctx, staging_res, res, src_offset, dst_offset, ptrans->box.width);
1026          } else
1027             transfer_buf_to_image(ctx, res, staging_res, trans, 0);
1028       }
1029 
1030       pipe_resource_reference(&trans->staging_res, NULL);
1031    } else {
1032       if (trans->base.usage & PIPE_MAP_WRITE) {
1033          range.Begin = ptrans->box.x;
1034          range.End = ptrans->box.x + ptrans->box.width;
1035       }
1036       d3d12_bo_unmap(res->bo, &range);
1037    }
1038 
1039    pipe_resource_reference(&ptrans->resource, NULL);
1040    slab_free(&d3d12_context(pctx)->transfer_pool, ptrans);
1041 }
1042 
1043 void
d3d12_resource_make_writeable(struct pipe_context * pctx,struct pipe_resource * pres)1044 d3d12_resource_make_writeable(struct pipe_context *pctx,
1045                               struct pipe_resource *pres)
1046 {
1047    struct d3d12_context *ctx = d3d12_context(pctx);
1048    struct d3d12_resource *res = d3d12_resource(pres);
1049    struct d3d12_resource *dup_res;
1050 
1051    if (!res->bo || !d3d12_bo_is_suballocated(res->bo))
1052       return;
1053 
1054    dup_res = d3d12_resource(pipe_buffer_create(pres->screen,
1055                                                pres->bind & PIPE_BIND_STREAM_OUTPUT,
1056                                                (pipe_resource_usage) pres->usage,
1057                                                pres->width0));
1058 
1059    if (res->valid_buffer_range.end > res->valid_buffer_range.start) {
1060       struct pipe_box box;
1061 
1062       box.x = res->valid_buffer_range.start;
1063       box.y = 0;
1064       box.z = 0;
1065       box.width = res->valid_buffer_range.end - res->valid_buffer_range.start;
1066       box.height = 1;
1067       box.depth = 1;
1068 
1069       d3d12_direct_copy(ctx, dup_res, 0, &box, res, 0, &box, PIPE_MASK_RGBAZS);
1070    }
1071 
1072    /* Move new BO to old resource */
1073    d3d12_bo_unreference(res->bo);
1074    res->bo = dup_res->bo;
1075    d3d12_bo_reference(res->bo);
1076 
1077    d3d12_resource_destroy(dup_res->base.screen, &dup_res->base);
1078 }
1079 
1080 void
d3d12_context_resource_init(struct pipe_context * pctx)1081 d3d12_context_resource_init(struct pipe_context *pctx)
1082 {
1083    pctx->buffer_map = d3d12_transfer_map;
1084    pctx->buffer_unmap = d3d12_transfer_unmap;
1085    pctx->texture_map = d3d12_transfer_map;
1086    pctx->texture_unmap = d3d12_transfer_unmap;
1087 
1088    pctx->transfer_flush_region = u_default_transfer_flush_region;
1089    pctx->buffer_subdata = u_default_buffer_subdata;
1090    pctx->texture_subdata = u_default_texture_subdata;
1091 }
1092