gallium: enable GL_AMD_depth_clamp_separate on r600, radeonsi
[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/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
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 *
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 *
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
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
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 *
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 struct pipe_box map_box = *box;
211 map_box.x = 0;
212 map_box.y = 0;
213
214 void *ss_map = pctx->transfer_map(pctx, trans->ss, 0, usage, &map_box,
215 &trans->trans);
216 if (!ss_map) {
217 free(trans);
218 return NULL;
219 }
220
221 ptrans->stride = trans->trans->stride;
222 *pptrans = ptrans;
223 return ss_map;
224 }
225
226 void *
227 u_transfer_helper_transfer_map(struct pipe_context *pctx,
228 struct pipe_resource *prsc,
229 unsigned level, unsigned usage,
230 const struct pipe_box *box,
231 struct pipe_transfer **pptrans)
232 {
233 struct u_transfer_helper *helper = pctx->screen->transfer_helper;
234 struct u_transfer *trans;
235 struct pipe_transfer *ptrans;
236 enum pipe_format format = prsc->format;
237 unsigned width = box->width;
238 unsigned height = box->height;
239
240 if (!handle_transfer(prsc))
241 return helper->vtbl->transfer_map(pctx, prsc, level, usage, box, pptrans);
242
243 if (helper->msaa_map && (prsc->nr_samples > 1))
244 return transfer_map_msaa(pctx, prsc, level, usage, box, pptrans);
245
246 debug_assert(box->depth == 1);
247
248 trans = calloc(1, sizeof(*trans));
249 if (!trans)
250 return NULL;
251
252 ptrans = &trans->base;
253 pipe_resource_reference(&ptrans->resource, prsc);
254 ptrans->level = level;
255 ptrans->usage = usage;
256 ptrans->box = *box;
257 ptrans->stride = util_format_get_stride(format, box->width);
258 ptrans->layer_stride = ptrans->stride * box->height;
259
260 trans->staging = malloc(ptrans->layer_stride);
261 if (!trans->staging)
262 goto fail;
263
264 trans->ptr = helper->vtbl->transfer_map(pctx, prsc, level, usage, box,
265 &trans->trans);
266 if (!trans->ptr)
267 goto fail;
268
269 if (prsc->format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT) {
270 struct pipe_resource *stencil = helper->vtbl->get_stencil(prsc);
271 trans->ptr2 = helper->vtbl->transfer_map(pctx, stencil, level,
272 usage, box, &trans->trans2);
273
274 if (needs_pack(usage)) {
275 util_format_z32_float_s8x24_uint_pack_z_float(trans->staging,
276 ptrans->stride,
277 trans->ptr,
278 trans->trans->stride,
279 width, height);
280 util_format_z32_float_s8x24_uint_pack_s_8uint(trans->staging,
281 ptrans->stride,
282 trans->ptr2,
283 trans->trans2->stride,
284 width, height);
285 }
286 } else if (needs_pack(usage) &&
287 util_format_description(prsc->format)->layout == UTIL_FORMAT_LAYOUT_RGTC) {
288 switch (prsc->format) {
289 case PIPE_FORMAT_RGTC1_UNORM:
290 case PIPE_FORMAT_RGTC1_SNORM:
291 case PIPE_FORMAT_LATC1_UNORM:
292 case PIPE_FORMAT_LATC1_SNORM:
293 util_format_rgtc1_unorm_pack_rgba_8unorm(trans->staging,
294 ptrans->stride,
295 trans->ptr,
296 trans->trans->stride,
297 width, height);
298 break;
299 case PIPE_FORMAT_RGTC2_UNORM:
300 case PIPE_FORMAT_RGTC2_SNORM:
301 case PIPE_FORMAT_LATC2_UNORM:
302 case PIPE_FORMAT_LATC2_SNORM:
303 util_format_rgtc2_unorm_pack_rgba_8unorm(trans->staging,
304 ptrans->stride,
305 trans->ptr,
306 trans->trans->stride,
307 width, height);
308 break;
309 default:
310 assert(!"Unexpected format");
311 break;
312 }
313 } else {
314 unreachable("bleh");
315 }
316
317 *pptrans = ptrans;
318 return trans->staging;
319
320 fail:
321 if (trans->trans)
322 helper->vtbl->transfer_unmap(pctx, trans->trans);
323 if (trans->trans2)
324 helper->vtbl->transfer_unmap(pctx, trans->trans2);
325 pipe_resource_reference(&ptrans->resource, NULL);
326 free(trans->staging);
327 free(trans);
328 return NULL;
329 }
330
331 static void
332 flush_region(struct pipe_context *pctx, struct pipe_transfer *ptrans,
333 const struct pipe_box *box)
334 {
335 struct u_transfer_helper *helper = pctx->screen->transfer_helper;
336 struct u_transfer *trans = u_transfer(ptrans);
337 enum pipe_format iformat, format = ptrans->resource->format;
338 unsigned width = box->width;
339 unsigned height = box->height;
340 void *src, *dst;
341
342 if (!(ptrans->usage & PIPE_TRANSFER_WRITE))
343 return;
344
345 if (trans->ss) {
346 struct pipe_blit_info blit;
347 memset(&blit, 0, sizeof(blit));
348
349 blit.src.resource = trans->ss;
350 blit.src.format = trans->ss->format;
351 blit.src.box = *box;
352
353 blit.dst.resource = ptrans->resource;
354 blit.dst.format = ptrans->resource->format;
355 blit.dst.level = ptrans->level;
356
357 u_box_2d(ptrans->box.x + box->x,
358 ptrans->box.y + box->y,
359 box->width, box->height,
360 &blit.dst.box);
361
362 blit.mask = util_format_get_mask(ptrans->resource->format);
363 blit.filter = PIPE_TEX_FILTER_NEAREST;
364
365 pctx->blit(pctx, &blit);
366
367 return;
368 }
369
370 iformat = helper->vtbl->get_internal_format(ptrans->resource);
371
372 src = (uint8_t *)trans->staging +
373 (box->y * ptrans->stride) +
374 (box->x * util_format_get_blocksize(format));
375 dst = (uint8_t *)trans->ptr +
376 (box->y * trans->trans->stride) +
377 (box->x * util_format_get_blocksize(iformat));
378
379 switch (format) {
380 case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
381 util_format_z32_float_s8x24_uint_unpack_z_float(dst,
382 trans->trans->stride,
383 src,
384 ptrans->stride,
385 width, height);
386 /* fallthru */
387 case PIPE_FORMAT_X32_S8X24_UINT:
388 dst = (uint8_t *)trans->ptr2 +
389 (box->y * trans->trans2->stride) +
390 (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT));
391
392 util_format_z32_float_s8x24_uint_unpack_s_8uint(dst,
393 trans->trans2->stride,
394 src,
395 ptrans->stride,
396 width, height);
397 break;
398 case PIPE_FORMAT_RGTC1_UNORM:
399 case PIPE_FORMAT_RGTC1_SNORM:
400 case PIPE_FORMAT_LATC1_UNORM:
401 case PIPE_FORMAT_LATC1_SNORM:
402 util_format_rgtc1_unorm_unpack_rgba_8unorm(dst,
403 trans->trans->stride,
404 src,
405 ptrans->stride,
406 width, height);
407 break;
408 case PIPE_FORMAT_RGTC2_UNORM:
409 case PIPE_FORMAT_RGTC2_SNORM:
410 case PIPE_FORMAT_LATC2_UNORM:
411 case PIPE_FORMAT_LATC2_SNORM:
412 util_format_rgtc2_unorm_unpack_rgba_8unorm(dst,
413 trans->trans->stride,
414 src,
415 ptrans->stride,
416 width, height);
417 break;
418 default:
419 assert(!"Unexpected staging transfer type");
420 break;
421 }
422 }
423
424 void
425 u_transfer_helper_transfer_flush_region(struct pipe_context *pctx,
426 struct pipe_transfer *ptrans,
427 const struct pipe_box *box)
428 {
429 struct u_transfer_helper *helper = pctx->screen->transfer_helper;
430
431 if (handle_transfer(ptrans->resource)) {
432 struct u_transfer *trans = u_transfer(ptrans);
433
434 flush_region(pctx, ptrans, box);
435
436 /* handle MSAA case, since there could be multiple levels of
437 * wrapped transfer, call pctx->transfer_flush_region()
438 * instead of helper->vtbl->transfer_flush_region()
439 */
440 if (trans->ss) {
441 pctx->transfer_flush_region(pctx, trans->trans, box);
442 return;
443 }
444
445 helper->vtbl->transfer_flush_region(pctx, trans->trans, box);
446 if (trans->trans2)
447 helper->vtbl->transfer_flush_region(pctx, trans->trans2, box);
448
449 } else {
450 helper->vtbl->transfer_flush_region(pctx, ptrans, box);
451 }
452 }
453
454 void
455 u_transfer_helper_transfer_unmap(struct pipe_context *pctx,
456 struct pipe_transfer *ptrans)
457 {
458 struct u_transfer_helper *helper = pctx->screen->transfer_helper;
459
460 if (handle_transfer(ptrans->resource)) {
461 struct u_transfer *trans = u_transfer(ptrans);
462
463 if (!(ptrans->usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) {
464 struct pipe_box box;
465 u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box);
466 flush_region(pctx, ptrans, &box);
467 }
468
469 /* in MSAA case, there could be multiple levels of wrapping
470 * so don't call helper->vtbl->transfer_unmap() directly
471 */
472 if (trans->ss) {
473 pctx->transfer_unmap(pctx, trans->trans);
474 pipe_resource_reference(&trans->ss, NULL);
475 } else {
476 helper->vtbl->transfer_unmap(pctx, trans->trans);
477 if (trans->trans2)
478 helper->vtbl->transfer_unmap(pctx, trans->trans2);
479 }
480
481 free(trans);
482 } else {
483 helper->vtbl->transfer_unmap(pctx, ptrans);
484 }
485 }
486
487 struct u_transfer_helper *
488 u_transfer_helper_create(const struct u_transfer_vtbl *vtbl,
489 bool separate_z32s8,
490 bool fake_rgtc,
491 bool msaa_map)
492 {
493 struct u_transfer_helper *helper = calloc(1, sizeof(*helper));
494
495 helper->vtbl = vtbl;
496 helper->separate_z32s8 = separate_z32s8;
497 helper->fake_rgtc = fake_rgtc;
498 helper->msaa_map = msaa_map;
499
500 return helper;
501 }
502
503 void
504 u_transfer_helper_destroy(struct u_transfer_helper *helper)
505 {
506 free(helper);
507 }