2 * Copyright © 2017 Red Hat
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:
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
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
24 #include "pipe/p_screen.h"
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"
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 */
42 static inline bool handle_transfer(struct pipe_resource
*prsc
)
44 struct u_transfer_helper
*helper
= prsc
->screen
->transfer_helper
;
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
)
53 if (helper
->msaa_map
&& (prsc
->nr_samples
> 1))
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.
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.
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 */
77 static inline struct u_transfer
*
78 u_transfer(struct pipe_transfer
*ptrans
)
80 debug_assert(handle_transfer(ptrans
->resource
));
81 return (struct u_transfer
*)ptrans
;
84 struct pipe_resource
*
85 u_transfer_helper_resource_create(struct pipe_screen
*pscreen
,
86 const struct pipe_resource
*templ
)
88 struct u_transfer_helper
*helper
= pscreen
->transfer_helper
;
89 enum pipe_format format
= templ
->format
;
90 struct pipe_resource
*prsc
;
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
;
97 t
.format
= util_format_get_depth_only(format
);
99 prsc
= helper
->vtbl
->resource_create(pscreen
, &t
);
103 prsc
->format
= format
; /* frob the format back to the "external" format */
105 t
.format
= PIPE_FORMAT_S8_UINT
;
106 stencil
= helper
->vtbl
->resource_create(pscreen
, &t
);
109 helper
->vtbl
->resource_destroy(pscreen
, prsc
);
113 helper
->vtbl
->set_stencil(prsc
, stencil
);
114 } else if ((util_format_description(format
)->layout
== UTIL_FORMAT_LAYOUT_RGTC
) &&
116 struct pipe_resource t
= *templ
;
117 t
.format
= PIPE_FORMAT_R8G8B8A8_UNORM
;
119 prsc
= helper
->vtbl
->resource_create(pscreen
, &t
);
123 prsc
->format
= format
; /* frob the format back to the "external" format */
125 /* normal case, no special handling: */
126 prsc
= helper
->vtbl
->resource_create(pscreen
, templ
);
135 u_transfer_helper_resource_destroy(struct pipe_screen
*pscreen
,
136 struct pipe_resource
*prsc
)
138 struct u_transfer_helper
*helper
= pscreen
->transfer_helper
;
140 if (helper
->vtbl
->get_stencil
) {
141 struct pipe_resource
*stencil
= helper
->vtbl
->get_stencil(prsc
);
143 pipe_resource_reference(&stencil
, NULL
);
146 helper
->vtbl
->resource_destroy(pscreen
, prsc
);
149 static bool needs_pack(unsigned usage
)
151 return (usage
& PIPE_TRANSFER_READ
) &&
152 !(usage
& (PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE
| PIPE_TRANSFER_DISCARD_RANGE
));
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
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
)
166 struct pipe_screen
*pscreen
= pctx
->screen
;
167 struct u_transfer
*trans
= calloc(1, sizeof(*trans
));
170 struct pipe_transfer
*ptrans
= &trans
->base
;
172 pipe_resource_reference(&ptrans
->resource
, prsc
);
173 ptrans
->level
= level
;
174 ptrans
->usage
= usage
;
177 struct pipe_resource tmpl
= {
178 .target
= prsc
->target
,
179 .format
= prsc
->format
,
180 .width0
= box
->width
,
181 .height0
= box
->height
,
185 trans
->ss
= pscreen
->resource_create(pscreen
, &tmpl
);
191 if (needs_pack(usage
)) {
192 struct pipe_blit_info blit
;
193 memset(&blit
, 0, sizeof(blit
));
195 blit
.src
.resource
= ptrans
->resource
;
196 blit
.src
.format
= ptrans
->resource
->format
;
197 blit
.src
.level
= ptrans
->level
;
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;
206 blit
.mask
= util_format_get_mask(prsc
->format
);
207 blit
.filter
= PIPE_TEX_FILTER_NEAREST
;
209 pctx
->blit(pctx
, &blit
);
212 struct pipe_box map_box
= *box
;
216 void *ss_map
= pctx
->transfer_map(pctx
, trans
->ss
, 0, usage
, &map_box
,
223 ptrans
->stride
= trans
->trans
->stride
;
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
)
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
;
242 if (!handle_transfer(prsc
))
243 return helper
->vtbl
->transfer_map(pctx
, prsc
, level
, usage
, box
, pptrans
);
245 if (helper
->msaa_map
&& (prsc
->nr_samples
> 1))
246 return transfer_map_msaa(pctx
, prsc
, level
, usage
, box
, pptrans
);
248 debug_assert(box
->depth
== 1);
250 trans
= calloc(1, sizeof(*trans
));
254 ptrans
= &trans
->base
;
255 pipe_resource_reference(&ptrans
->resource
, prsc
);
256 ptrans
->level
= level
;
257 ptrans
->usage
= usage
;
259 ptrans
->stride
= util_format_get_stride(format
, box
->width
);
260 ptrans
->layer_stride
= ptrans
->stride
* box
->height
;
262 trans
->staging
= malloc(ptrans
->layer_stride
);
266 trans
->ptr
= helper
->vtbl
->transfer_map(pctx
, prsc
, level
, usage
, box
,
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
);
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
,
282 trans
->trans
->stride
,
284 util_format_z32_float_s8x24_uint_pack_s_8uint(trans
->staging
,
287 trans
->trans2
->stride
,
290 case PIPE_FORMAT_Z24_UNORM_S8_UINT
:
291 util_format_z24_unorm_s8_uint_pack_separate(trans
->staging
,
294 trans
->trans
->stride
,
296 trans
->trans2
->stride
,
300 unreachable("Unexpected format");
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
,
313 trans
->trans
->stride
,
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
,
323 trans
->trans
->stride
,
327 assert(!"Unexpected format");
336 return trans
->staging
;
340 helper
->vtbl
->transfer_unmap(pctx
, trans
->trans
);
342 helper
->vtbl
->transfer_unmap(pctx
, trans
->trans2
);
343 pipe_resource_reference(&ptrans
->resource
, NULL
);
344 free(trans
->staging
);
350 flush_region(struct pipe_context
*pctx
, struct pipe_transfer
*ptrans
,
351 const struct pipe_box
*box
)
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
;
361 if (!(ptrans
->usage
& PIPE_TRANSFER_WRITE
))
365 struct pipe_blit_info blit
;
366 memset(&blit
, 0, sizeof(blit
));
368 blit
.src
.resource
= trans
->ss
;
369 blit
.src
.format
= trans
->ss
->format
;
372 blit
.dst
.resource
= ptrans
->resource
;
373 blit
.dst
.format
= ptrans
->resource
->format
;
374 blit
.dst
.level
= ptrans
->level
;
376 u_box_2d(ptrans
->box
.x
+ box
->x
,
377 ptrans
->box
.y
+ box
->y
,
378 box
->width
, box
->height
,
381 blit
.mask
= util_format_get_mask(ptrans
->resource
->format
);
382 blit
.filter
= PIPE_TEX_FILTER_NEAREST
;
384 pctx
->blit(pctx
, &blit
);
389 iformat
= helper
->vtbl
->get_internal_format(ptrans
->resource
);
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
));
399 case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT
:
400 util_format_z32_float_s8x24_uint_unpack_z_float(dst
,
401 trans
->trans
->stride
,
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
));
411 util_format_z32_float_s8x24_uint_unpack_s_8uint(dst
,
412 trans
->trans2
->stride
,
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
,
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
));
428 util_format_z24_unorm_s8_uint_unpack_s_8uint(dst
, trans
->trans2
->stride
,
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
,
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
,
454 assert(!"Unexpected staging transfer type");
460 u_transfer_helper_transfer_flush_region(struct pipe_context
*pctx
,
461 struct pipe_transfer
*ptrans
,
462 const struct pipe_box
*box
)
464 struct u_transfer_helper
*helper
= pctx
->screen
->transfer_helper
;
466 if (handle_transfer(ptrans
->resource
)) {
467 struct u_transfer
*trans
= u_transfer(ptrans
);
469 flush_region(pctx
, ptrans
, box
);
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()
476 pctx
->transfer_flush_region(pctx
, trans
->trans
, box
);
480 helper
->vtbl
->transfer_flush_region(pctx
, trans
->trans
, box
);
482 helper
->vtbl
->transfer_flush_region(pctx
, trans
->trans2
, box
);
485 helper
->vtbl
->transfer_flush_region(pctx
, ptrans
, box
);
490 u_transfer_helper_transfer_unmap(struct pipe_context
*pctx
,
491 struct pipe_transfer
*ptrans
)
493 struct u_transfer_helper
*helper
= pctx
->screen
->transfer_helper
;
495 if (handle_transfer(ptrans
->resource
)) {
496 struct u_transfer
*trans
= u_transfer(ptrans
);
498 if (!(ptrans
->usage
& PIPE_TRANSFER_FLUSH_EXPLICIT
)) {
500 u_box_2d(0, 0, ptrans
->box
.width
, ptrans
->box
.height
, &box
);
501 flush_region(pctx
, ptrans
, &box
);
504 /* in MSAA case, there could be multiple levels of wrapping
505 * so don't call helper->vtbl->transfer_unmap() directly
508 pctx
->transfer_unmap(pctx
, trans
->trans
);
509 pipe_resource_reference(&trans
->ss
, NULL
);
511 helper
->vtbl
->transfer_unmap(pctx
, trans
->trans
);
513 helper
->vtbl
->transfer_unmap(pctx
, trans
->trans2
);
516 pipe_resource_reference(&ptrans
->resource
, NULL
);
518 free(trans
->staging
);
521 helper
->vtbl
->transfer_unmap(pctx
, ptrans
);
525 struct u_transfer_helper
*
526 u_transfer_helper_create(const struct u_transfer_vtbl
*vtbl
,
528 bool separate_stencil
,
532 struct u_transfer_helper
*helper
= calloc(1, sizeof(*helper
));
535 helper
->separate_z32s8
= separate_z32s8
;
536 helper
->separate_stencil
= separate_stencil
;
537 helper
->fake_rgtc
= fake_rgtc
;
538 helper
->msaa_map
= msaa_map
;
544 u_transfer_helper_destroy(struct u_transfer_helper
*helper
)
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
553 * drivers should expect to be passed the same buffer repeatedly with the format changed
554 * to indicate which component is being mapped
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
)
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
;
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
);
574 debug_assert(box
->depth
== 1);
576 trans
= calloc(1, sizeof(*trans
));
580 ptrans
= &trans
->base
;
581 pipe_resource_reference(&ptrans
->resource
, prsc
);
582 ptrans
->level
= level
;
583 ptrans
->usage
= usage
;
585 ptrans
->stride
= util_format_get_stride(format
, box
->width
);
586 ptrans
->layer_stride
= ptrans
->stride
* box
->height
;
588 trans
->staging
= malloc(ptrans
->layer_stride
);
592 trans
->ptr
= helper
->vtbl
->transfer_map(pctx
, prsc
, level
, usage
| PIPE_TRANSFER_DEPTH_ONLY
, box
,
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
,
605 trans
->trans
->stride
,
607 util_format_z32_float_s8x24_uint_pack_s_8uint(trans
->staging
,
610 trans
->trans2
->stride
,
613 case PIPE_FORMAT_Z24_UNORM_S8_UINT
:
614 util_format_z24_unorm_s8_uint_pack_separate(trans
->staging
,
617 trans
->trans
->stride
,
619 trans
->trans2
->stride
,
623 unreachable("Unexpected format");
628 return trans
->staging
;
632 helper
->vtbl
->transfer_unmap(pctx
, trans
->trans
);
634 helper
->vtbl
->transfer_unmap(pctx
, trans
->trans2
);
635 pipe_resource_reference(&ptrans
->resource
, NULL
);
636 free(trans
->staging
);
642 u_transfer_helper_deinterleave_transfer_unmap(struct pipe_context
*pctx
,
643 struct pipe_transfer
*ptrans
)
645 struct u_transfer_helper
*helper
= pctx
->screen
->transfer_helper
;
646 enum pipe_format format
= ptrans
->resource
->format
;
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
;
652 if (!(ptrans
->usage
& PIPE_TRANSFER_FLUSH_EXPLICIT
)) {
654 u_box_2d(0, 0, ptrans
->box
.width
, ptrans
->box
.height
, &box
);
655 flush_region(pctx
, ptrans
, &box
);
658 helper
->vtbl
->transfer_unmap(pctx
, trans
->trans
);
660 helper
->vtbl
->transfer_unmap(pctx
, trans
->trans2
);
662 pipe_resource_reference(&ptrans
->resource
, NULL
);
664 free(trans
->staging
);
667 helper
->vtbl
->transfer_unmap(pctx
, ptrans
);