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/format/u_format.h"
28#include "util/format/u_format_rgtc.h"
29#include "util/format/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_MAP_READ) &&
152      !(usage & (PIPE_MAP_DISCARD_WHOLE_RESOURCE | PIPE_MAP_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->texture_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 (util_format_description(prsc->format)->layout == UTIL_FORMAT_LAYOUT_RGTC) {
304      if (needs_pack(usage)) {
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      }
331   } else {
332      unreachable("bleh");
333   }
334
335   *pptrans = ptrans;
336   return trans->staging;
337
338fail:
339   if (trans->trans)
340      helper->vtbl->transfer_unmap(pctx, trans->trans);
341   if (trans->trans2)
342      helper->vtbl->transfer_unmap(pctx, trans->trans2);
343   pipe_resource_reference(&ptrans->resource, NULL);
344   free(trans->staging);
345   free(trans);
346   return NULL;
347}
348
349static void
350flush_region(struct pipe_context *pctx, struct pipe_transfer *ptrans,
351             const struct pipe_box *box)
352{
353   struct u_transfer_helper *helper = pctx->screen->transfer_helper;
354   /* using the function here hits an assert for the deinterleave cases */
355   struct u_transfer *trans = (struct u_transfer *)ptrans;
356   enum pipe_format iformat, format = ptrans->resource->format;
357   unsigned width = box->width;
358   unsigned height = box->height;
359   void *src, *dst;
360
361   if (!(ptrans->usage & PIPE_MAP_WRITE))
362      return;
363
364   if (trans->ss) {
365      struct pipe_blit_info blit;
366      memset(&blit, 0, sizeof(blit));
367
368      blit.src.resource = trans->ss;
369      blit.src.format = trans->ss->format;
370      blit.src.box = *box;
371
372      blit.dst.resource = ptrans->resource;
373      blit.dst.format = ptrans->resource->format;
374      blit.dst.level = ptrans->level;
375
376      u_box_2d(ptrans->box.x + box->x,
377               ptrans->box.y + box->y,
378               box->width, box->height,
379               &blit.dst.box);
380
381      blit.mask = util_format_get_mask(ptrans->resource->format);
382      blit.filter = PIPE_TEX_FILTER_NEAREST;
383
384      pctx->blit(pctx, &blit);
385
386      return;
387   }
388
389   iformat = helper->vtbl->get_internal_format(ptrans->resource);
390
391   src = (uint8_t *)trans->staging +
392         (box->y * ptrans->stride) +
393         (box->x * util_format_get_blocksize(format));
394   dst = (uint8_t *)trans->ptr +
395         (box->y * trans->trans->stride) +
396         (box->x * util_format_get_blocksize(iformat));
397
398   switch (format) {
399   case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
400      util_format_z32_float_s8x24_uint_unpack_z_float(dst,
401                                                      trans->trans->stride,
402                                                      src,
403                                                      ptrans->stride,
404                                                      width, height);
405      FALLTHROUGH;
406   case PIPE_FORMAT_X32_S8X24_UINT:
407      dst = (uint8_t *)trans->ptr2 +
408            (box->y * trans->trans2->stride) +
409            (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT));
410
411      util_format_z32_float_s8x24_uint_unpack_s_8uint(dst,
412                                                      trans->trans2->stride,
413                                                      src,
414                                                      ptrans->stride,
415                                                      width, height);
416      break;
417   case PIPE_FORMAT_Z24_UNORM_S8_UINT:
418      /* just do a strided 32-bit copy for depth; s8 can become garbage x8 */
419      util_format_z32_unorm_unpack_z_32unorm(dst, trans->trans->stride,
420                                             src, ptrans->stride,
421                                             width, height);
422      FALLTHROUGH;
423   case PIPE_FORMAT_X24S8_UINT:
424      dst = (uint8_t *)trans->ptr2 +
425            (box->y * trans->trans2->stride) +
426            (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT));
427
428      util_format_z24_unorm_s8_uint_unpack_s_8uint(dst, trans->trans2->stride,
429                                                   src, ptrans->stride,
430                                                   width, height);
431      break;
432
433   case PIPE_FORMAT_RGTC1_UNORM:
434   case PIPE_FORMAT_RGTC1_SNORM:
435   case PIPE_FORMAT_LATC1_UNORM:
436   case PIPE_FORMAT_LATC1_SNORM:
437      util_format_rgtc1_unorm_unpack_rgba_8unorm(dst,
438                                                 trans->trans->stride,
439                                                 src,
440                                                 ptrans->stride,
441                                                 width, height);
442      break;
443   case PIPE_FORMAT_RGTC2_UNORM:
444   case PIPE_FORMAT_RGTC2_SNORM:
445   case PIPE_FORMAT_LATC2_UNORM:
446   case PIPE_FORMAT_LATC2_SNORM:
447      util_format_rgtc2_unorm_unpack_rgba_8unorm(dst,
448                                                 trans->trans->stride,
449                                                 src,
450                                                 ptrans->stride,
451                                                 width, height);
452      break;
453   default:
454      assert(!"Unexpected staging transfer type");
455      break;
456   }
457}
458
459void
460u_transfer_helper_transfer_flush_region(struct pipe_context *pctx,
461                                        struct pipe_transfer *ptrans,
462                                        const struct pipe_box *box)
463{
464   struct u_transfer_helper *helper = pctx->screen->transfer_helper;
465
466   if (handle_transfer(ptrans->resource)) {
467      struct u_transfer *trans = u_transfer(ptrans);
468
469      flush_region(pctx, ptrans, box);
470
471      /* handle MSAA case, since there could be multiple levels of
472       * wrapped transfer, call pctx->transfer_flush_region()
473       * instead of helper->vtbl->transfer_flush_region()
474       */
475      if (trans->ss) {
476         pctx->transfer_flush_region(pctx, trans->trans, box);
477         return;
478      }
479
480      helper->vtbl->transfer_flush_region(pctx, trans->trans, box);
481      if (trans->trans2)
482         helper->vtbl->transfer_flush_region(pctx, trans->trans2, box);
483
484   } else {
485      helper->vtbl->transfer_flush_region(pctx, ptrans, box);
486   }
487}
488
489void
490u_transfer_helper_transfer_unmap(struct pipe_context *pctx,
491                                 struct pipe_transfer *ptrans)
492{
493   struct u_transfer_helper *helper = pctx->screen->transfer_helper;
494
495   if (handle_transfer(ptrans->resource)) {
496      struct u_transfer *trans = u_transfer(ptrans);
497
498      if (!(ptrans->usage & PIPE_MAP_FLUSH_EXPLICIT)) {
499         struct pipe_box box;
500         u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box);
501         flush_region(pctx, ptrans, &box);
502      }
503
504      /* in MSAA case, there could be multiple levels of wrapping
505       * so don't call helper->vtbl->transfer_unmap() directly
506       */
507      if (trans->ss) {
508         pctx->texture_unmap(pctx, trans->trans);
509         pipe_resource_reference(&trans->ss, NULL);
510      } else {
511         helper->vtbl->transfer_unmap(pctx, trans->trans);
512         if (trans->trans2)
513            helper->vtbl->transfer_unmap(pctx, trans->trans2);
514      }
515
516      pipe_resource_reference(&ptrans->resource, NULL);
517
518      free(trans->staging);
519      free(trans);
520   } else {
521      helper->vtbl->transfer_unmap(pctx, ptrans);
522   }
523}
524
525struct u_transfer_helper *
526u_transfer_helper_create(const struct u_transfer_vtbl *vtbl,
527                         bool separate_z32s8,
528                         bool separate_stencil,
529                         bool fake_rgtc,
530                         bool msaa_map)
531{
532   struct u_transfer_helper *helper = calloc(1, sizeof(*helper));
533
534   helper->vtbl = vtbl;
535   helper->separate_z32s8 = separate_z32s8;
536   helper->separate_stencil = separate_stencil;
537   helper->fake_rgtc = fake_rgtc;
538   helper->msaa_map = msaa_map;
539
540   return helper;
541}
542
543void
544u_transfer_helper_destroy(struct u_transfer_helper *helper)
545{
546   free(helper);
547}
548
549
550/* these two functions 'deinterleave' are meant to be used without the corresponding
551 * resource_create/destroy hooks, as they perform the interleaving on-the-fly
552 *
553 * drivers should expect to be passed the same buffer repeatedly with the format changed
554 * to indicate which component is being mapped
555 */
556void *
557u_transfer_helper_deinterleave_transfer_map(struct pipe_context *pctx,
558                                            struct pipe_resource *prsc,
559                                            unsigned level, unsigned usage,
560                                            const struct pipe_box *box,
561                                            struct pipe_transfer **pptrans)
562{
563   struct u_transfer_helper *helper = pctx->screen->transfer_helper;
564   struct u_transfer *trans;
565   struct pipe_transfer *ptrans;
566   enum pipe_format format = prsc->format;
567   unsigned width = box->width;
568   unsigned height = box->height;
569
570   if (!((helper->separate_stencil && util_format_is_depth_and_stencil(format)) ||
571       (format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT && helper->separate_z32s8)))
572      return helper->vtbl->transfer_map(pctx, prsc, level, usage, box, pptrans);
573
574   debug_assert(box->depth == 1);
575
576   trans = calloc(1, sizeof(*trans));
577   if (!trans)
578      return NULL;
579
580   ptrans = &trans->base;
581   pipe_resource_reference(&ptrans->resource, prsc);
582   ptrans->level = level;
583   ptrans->usage = usage;
584   ptrans->box   = *box;
585   ptrans->stride = util_format_get_stride(format, box->width);
586   ptrans->layer_stride = ptrans->stride * box->height;
587
588   trans->staging = malloc(ptrans->layer_stride);
589   if (!trans->staging)
590      goto fail;
591
592   trans->ptr = helper->vtbl->transfer_map(pctx, prsc, level, usage | PIPE_MAP_DEPTH_ONLY, box,
593                                           &trans->trans);
594   if (!trans->ptr)
595      goto fail;
596
597   trans->ptr2 = helper->vtbl->transfer_map(pctx, prsc, level,
598                                            usage | PIPE_MAP_STENCIL_ONLY, box, &trans->trans2);
599   if (needs_pack(usage)) {
600      switch (prsc->format) {
601      case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
602         util_format_z32_float_s8x24_uint_pack_z_float(trans->staging,
603                                                       ptrans->stride,
604                                                       trans->ptr,
605                                                       trans->trans->stride,
606                                                       width, height);
607         util_format_z32_float_s8x24_uint_pack_s_8uint(trans->staging,
608                                                       ptrans->stride,
609                                                       trans->ptr2,
610                                                       trans->trans2->stride,
611                                                       width, height);
612         break;
613      case PIPE_FORMAT_Z24_UNORM_S8_UINT:
614         util_format_z24_unorm_s8_uint_pack_separate(trans->staging,
615                                                     ptrans->stride,
616                                                     trans->ptr,
617                                                     trans->trans->stride,
618                                                     trans->ptr2,
619                                                     trans->trans2->stride,
620                                                     width, height);
621         break;
622      default:
623         unreachable("Unexpected format");
624      }
625   }
626
627   *pptrans = ptrans;
628   return trans->staging;
629
630fail:
631   if (trans->trans)
632      helper->vtbl->transfer_unmap(pctx, trans->trans);
633   if (trans->trans2)
634      helper->vtbl->transfer_unmap(pctx, trans->trans2);
635   pipe_resource_reference(&ptrans->resource, NULL);
636   free(trans->staging);
637   free(trans);
638   return NULL;
639}
640
641void
642u_transfer_helper_deinterleave_transfer_unmap(struct pipe_context *pctx,
643                                              struct pipe_transfer *ptrans)
644{
645   struct u_transfer_helper *helper = pctx->screen->transfer_helper;
646   enum pipe_format format = ptrans->resource->format;
647
648   if ((helper->separate_stencil && util_format_is_depth_and_stencil(format)) ||
649       (format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT && helper->separate_z32s8)) {
650      struct u_transfer *trans = (struct u_transfer *)ptrans;
651
652      if (!(ptrans->usage & PIPE_MAP_FLUSH_EXPLICIT)) {
653         struct pipe_box box;
654         u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box);
655         flush_region(pctx, ptrans, &box);
656      }
657
658      helper->vtbl->transfer_unmap(pctx, trans->trans);
659      if (trans->trans2)
660         helper->vtbl->transfer_unmap(pctx, trans->trans2);
661
662      pipe_resource_reference(&ptrans->resource, NULL);
663
664      free(trans->staging);
665      free(trans);
666   } else {
667      helper->vtbl->transfer_unmap(pctx, ptrans);
668   }
669}
670