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