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