xref: /aosp_15_r20/external/mesa3d/src/gallium/auxiliary/util/u_transfer_helper.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2017 Red Hat
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 FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 
24 #include "pipe/p_screen.h"
25 
26 #include "util/box.h"
27 #include "util/format/u_format.h"
28 #include "util/format/u_format_zs.h"
29 #include "util/u_inlines.h"
30 #include "util/u_transfer_helper.h"
31 
32 
33 struct u_transfer_helper {
34    const struct u_transfer_vtbl *vtbl;
35    bool separate_z32s8; /**< separate z32 and s8 */
36    bool separate_stencil; /**< separate stencil for all formats */
37    bool msaa_map;
38    bool z24_in_z32f; /* the z24 values are stored in a z32 - translate them. */
39    bool interleave_in_place;
40 };
41 
42 /* If we need to take the path for PIPE_MAP_DEPTH/STENCIL_ONLY on the parent
43  * depth/stencil resource an interleaving those to/from a staging buffer. The
44  * other path for z/s interleave is when separate z and s resources are
45  * created at resource create time.
46  */
needs_in_place_zs_interleave(struct u_transfer_helper * helper,enum pipe_format format)47 static inline bool needs_in_place_zs_interleave(struct u_transfer_helper *helper,
48                                                 enum pipe_format format)
49 {
50    if (!helper->interleave_in_place)
51       return false;
52    if (helper->separate_stencil && util_format_is_depth_and_stencil(format))
53       return true;
54    if (helper->separate_z32s8 && format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT)
55       return true;
56    /* this isn't interleaving, but still needs conversions on that path. */
57    if (helper->z24_in_z32f && format == PIPE_FORMAT_Z24X8_UNORM)
58       return true;
59    return false;
60 }
61 
handle_transfer(struct pipe_resource * prsc)62 static inline bool handle_transfer(struct pipe_resource *prsc)
63 {
64    struct u_transfer_helper *helper = prsc->screen->transfer_helper;
65 
66    if (helper->vtbl->get_internal_format) {
67       enum pipe_format internal_format =
68             helper->vtbl->get_internal_format(prsc);
69       if (internal_format != prsc->format)
70          return true;
71    }
72 
73    if (helper->msaa_map && (prsc->nr_samples > 1))
74       return true;
75 
76    if (needs_in_place_zs_interleave(helper, prsc->format))
77       return true;
78 
79    return false;
80 }
81 
82 /* The pipe_transfer ptr could either be the driver's, or u_transfer,
83  * depending on whether we are intervening or not.  Check handle_transfer()
84  * before dereferencing.
85  */
86 struct u_transfer {
87    struct pipe_transfer base;
88    /* Note that in case of MSAA resolve for transfer plus z32s8
89     * we end up with stacked u_transfer's.  The MSAA resolve case doesn't call
90     * helper->vtbl fxns directly, but calls back to pctx->transfer_map()/etc
91     * so the format related handling can work in conjunction with MSAA resolve.
92     */
93    struct pipe_transfer *trans;   /* driver's transfer */
94    struct pipe_transfer *trans2;  /* 2nd transfer for s8 stencil buffer in z32s8 */
95    void *ptr, *ptr2;              /* ptr to trans, and trans2 */
96    void *staging;                 /* staging buffer */
97    struct pipe_resource *ss;      /* staging resource for MSAA resolves */
98 };
99 
100 static inline struct u_transfer *
u_transfer(struct pipe_transfer * ptrans)101 u_transfer(struct pipe_transfer *ptrans)
102 {
103    assert(handle_transfer(ptrans->resource));
104    return (struct u_transfer *)ptrans;
105 }
106 
107 struct pipe_resource *
u_transfer_helper_resource_create(struct pipe_screen * pscreen,const struct pipe_resource * templ)108 u_transfer_helper_resource_create(struct pipe_screen *pscreen,
109                                   const struct pipe_resource *templ)
110 {
111    struct u_transfer_helper *helper = pscreen->transfer_helper;
112    enum pipe_format format = templ->format;
113    struct pipe_resource *prsc;
114 
115    if (((helper->separate_stencil && util_format_is_depth_and_stencil(format)) ||
116         (format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT && helper->separate_z32s8)) &&
117        !helper->interleave_in_place) {
118       struct pipe_resource t = *templ;
119       struct pipe_resource *stencil;
120 
121       t.format = util_format_get_depth_only(format);
122 
123       if (t.format == PIPE_FORMAT_Z24X8_UNORM && helper->z24_in_z32f)
124          t.format = PIPE_FORMAT_Z32_FLOAT;
125 
126       prsc = helper->vtbl->resource_create(pscreen, &t);
127       if (!prsc)
128          return NULL;
129 
130       prsc->format = format;  /* frob the format back to the "external" format */
131 
132       t.format = PIPE_FORMAT_S8_UINT;
133       stencil = helper->vtbl->resource_create(pscreen, &t);
134 
135       if (!stencil) {
136          helper->vtbl->resource_destroy(pscreen, prsc);
137          return NULL;
138       }
139 
140       helper->vtbl->set_stencil(prsc, stencil);
141    } else if (format == PIPE_FORMAT_Z24X8_UNORM && helper->z24_in_z32f) {
142       struct pipe_resource t = *templ;
143       t.format = PIPE_FORMAT_Z32_FLOAT;
144 
145       prsc = helper->vtbl->resource_create(pscreen, &t);
146       if (!prsc)
147          return NULL;
148 
149       prsc->format = format;  /* frob the format back to the "external" format */
150    } else {
151       /* normal case, no special handling: */
152       prsc = helper->vtbl->resource_create(pscreen, templ);
153       if (!prsc)
154          return NULL;
155    }
156 
157    return prsc;
158 }
159 
160 void
u_transfer_helper_resource_destroy(struct pipe_screen * pscreen,struct pipe_resource * prsc)161 u_transfer_helper_resource_destroy(struct pipe_screen *pscreen,
162                                    struct pipe_resource *prsc)
163 {
164    struct u_transfer_helper *helper = pscreen->transfer_helper;
165 
166    if (helper->vtbl->get_stencil && !helper->interleave_in_place) {
167       struct pipe_resource *stencil = helper->vtbl->get_stencil(prsc);
168 
169       pipe_resource_reference(&stencil, NULL);
170    }
171 
172    helper->vtbl->resource_destroy(pscreen, prsc);
173 }
174 
needs_pack(unsigned usage)175 static inline bool needs_pack(unsigned usage)
176 {
177    return (usage & PIPE_MAP_READ) &&
178       !(usage & (PIPE_MAP_DISCARD_WHOLE_RESOURCE | PIPE_MAP_DISCARD_RANGE));
179 }
180 
181 /* In the case of transfer_map of a multi-sample resource, call back into
182  * pctx->transfer_map() to map the staging resource, to handle cases of
183  * MSAA + separate_z32s8
184  */
185 static void *
transfer_map_msaa(struct pipe_context * pctx,struct pipe_resource * prsc,unsigned level,unsigned usage,const struct pipe_box * box,struct pipe_transfer ** pptrans)186 transfer_map_msaa(struct pipe_context *pctx,
187                   struct pipe_resource *prsc,
188                   unsigned level, unsigned usage,
189                   const struct pipe_box *box,
190                   struct pipe_transfer **pptrans)
191 {
192    struct pipe_screen *pscreen = pctx->screen;
193    struct u_transfer *trans = calloc(1, sizeof(*trans));
194    if (!trans)
195       return NULL;
196    struct pipe_transfer *ptrans = &trans->base;
197    bool need_pack = needs_pack(usage);
198 
199    pipe_resource_reference(&ptrans->resource, prsc);
200    ptrans->level = level;
201    ptrans->usage = usage;
202    ptrans->box = *box;
203 
204    struct pipe_resource tmpl = {
205          .target = prsc->target,
206          .format = prsc->format,
207          .width0 = box->width,
208          .height0 = box->height,
209          .depth0 = 1,
210          .array_size = 1,
211          .usage = need_pack ? PIPE_USAGE_STAGING : 0,
212    };
213    if (util_format_is_depth_or_stencil(tmpl.format))
214       tmpl.bind |= PIPE_BIND_DEPTH_STENCIL;
215    else
216       tmpl.bind |= PIPE_BIND_RENDER_TARGET;
217    trans->ss = pscreen->resource_create(pscreen, &tmpl);
218    if (!trans->ss) {
219       free(trans);
220       return NULL;
221    }
222 
223    if (need_pack) {
224       struct pipe_blit_info blit;
225       memset(&blit, 0, sizeof(blit));
226 
227       blit.src.resource = ptrans->resource;
228       blit.src.format = ptrans->resource->format;
229       blit.src.level = ptrans->level;
230       blit.src.box = *box;
231 
232       blit.dst.resource = trans->ss;
233       blit.dst.format = trans->ss->format;
234       blit.dst.box.width = box->width;
235       blit.dst.box.height = box->height;
236       blit.dst.box.depth = 1;
237 
238       blit.mask = util_format_get_mask(prsc->format);
239       blit.filter = PIPE_TEX_FILTER_NEAREST;
240 
241       pctx->blit(pctx, &blit);
242    }
243 
244    struct pipe_box map_box = *box;
245    map_box.x = 0;
246    map_box.y = 0;
247 
248    void *ss_map = pctx->texture_map(pctx, trans->ss, 0, usage, &map_box,
249          &trans->trans);
250    if (!ss_map) {
251       free(trans);
252       return NULL;
253    }
254 
255    ptrans->stride = trans->trans->stride;
256    *pptrans = ptrans;
257    return ss_map;
258 }
259 
260 void *
u_transfer_helper_transfer_map(struct pipe_context * pctx,struct pipe_resource * prsc,unsigned level,unsigned usage,const struct pipe_box * box,struct pipe_transfer ** pptrans)261 u_transfer_helper_transfer_map(struct pipe_context *pctx,
262                                struct pipe_resource *prsc,
263                                unsigned level, unsigned usage,
264                                const struct pipe_box *box,
265                                struct pipe_transfer **pptrans)
266 {
267    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
268    struct u_transfer *trans;
269    struct pipe_transfer *ptrans;
270    enum pipe_format format = prsc->format;
271    unsigned width = box->width;
272    unsigned height = box->height;
273    bool in_place_zs_interleave = needs_in_place_zs_interleave(helper, format);
274 
275    if (!handle_transfer(prsc))
276       return helper->vtbl->transfer_map(pctx, prsc, level, usage, box, pptrans);
277 
278    if (helper->msaa_map && (prsc->nr_samples > 1))
279       return transfer_map_msaa(pctx, prsc, level, usage, box, pptrans);
280 
281    assert(box->depth == 1);
282 
283    trans = calloc(1, sizeof(*trans));
284    if (!trans)
285       return NULL;
286 
287    ptrans = &trans->base;
288    pipe_resource_reference(&ptrans->resource, prsc);
289    ptrans->level = level;
290    ptrans->usage = usage;
291    ptrans->box   = *box;
292    ptrans->stride = util_format_get_stride(format, box->width);
293    ptrans->layer_stride = (uint64_t)ptrans->stride * box->height;
294 
295    trans->staging = malloc(ptrans->layer_stride);
296    if (!trans->staging)
297       goto fail;
298 
299    trans->ptr = helper->vtbl->transfer_map(pctx, prsc, level,
300                                            usage | (in_place_zs_interleave ? PIPE_MAP_DEPTH_ONLY : 0),
301                                            box, &trans->trans);
302    if (!trans->ptr)
303       goto fail;
304 
305    if (util_format_is_depth_and_stencil(prsc->format)) {
306       struct pipe_resource *stencil;
307 
308       if (in_place_zs_interleave)
309          stencil = prsc;
310      else
311          stencil = helper->vtbl->get_stencil(prsc);
312       trans->ptr2 = helper->vtbl->transfer_map(pctx, stencil, level,
313                                                usage | (in_place_zs_interleave ? PIPE_MAP_STENCIL_ONLY : 0),
314                                                box, &trans->trans2);
315 
316       if (needs_pack(usage)) {
317          switch (prsc->format) {
318          case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
319             util_format_z32_float_s8x24_uint_pack_z_float(trans->staging,
320                                                           ptrans->stride,
321                                                           trans->ptr,
322                                                           trans->trans->stride,
323                                                           width, height);
324             util_format_z32_float_s8x24_uint_pack_s_8uint(trans->staging,
325                                                           ptrans->stride,
326                                                           trans->ptr2,
327                                                           trans->trans2->stride,
328                                                           width, height);
329             break;
330          case PIPE_FORMAT_Z24_UNORM_S8_UINT:
331             if (in_place_zs_interleave) {
332                if (helper->z24_in_z32f) {
333                   util_format_z24_unorm_s8_uint_pack_separate_z32(trans->staging,
334                                                                   ptrans->stride,
335                                                                   trans->ptr,
336                                                                   trans->trans->stride,
337                                                                   trans->ptr2,
338                                                                   trans->trans2->stride,
339                                                                   width, height);
340                } else {
341                   util_format_z24_unorm_s8_uint_pack_separate(trans->staging,
342                                                              ptrans->stride,
343                                                              trans->ptr,
344                                                              trans->trans->stride,
345                                                              trans->ptr2,
346                                                              trans->trans2->stride,
347                                                              width, height);
348                }
349             } else {
350                if (helper->z24_in_z32f) {
351                   util_format_z24_unorm_s8_uint_pack_z_float(trans->staging,
352                                                              ptrans->stride,
353                                                              trans->ptr,
354                                                              trans->trans->stride,
355                                                              width, height);
356                   util_format_z24_unorm_s8_uint_pack_s_8uint(trans->staging,
357                                                              ptrans->stride,
358                                                              trans->ptr2,
359                                                              trans->trans2->stride,
360                                                              width, height);
361                } else {
362                   util_format_z24_unorm_s8_uint_pack_separate(trans->staging,
363                                                               ptrans->stride,
364                                                               trans->ptr,
365                                                               trans->trans->stride,
366                                                               trans->ptr2,
367                                                               trans->trans2->stride,
368                                                               width, height);
369                }
370             }
371             break;
372          case PIPE_FORMAT_Z24X8_UNORM:
373             assert(helper->z24_in_z32f);
374             util_format_z24x8_unorm_pack_z_float(trans->staging, ptrans->stride,
375                                                 trans->ptr, trans->trans->stride,
376                                                 width, height);
377             break;
378          default:
379             unreachable("Unexpected format");
380          }
381       }
382    } else if (prsc->format == PIPE_FORMAT_Z24X8_UNORM) {
383          assert(helper->z24_in_z32f);
384          util_format_z24x8_unorm_pack_z_float(trans->staging, ptrans->stride,
385                                               trans->ptr, trans->trans->stride,
386                                               width, height);
387    } else {
388       unreachable("bleh");
389    }
390 
391    *pptrans = ptrans;
392    return trans->staging;
393 
394 fail:
395    if (trans->trans)
396       helper->vtbl->transfer_unmap(pctx, trans->trans);
397    if (trans->trans2)
398       helper->vtbl->transfer_unmap(pctx, trans->trans2);
399    pipe_resource_reference(&ptrans->resource, NULL);
400    free(trans->staging);
401    free(trans);
402    return NULL;
403 }
404 
405 static void
flush_region(struct pipe_context * pctx,struct pipe_transfer * ptrans,const struct pipe_box * box)406 flush_region(struct pipe_context *pctx, struct pipe_transfer *ptrans,
407              const struct pipe_box *box)
408 {
409    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
410    /* using the function here hits an assert for the deinterleave cases */
411    struct u_transfer *trans = (struct u_transfer *)ptrans;
412    enum pipe_format iformat, format = ptrans->resource->format;
413    unsigned width = box->width;
414    unsigned height = box->height;
415    void *src, *dst;
416 
417    if (!(ptrans->usage & PIPE_MAP_WRITE))
418       return;
419 
420    if (trans->ss) {
421       struct pipe_blit_info blit;
422       memset(&blit, 0, sizeof(blit));
423 
424       blit.src.resource = trans->ss;
425       blit.src.format = trans->ss->format;
426       blit.src.box = *box;
427 
428       blit.dst.resource = ptrans->resource;
429       blit.dst.format = ptrans->resource->format;
430       blit.dst.level = ptrans->level;
431 
432       u_box_2d(ptrans->box.x + box->x,
433                ptrans->box.y + box->y,
434                box->width, box->height,
435                &blit.dst.box);
436 
437       blit.mask = util_format_get_mask(ptrans->resource->format);
438       blit.filter = PIPE_TEX_FILTER_NEAREST;
439 
440       pctx->blit(pctx, &blit);
441 
442       return;
443    }
444 
445    iformat = helper->vtbl->get_internal_format(ptrans->resource);
446 
447    src = (uint8_t *)trans->staging +
448          (box->y * ptrans->stride) +
449          (box->x * util_format_get_blocksize(format));
450    dst = (uint8_t *)trans->ptr +
451          (box->y * trans->trans->stride) +
452          (box->x * util_format_get_blocksize(iformat));
453 
454    switch (format) {
455    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
456       util_format_z32_float_s8x24_uint_unpack_z_float(dst,
457                                                       trans->trans->stride,
458                                                       src,
459                                                       ptrans->stride,
460                                                       width, height);
461       FALLTHROUGH;
462    case PIPE_FORMAT_X32_S8X24_UINT:
463       dst = (uint8_t *)trans->ptr2 +
464             (box->y * trans->trans2->stride) +
465             (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT));
466 
467       util_format_z32_float_s8x24_uint_unpack_s_8uint(dst,
468                                                       trans->trans2->stride,
469                                                       src,
470                                                       ptrans->stride,
471                                                       width, height);
472       break;
473    case PIPE_FORMAT_Z24X8_UNORM:
474       util_format_z24x8_unorm_unpack_z_float(dst, trans->trans->stride,
475                                              src, ptrans->stride,
476                                              width, height);
477       break;
478    case PIPE_FORMAT_Z24_UNORM_S8_UINT:
479       if (helper->z24_in_z32f) {
480          util_format_z24_unorm_s8_uint_unpack_z_float(dst, trans->trans->stride,
481                                                       src, ptrans->stride,
482                                                       width, height);
483       } else {
484          /* just do a strided 32-bit copy for depth; s8 can become garbage x8 */
485          util_format_z32_unorm_unpack_z_32unorm(dst, trans->trans->stride,
486                                                 src, ptrans->stride,
487                                                 width, height);
488       }
489       FALLTHROUGH;
490    case PIPE_FORMAT_X24S8_UINT:
491       dst = (uint8_t *)trans->ptr2 +
492             (box->y * trans->trans2->stride) +
493             (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT));
494 
495       util_format_z24_unorm_s8_uint_unpack_s_8uint(dst, trans->trans2->stride,
496                                                    src, ptrans->stride,
497                                                    width, height);
498       break;
499 
500    default:
501       assert(!"Unexpected staging transfer type");
502       break;
503    }
504 }
505 
506 void
u_transfer_helper_transfer_flush_region(struct pipe_context * pctx,struct pipe_transfer * ptrans,const struct pipe_box * box)507 u_transfer_helper_transfer_flush_region(struct pipe_context *pctx,
508                                         struct pipe_transfer *ptrans,
509                                         const struct pipe_box *box)
510 {
511    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
512 
513    if (handle_transfer(ptrans->resource)) {
514       struct u_transfer *trans = u_transfer(ptrans);
515 
516       /* handle MSAA case, since there could be multiple levels of
517        * wrapped transfer, call pctx->transfer_flush_region()
518        * instead of helper->vtbl->transfer_flush_region()
519        */
520       if (trans->ss) {
521          pctx->transfer_flush_region(pctx, trans->trans, box);
522          flush_region(pctx, ptrans, box);
523          return;
524       }
525 
526       flush_region(pctx, ptrans, box);
527 
528       helper->vtbl->transfer_flush_region(pctx, trans->trans, box);
529       if (trans->trans2)
530          helper->vtbl->transfer_flush_region(pctx, trans->trans2, box);
531 
532    } else {
533       helper->vtbl->transfer_flush_region(pctx, ptrans, box);
534    }
535 }
536 
537 void
u_transfer_helper_transfer_unmap(struct pipe_context * pctx,struct pipe_transfer * ptrans)538 u_transfer_helper_transfer_unmap(struct pipe_context *pctx,
539                                  struct pipe_transfer *ptrans)
540 {
541    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
542 
543    if (handle_transfer(ptrans->resource)) {
544       struct u_transfer *trans = u_transfer(ptrans);
545 
546       if (!(ptrans->usage & PIPE_MAP_FLUSH_EXPLICIT)) {
547          struct pipe_box box;
548          u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box);
549          if (trans->ss)
550             pctx->transfer_flush_region(pctx, trans->trans, &box);
551          flush_region(pctx, ptrans, &box);
552       }
553 
554       /* in MSAA case, there could be multiple levels of wrapping
555        * so don't call helper->vtbl->transfer_unmap() directly
556        */
557       if (trans->ss) {
558          pctx->texture_unmap(pctx, trans->trans);
559          pipe_resource_reference(&trans->ss, NULL);
560       } else {
561          helper->vtbl->transfer_unmap(pctx, trans->trans);
562          if (trans->trans2)
563             helper->vtbl->transfer_unmap(pctx, trans->trans2);
564       }
565 
566       pipe_resource_reference(&ptrans->resource, NULL);
567 
568       free(trans->staging);
569       free(trans);
570    } else {
571       helper->vtbl->transfer_unmap(pctx, ptrans);
572    }
573 }
574 
575 struct u_transfer_helper *
u_transfer_helper_create(const struct u_transfer_vtbl * vtbl,enum u_transfer_helper_flags flags)576 u_transfer_helper_create(const struct u_transfer_vtbl *vtbl,
577                          enum u_transfer_helper_flags flags)
578 {
579    struct u_transfer_helper *helper = calloc(1, sizeof(*helper));
580 
581    helper->vtbl = vtbl;
582    helper->separate_z32s8 = flags & U_TRANSFER_HELPER_SEPARATE_Z32S8;
583    helper->separate_stencil = flags & U_TRANSFER_HELPER_SEPARATE_STENCIL;
584    helper->msaa_map = flags & U_TRANSFER_HELPER_MSAA_MAP;
585    helper->z24_in_z32f = flags & U_TRANSFER_HELPER_Z24_IN_Z32F;
586    helper->interleave_in_place = flags & U_TRANSFER_HELPER_INTERLEAVE_IN_PLACE;
587 
588    return helper;
589 }
590 
591 void
u_transfer_helper_destroy(struct u_transfer_helper * helper)592 u_transfer_helper_destroy(struct u_transfer_helper *helper)
593 {
594    free(helper);
595 }
596