gallium/u_transfer_helper: add util functions for doing deinterleaving during map
[mesa.git] / src / gallium / auxiliary / util / u_transfer_helper.c
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
34 struct 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
42 static 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 */
63 struct 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
77 static inline struct u_transfer *
78 u_transfer(struct pipe_transfer *ptrans)
79 {
80 debug_assert(handle_transfer(ptrans->resource));
81 return (struct u_transfer *)ptrans;
82 }
83
84 struct pipe_resource *
85 u_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
134 void
135 u_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
149 static 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 */
159 static void *
160 transfer_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
228 void *
229 u_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
338 fail:
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
349 static void
350 flush_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_TRANSFER_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 /* fallthru */
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 /* fallthru */
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
459 void
460 u_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
489 void
490 u_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_TRANSFER_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->transfer_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
525 struct u_transfer_helper *
526 u_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
543 void
544 u_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 */
556 void *
557 u_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_TRANSFER_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_TRANSFER_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
630 fail:
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
641 void
642 u_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_TRANSFER_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 }