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 34struct 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 42static 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 */ 63struct 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 77static inline struct u_transfer * 78u_transfer(struct pipe_transfer *ptrans) 79{ 80 debug_assert(handle_transfer(ptrans->resource)); 81 return (struct u_transfer *)ptrans; 82} 83 84struct pipe_resource * 85u_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 134void 135u_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 149static 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 */ 159static void * 160transfer_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 228void * 229u_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 (needs_pack(usage) && 304 util_format_description(prsc->format)->layout == UTIL_FORMAT_LAYOUT_RGTC) { 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 } else { 331 unreachable("bleh"); 332 } 333 334 *pptrans = ptrans; 335 return trans->staging; 336 337fail: 338 if (trans->trans) 339 helper->vtbl->transfer_unmap(pctx, trans->trans); 340 if (trans->trans2) 341 helper->vtbl->transfer_unmap(pctx, trans->trans2); 342 pipe_resource_reference(&ptrans->resource, NULL); 343 free(trans->staging); 344 free(trans); 345 return NULL; 346} 347 348static void 349flush_region(struct pipe_context *pctx, struct pipe_transfer *ptrans, 350 const struct pipe_box *box) 351{ 352 struct u_transfer_helper *helper = pctx->screen->transfer_helper; 353 struct u_transfer *trans = u_transfer(ptrans); 354 enum pipe_format iformat, format = ptrans->resource->format; 355 unsigned width = box->width; 356 unsigned height = box->height; 357 void *src, *dst; 358 359 if (!(ptrans->usage & PIPE_TRANSFER_WRITE)) 360 return; 361 362 if (trans->ss) { 363 struct pipe_blit_info blit; 364 memset(&blit, 0, sizeof(blit)); 365 366 blit.src.resource = trans->ss; 367 blit.src.format = trans->ss->format; 368 blit.src.box = *box; 369 370 blit.dst.resource = ptrans->resource; 371 blit.dst.format = ptrans->resource->format; 372 blit.dst.level = ptrans->level; 373 374 u_box_2d(ptrans->box.x + box->x, 375 ptrans->box.y + box->y, 376 box->width, box->height, 377 &blit.dst.box); 378 379 blit.mask = util_format_get_mask(ptrans->resource->format); 380 blit.filter = PIPE_TEX_FILTER_NEAREST; 381 382 pctx->blit(pctx, &blit); 383 384 return; 385 } 386 387 iformat = helper->vtbl->get_internal_format(ptrans->resource); 388 389 src = (uint8_t *)trans->staging + 390 (box->y * ptrans->stride) + 391 (box->x * util_format_get_blocksize(format)); 392 dst = (uint8_t *)trans->ptr + 393 (box->y * trans->trans->stride) + 394 (box->x * util_format_get_blocksize(iformat)); 395 396 switch (format) { 397 case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: 398 util_format_z32_float_s8x24_uint_unpack_z_float(dst, 399 trans->trans->stride, 400 src, 401 ptrans->stride, 402 width, height); 403 /* fallthru */ 404 case PIPE_FORMAT_X32_S8X24_UINT: 405 dst = (uint8_t *)trans->ptr2 + 406 (box->y * trans->trans2->stride) + 407 (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT)); 408 409 util_format_z32_float_s8x24_uint_unpack_s_8uint(dst, 410 trans->trans2->stride, 411 src, 412 ptrans->stride, 413 width, height); 414 break; 415 case PIPE_FORMAT_Z24_UNORM_S8_UINT: 416 /* just do a strided 32-bit copy for depth; s8 can become garbage x8 */ 417 util_format_z32_unorm_unpack_z_32unorm(dst, trans->trans->stride, 418 src, ptrans->stride, 419 width, height); 420 /* fallthru */ 421 case PIPE_FORMAT_X24S8_UINT: 422 dst = (uint8_t *)trans->ptr2 + 423 (box->y * trans->trans2->stride) + 424 (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT)); 425 426 util_format_z24_unorm_s8_uint_unpack_s_8uint(dst, trans->trans2->stride, 427 src, ptrans->stride, 428 width, height); 429 break; 430 431 case PIPE_FORMAT_RGTC1_UNORM: 432 case PIPE_FORMAT_RGTC1_SNORM: 433 case PIPE_FORMAT_LATC1_UNORM: 434 case PIPE_FORMAT_LATC1_SNORM: 435 util_format_rgtc1_unorm_unpack_rgba_8unorm(dst, 436 trans->trans->stride, 437 src, 438 ptrans->stride, 439 width, height); 440 break; 441 case PIPE_FORMAT_RGTC2_UNORM: 442 case PIPE_FORMAT_RGTC2_SNORM: 443 case PIPE_FORMAT_LATC2_UNORM: 444 case PIPE_FORMAT_LATC2_SNORM: 445 util_format_rgtc2_unorm_unpack_rgba_8unorm(dst, 446 trans->trans->stride, 447 src, 448 ptrans->stride, 449 width, height); 450 break; 451 default: 452 assert(!"Unexpected staging transfer type"); 453 break; 454 } 455} 456 457void 458u_transfer_helper_transfer_flush_region(struct pipe_context *pctx, 459 struct pipe_transfer *ptrans, 460 const struct pipe_box *box) 461{ 462 struct u_transfer_helper *helper = pctx->screen->transfer_helper; 463 464 if (handle_transfer(ptrans->resource)) { 465 struct u_transfer *trans = u_transfer(ptrans); 466 467 flush_region(pctx, ptrans, box); 468 469 /* handle MSAA case, since there could be multiple levels of 470 * wrapped transfer, call pctx->transfer_flush_region() 471 * instead of helper->vtbl->transfer_flush_region() 472 */ 473 if (trans->ss) { 474 pctx->transfer_flush_region(pctx, trans->trans, box); 475 return; 476 } 477 478 helper->vtbl->transfer_flush_region(pctx, trans->trans, box); 479 if (trans->trans2) 480 helper->vtbl->transfer_flush_region(pctx, trans->trans2, box); 481 482 } else { 483 helper->vtbl->transfer_flush_region(pctx, ptrans, box); 484 } 485} 486 487void 488u_transfer_helper_transfer_unmap(struct pipe_context *pctx, 489 struct pipe_transfer *ptrans) 490{ 491 struct u_transfer_helper *helper = pctx->screen->transfer_helper; 492 493 if (handle_transfer(ptrans->resource)) { 494 struct u_transfer *trans = u_transfer(ptrans); 495 496 if (!(ptrans->usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) { 497 struct pipe_box box; 498 u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box); 499 flush_region(pctx, ptrans, &box); 500 } 501 502 /* in MSAA case, there could be multiple levels of wrapping 503 * so don't call helper->vtbl->transfer_unmap() directly 504 */ 505 if (trans->ss) { 506 pctx->transfer_unmap(pctx, trans->trans); 507 pipe_resource_reference(&trans->ss, NULL); 508 } else { 509 helper->vtbl->transfer_unmap(pctx, trans->trans); 510 if (trans->trans2) 511 helper->vtbl->transfer_unmap(pctx, trans->trans2); 512 } 513 514 free(trans); 515 } else { 516 helper->vtbl->transfer_unmap(pctx, ptrans); 517 } 518} 519 520struct u_transfer_helper * 521u_transfer_helper_create(const struct u_transfer_vtbl *vtbl, 522 bool separate_z32s8, 523 bool separate_stencil, 524 bool fake_rgtc, 525 bool msaa_map) 526{ 527 struct u_transfer_helper *helper = calloc(1, sizeof(*helper)); 528 529 helper->vtbl = vtbl; 530 helper->separate_z32s8 = separate_z32s8; 531 helper->separate_stencil = separate_stencil; 532 helper->fake_rgtc = fake_rgtc; 533 helper->msaa_map = msaa_map; 534 535 return helper; 536} 537 538void 539u_transfer_helper_destroy(struct u_transfer_helper *helper) 540{ 541 free(helper); 542} 543