• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/u_box.h"
27 #include "util/u_format.h"
28 #include "util/u_format_rgtc.h"
29 #include "util/u_format_zs.h"
30 #include "util/u_inlines.h"
31 #include "util/u_transfer_helper.h"
32 
33 
34 struct u_transfer_helper {
35    const struct u_transfer_vtbl *vtbl;
36    bool separate_z32s8;
37    bool fake_rgtc;
38    bool msaa_map;
39 };
40 
handle_transfer(struct pipe_resource * prsc)41 static inline bool handle_transfer(struct pipe_resource *prsc)
42 {
43    struct u_transfer_helper *helper = prsc->screen->transfer_helper;
44 
45    if (helper->vtbl->get_internal_format) {
46       enum pipe_format internal_format =
47             helper->vtbl->get_internal_format(prsc);
48       if (internal_format != prsc->format)
49          return true;
50    }
51 
52    if (helper->msaa_map && (prsc->nr_samples > 1))
53       return true;
54 
55    return false;
56 }
57 
58 /* The pipe_transfer ptr could either be the driver's, or u_transfer,
59  * depending on whether we are intervening or not.  Check handle_transfer()
60  * before dereferencing.
61  */
62 struct u_transfer {
63    struct pipe_transfer base;
64    /* Note that in case of MSAA resolve for transfer plus z32s8 or fake rgtc
65     * we end up with stacked u_transfer's.  The MSAA resolve case doesn't call
66     * helper->vtbl fxns directly, but calls back to pctx->transfer_map()/etc
67     * so the format related handling can work in conjunction with MSAA resolve.
68     */
69    struct pipe_transfer *trans;   /* driver's transfer */
70    struct pipe_transfer *trans2;  /* 2nd transfer for s8 stencil buffer in z32s8 */
71    void *ptr, *ptr2;              /* ptr to trans, and trans2 */
72    void *staging;                 /* staging buffer */
73    struct pipe_resource *ss;      /* staging resource for MSAA resolves */
74 };
75 
76 static inline struct u_transfer *
u_transfer(struct pipe_transfer * ptrans)77 u_transfer(struct pipe_transfer *ptrans)
78 {
79    debug_assert(handle_transfer(ptrans->resource));
80    return (struct u_transfer *)ptrans;
81 }
82 
83 struct pipe_resource *
u_transfer_helper_resource_create(struct pipe_screen * pscreen,const struct pipe_resource * templ)84 u_transfer_helper_resource_create(struct pipe_screen *pscreen,
85                                   const struct pipe_resource *templ)
86 {
87    struct u_transfer_helper *helper = pscreen->transfer_helper;
88    enum pipe_format format = templ->format;
89    struct pipe_resource *prsc;
90 
91    if ((format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT) && helper->separate_z32s8) {
92       struct pipe_resource t = *templ;
93       struct pipe_resource *stencil;
94 
95       t.format = PIPE_FORMAT_Z32_FLOAT;
96 
97       prsc = helper->vtbl->resource_create(pscreen, &t);
98       if (!prsc)
99          return NULL;
100 
101       prsc->format = format;  /* frob the format back to the "external" format */
102 
103       t.format = PIPE_FORMAT_S8_UINT;
104       stencil = helper->vtbl->resource_create(pscreen, &t);
105 
106       if (!stencil) {
107          helper->vtbl->resource_destroy(pscreen, prsc);
108          return NULL;
109       }
110 
111       helper->vtbl->set_stencil(prsc, stencil);
112    } else if ((util_format_description(format)->layout == UTIL_FORMAT_LAYOUT_RGTC) &&
113          helper->fake_rgtc) {
114       struct pipe_resource t = *templ;
115       t.format = PIPE_FORMAT_R8G8B8A8_UNORM;
116 
117       prsc = helper->vtbl->resource_create(pscreen, &t);
118       if (!prsc)
119          return NULL;
120 
121       prsc->format = format;  /* frob the format back to the "external" format */
122    } else {
123       /* normal case, no special handling: */
124       prsc = helper->vtbl->resource_create(pscreen, templ);
125       if (!prsc)
126          return NULL;
127    }
128 
129    return prsc;
130 }
131 
132 void
u_transfer_helper_resource_destroy(struct pipe_screen * pscreen,struct pipe_resource * prsc)133 u_transfer_helper_resource_destroy(struct pipe_screen *pscreen,
134                                    struct pipe_resource *prsc)
135 {
136    struct u_transfer_helper *helper = pscreen->transfer_helper;
137 
138    if (helper->vtbl->get_stencil) {
139       struct pipe_resource *stencil = helper->vtbl->get_stencil(prsc);
140 
141       pipe_resource_reference(&stencil, NULL);
142    }
143 
144    helper->vtbl->resource_destroy(pscreen, prsc);
145 }
146 
needs_pack(unsigned usage)147 static bool needs_pack(unsigned usage)
148 {
149    return (usage & PIPE_TRANSFER_READ) &&
150       !(usage & (PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE | PIPE_TRANSFER_DISCARD_RANGE));
151 }
152 
153 /* In the case of transfer_map of a multi-sample resource, call back into
154  * pctx->transfer_map() to map the staging resource, to handle cases of
155  * MSAA + separate_z32s8 or fake_rgtc
156  */
157 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)158 transfer_map_msaa(struct pipe_context *pctx,
159                   struct pipe_resource *prsc,
160                   unsigned level, unsigned usage,
161                   const struct pipe_box *box,
162                   struct pipe_transfer **pptrans)
163 {
164    struct pipe_screen *pscreen = pctx->screen;
165    struct u_transfer *trans = calloc(1, sizeof(*trans));
166    if (!trans)
167       return NULL;
168    struct pipe_transfer *ptrans = &trans->base;
169 
170    pipe_resource_reference(&ptrans->resource, prsc);
171    ptrans->level = level;
172    ptrans->usage = usage;
173    ptrans->box = *box;
174 
175    struct pipe_resource tmpl = {
176          .target = prsc->target,
177          .format = prsc->format,
178          .width0 = box->width,
179          .height0 = box->height,
180          .depth0 = 1,
181          .array_size = 1,
182    };
183    trans->ss = pscreen->resource_create(pscreen, &tmpl);
184    if (!trans->ss) {
185       free(trans);
186       return NULL;
187    }
188 
189    if (needs_pack(usage)) {
190       struct pipe_blit_info blit;
191       memset(&blit, 0, sizeof(blit));
192 
193       blit.src.resource = ptrans->resource;
194       blit.src.format = ptrans->resource->format;
195       blit.src.level = ptrans->level;
196       blit.src.box = *box;
197 
198       blit.dst.resource = trans->ss;
199       blit.dst.format = trans->ss->format;
200       blit.dst.box.width = box->width;
201       blit.dst.box.height = box->height;
202       blit.dst.box.depth = 1;
203 
204       blit.mask = util_format_get_mask(prsc->format);
205       blit.filter = PIPE_TEX_FILTER_NEAREST;
206 
207       pctx->blit(pctx, &blit);
208    }
209 
210    void *ss_map = pctx->transfer_map(pctx, trans->ss, 0, usage, box,
211          &trans->trans);
212    if (!ss_map) {
213       free(trans);
214       return NULL;
215    }
216 
217    *pptrans = ptrans;
218    return ss_map;
219 }
220 
221 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)222 u_transfer_helper_transfer_map(struct pipe_context *pctx,
223                                struct pipe_resource *prsc,
224                                unsigned level, unsigned usage,
225                                const struct pipe_box *box,
226                                struct pipe_transfer **pptrans)
227 {
228    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
229    struct u_transfer *trans;
230    struct pipe_transfer *ptrans;
231    enum pipe_format format = prsc->format;
232    unsigned width = box->width;
233    unsigned height = box->height;
234 
235    if (!handle_transfer(prsc))
236       return helper->vtbl->transfer_map(pctx, prsc, level, usage, box, pptrans);
237 
238    if (helper->msaa_map && (prsc->nr_samples > 1))
239       return transfer_map_msaa(pctx, prsc, level, usage, box, pptrans);
240 
241    debug_assert(box->depth == 1);
242 
243    trans = calloc(1, sizeof(*trans));
244    if (!trans)
245       return NULL;
246 
247    ptrans = &trans->base;
248    pipe_resource_reference(&ptrans->resource, prsc);
249    ptrans->level = level;
250    ptrans->usage = usage;
251    ptrans->box   = *box;
252    ptrans->stride = util_format_get_stride(format, box->width);
253    ptrans->layer_stride = ptrans->stride * box->height;
254 
255    trans->staging = malloc(ptrans->layer_stride);
256    if (!trans->staging)
257       goto fail;
258 
259    trans->ptr = helper->vtbl->transfer_map(pctx, prsc, level, usage, box,
260                                            &trans->trans);
261    if (!trans->ptr)
262       goto fail;
263 
264    if (prsc->format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT) {
265       struct pipe_resource *stencil = helper->vtbl->get_stencil(prsc);
266       trans->ptr2 = helper->vtbl->transfer_map(pctx, stencil, level,
267                                                usage, box, &trans->trans2);
268 
269       if (needs_pack(usage)) {
270          util_format_z32_float_s8x24_uint_pack_z_float(trans->staging,
271                                                        ptrans->stride,
272                                                        trans->ptr,
273                                                        trans->trans->stride,
274                                                        width, height);
275          util_format_z32_float_s8x24_uint_pack_s_8uint(trans->staging,
276                                                        ptrans->stride,
277                                                        trans->ptr2,
278                                                        trans->trans2->stride,
279                                                        width, height);
280       }
281    } else if (needs_pack(usage) &&
282               util_format_description(prsc->format)->layout == UTIL_FORMAT_LAYOUT_RGTC) {
283       switch (prsc->format) {
284       case PIPE_FORMAT_RGTC1_UNORM:
285       case PIPE_FORMAT_RGTC1_SNORM:
286       case PIPE_FORMAT_LATC1_UNORM:
287       case PIPE_FORMAT_LATC1_SNORM:
288          util_format_rgtc1_unorm_pack_rgba_8unorm(trans->staging,
289                                                   ptrans->stride,
290                                                   trans->ptr,
291                                                   trans->trans->stride,
292                                                   width, height);
293          break;
294       case PIPE_FORMAT_RGTC2_UNORM:
295       case PIPE_FORMAT_RGTC2_SNORM:
296       case PIPE_FORMAT_LATC2_UNORM:
297       case PIPE_FORMAT_LATC2_SNORM:
298          util_format_rgtc2_unorm_pack_rgba_8unorm(trans->staging,
299                                                   ptrans->stride,
300                                                   trans->ptr,
301                                                   trans->trans->stride,
302                                                   width, height);
303          break;
304       default:
305          assert(!"Unexpected format");
306          break;
307       }
308    } else {
309       unreachable("bleh");
310    }
311 
312    *pptrans = ptrans;
313    return trans->staging;
314 
315 fail:
316    if (trans->trans)
317       helper->vtbl->transfer_unmap(pctx, trans->trans);
318    if (trans->trans2)
319       helper->vtbl->transfer_unmap(pctx, trans->trans2);
320    pipe_resource_reference(&ptrans->resource, NULL);
321    free(trans->staging);
322    free(trans);
323    return NULL;
324 }
325 
326 static void
flush_region(struct pipe_context * pctx,struct pipe_transfer * ptrans,const struct pipe_box * box)327 flush_region(struct pipe_context *pctx, struct pipe_transfer *ptrans,
328              const struct pipe_box *box)
329 {
330    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
331    struct u_transfer *trans = u_transfer(ptrans);
332    enum pipe_format iformat, format = ptrans->resource->format;
333    unsigned width = box->width;
334    unsigned height = box->height;
335    void *src, *dst;
336 
337    if (!(ptrans->usage & PIPE_TRANSFER_WRITE))
338       return;
339 
340    if (trans->ss) {
341       struct pipe_blit_info blit;
342       memset(&blit, 0, sizeof(blit));
343 
344       blit.src.resource = trans->ss;
345       blit.src.format = trans->ss->format;
346       blit.src.box = *box;
347 
348       blit.dst.resource = ptrans->resource;
349       blit.dst.format = ptrans->resource->format;
350       blit.dst.level = ptrans->level;
351 
352       u_box_2d(ptrans->box.x + box->x,
353                ptrans->box.y + box->y,
354                box->width, box->height,
355                &blit.dst.box);
356 
357       blit.mask = util_format_get_mask(ptrans->resource->format);
358       blit.filter = PIPE_TEX_FILTER_NEAREST;
359 
360       pctx->blit(pctx, &blit);
361 
362       return;
363    }
364 
365    iformat = helper->vtbl->get_internal_format(ptrans->resource);
366 
367    src = (uint8_t *)trans->staging +
368          (box->y * ptrans->stride) +
369          (box->x * util_format_get_blocksize(format));
370    dst = (uint8_t *)trans->ptr +
371          (box->y * trans->trans->stride) +
372          (box->x * util_format_get_blocksize(iformat));
373 
374    switch (format) {
375    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
376       util_format_z32_float_s8x24_uint_unpack_z_float(dst,
377                                                       trans->trans->stride,
378                                                       src,
379                                                       ptrans->stride,
380                                                       width, height);
381       /* fallthru */
382    case PIPE_FORMAT_X32_S8X24_UINT:
383       dst = (uint8_t *)trans->ptr2 +
384             (box->y * trans->trans2->stride) +
385             (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT));
386 
387       util_format_z32_float_s8x24_uint_unpack_s_8uint(dst,
388                                                       trans->trans2->stride,
389                                                       src,
390                                                       ptrans->stride,
391                                                       width, height);
392       break;
393    case PIPE_FORMAT_RGTC1_UNORM:
394    case PIPE_FORMAT_RGTC1_SNORM:
395    case PIPE_FORMAT_LATC1_UNORM:
396    case PIPE_FORMAT_LATC1_SNORM:
397       util_format_rgtc1_unorm_unpack_rgba_8unorm(dst,
398                                                  trans->trans->stride,
399                                                  src,
400                                                  ptrans->stride,
401                                                  width, height);
402       break;
403    case PIPE_FORMAT_RGTC2_UNORM:
404    case PIPE_FORMAT_RGTC2_SNORM:
405    case PIPE_FORMAT_LATC2_UNORM:
406    case PIPE_FORMAT_LATC2_SNORM:
407       util_format_rgtc2_unorm_unpack_rgba_8unorm(dst,
408                                                  trans->trans->stride,
409                                                  src,
410                                                  ptrans->stride,
411                                                  width, height);
412       break;
413    default:
414       assert(!"Unexpected staging transfer type");
415       break;
416    }
417 }
418 
419 void
u_transfer_helper_transfer_flush_region(struct pipe_context * pctx,struct pipe_transfer * ptrans,const struct pipe_box * box)420 u_transfer_helper_transfer_flush_region(struct pipe_context *pctx,
421                                         struct pipe_transfer *ptrans,
422                                         const struct pipe_box *box)
423 {
424    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
425 
426    if (handle_transfer(ptrans->resource)) {
427       struct u_transfer *trans = u_transfer(ptrans);
428 
429       flush_region(pctx, ptrans, box);
430 
431       /* handle MSAA case, since there could be multiple levels of
432        * wrapped transfer, call pctx->transfer_flush_region()
433        * instead of helper->vtbl->transfer_flush_region()
434        */
435       if (trans->ss) {
436          pctx->transfer_flush_region(pctx, trans->trans, box);
437          return;
438       }
439 
440       helper->vtbl->transfer_flush_region(pctx, trans->trans, box);
441       if (trans->trans2)
442          helper->vtbl->transfer_flush_region(pctx, trans->trans2, box);
443 
444    } else {
445       helper->vtbl->transfer_flush_region(pctx, ptrans, box);
446    }
447 }
448 
449 void
u_transfer_helper_transfer_unmap(struct pipe_context * pctx,struct pipe_transfer * ptrans)450 u_transfer_helper_transfer_unmap(struct pipe_context *pctx,
451                                  struct pipe_transfer *ptrans)
452 {
453    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
454 
455    if (handle_transfer(ptrans->resource)) {
456       struct u_transfer *trans = u_transfer(ptrans);
457 
458       if (!(ptrans->usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) {
459          struct pipe_box box;
460          u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box);
461          flush_region(pctx, ptrans, &box);
462       }
463 
464       /* in MSAA case, there could be multiple levels of wrapping
465        * so don't call helper->vtbl->transfer_unmap() directly
466        */
467       if (trans->ss) {
468          pctx->transfer_unmap(pctx, trans->trans);
469          pipe_resource_reference(&trans->ss, NULL);
470       } else {
471          helper->vtbl->transfer_unmap(pctx, trans->trans);
472          if (trans->trans2)
473             helper->vtbl->transfer_unmap(pctx, trans->trans2);
474       }
475 
476       free(trans);
477    } else {
478       helper->vtbl->transfer_unmap(pctx, ptrans);
479    }
480 }
481 
482 struct u_transfer_helper *
u_transfer_helper_create(const struct u_transfer_vtbl * vtbl,bool separate_z32s8,bool fake_rgtc,bool msaa_map)483 u_transfer_helper_create(const struct u_transfer_vtbl *vtbl,
484                          bool separate_z32s8,
485                          bool fake_rgtc,
486                          bool msaa_map)
487 {
488    struct u_transfer_helper *helper = calloc(1, sizeof(*helper));
489 
490    helper->vtbl = vtbl;
491    helper->separate_z32s8 = separate_z32s8;
492    helper->fake_rgtc = fake_rgtc;
493    helper->msaa_map = msaa_map;
494 
495    return helper;
496 }
497 
498 void
u_transfer_helper_destroy(struct u_transfer_helper * helper)499 u_transfer_helper_destroy(struct u_transfer_helper *helper)
500 {
501    free(helper);
502 }
503