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