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/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" 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_MAP_READ) && 152 !(usage & (PIPE_MAP_DISCARD_WHOLE_RESOURCE | PIPE_MAP_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->texture_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 (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, 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 } 331 } else { 332 unreachable("bleh"); 333 } 334 335 *pptrans = ptrans; 336 return trans->staging; 337 338fail: 339 if (trans->trans) 340 helper->vtbl->transfer_unmap(pctx, trans->trans); 341 if (trans->trans2) 342 helper->vtbl->transfer_unmap(pctx, trans->trans2); 343 pipe_resource_reference(&ptrans->resource, NULL); 344 free(trans->staging); 345 free(trans); 346 return NULL; 347} 348 349static void 350flush_region(struct pipe_context *pctx, struct pipe_transfer *ptrans, 351 const struct pipe_box *box) 352{ 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; 359 void *src, *dst; 360 361 if (!(ptrans->usage & PIPE_MAP_WRITE)) 362 return; 363 364 if (trans->ss) { 365 struct pipe_blit_info blit; 366 memset(&blit, 0, sizeof(blit)); 367 368 blit.src.resource = trans->ss; 369 blit.src.format = trans->ss->format; 370 blit.src.box = *box; 371 372 blit.dst.resource = ptrans->resource; 373 blit.dst.format = ptrans->resource->format; 374 blit.dst.level = ptrans->level; 375 376 u_box_2d(ptrans->box.x + box->x, 377 ptrans->box.y + box->y, 378 box->width, box->height, 379 &blit.dst.box); 380 381 blit.mask = util_format_get_mask(ptrans->resource->format); 382 blit.filter = PIPE_TEX_FILTER_NEAREST; 383 384 pctx->blit(pctx, &blit); 385 386 return; 387 } 388 389 iformat = helper->vtbl->get_internal_format(ptrans->resource); 390 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)); 397 398 switch (format) { 399 case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: 400 util_format_z32_float_s8x24_uint_unpack_z_float(dst, 401 trans->trans->stride, 402 src, 403 ptrans->stride, 404 width, height); 405 FALLTHROUGH; 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)); 410 411 util_format_z32_float_s8x24_uint_unpack_s_8uint(dst, 412 trans->trans2->stride, 413 src, 414 ptrans->stride, 415 width, height); 416 break; 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, 420 src, ptrans->stride, 421 width, height); 422 FALLTHROUGH; 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)); 427 428 util_format_z24_unorm_s8_uint_unpack_s_8uint(dst, trans->trans2->stride, 429 src, ptrans->stride, 430 width, height); 431 break; 432 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, 439 src, 440 ptrans->stride, 441 width, height); 442 break; 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, 449 src, 450 ptrans->stride, 451 width, height); 452 break; 453 default: 454 assert(!"Unexpected staging transfer type"); 455 break; 456 } 457} 458 459void 460u_transfer_helper_transfer_flush_region(struct pipe_context *pctx, 461 struct pipe_transfer *ptrans, 462 const struct pipe_box *box) 463{ 464 struct u_transfer_helper *helper = pctx->screen->transfer_helper; 465 466 if (handle_transfer(ptrans->resource)) { 467 struct u_transfer *trans = u_transfer(ptrans); 468 469 flush_region(pctx, ptrans, box); 470 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() 474 */ 475 if (trans->ss) { 476 pctx->transfer_flush_region(pctx, trans->trans, box); 477 return; 478 } 479 480 helper->vtbl->transfer_flush_region(pctx, trans->trans, box); 481 if (trans->trans2) 482 helper->vtbl->transfer_flush_region(pctx, trans->trans2, box); 483 484 } else { 485 helper->vtbl->transfer_flush_region(pctx, ptrans, box); 486 } 487} 488 489void 490u_transfer_helper_transfer_unmap(struct pipe_context *pctx, 491 struct pipe_transfer *ptrans) 492{ 493 struct u_transfer_helper *helper = pctx->screen->transfer_helper; 494 495 if (handle_transfer(ptrans->resource)) { 496 struct u_transfer *trans = u_transfer(ptrans); 497 498 if (!(ptrans->usage & PIPE_MAP_FLUSH_EXPLICIT)) { 499 struct pipe_box box; 500 u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box); 501 flush_region(pctx, ptrans, &box); 502 } 503 504 /* in MSAA case, there could be multiple levels of wrapping 505 * so don't call helper->vtbl->transfer_unmap() directly 506 */ 507 if (trans->ss) { 508 pctx->texture_unmap(pctx, trans->trans); 509 pipe_resource_reference(&trans->ss, NULL); 510 } else { 511 helper->vtbl->transfer_unmap(pctx, trans->trans); 512 if (trans->trans2) 513 helper->vtbl->transfer_unmap(pctx, trans->trans2); 514 } 515 516 pipe_resource_reference(&ptrans->resource, NULL); 517 518 free(trans->staging); 519 free(trans); 520 } else { 521 helper->vtbl->transfer_unmap(pctx, ptrans); 522 } 523} 524 525struct u_transfer_helper * 526u_transfer_helper_create(const struct u_transfer_vtbl *vtbl, 527 bool separate_z32s8, 528 bool separate_stencil, 529 bool fake_rgtc, 530 bool msaa_map) 531{ 532 struct u_transfer_helper *helper = calloc(1, sizeof(*helper)); 533 534 helper->vtbl = vtbl; 535 helper->separate_z32s8 = separate_z32s8; 536 helper->separate_stencil = separate_stencil; 537 helper->fake_rgtc = fake_rgtc; 538 helper->msaa_map = msaa_map; 539 540 return helper; 541} 542 543void 544u_transfer_helper_destroy(struct u_transfer_helper *helper) 545{ 546 free(helper); 547} 548 549 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 552 * 553 * drivers should expect to be passed the same buffer repeatedly with the format changed 554 * to indicate which component is being mapped 555 */ 556void * 557u_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) 562{ 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; 569 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); 573 574 debug_assert(box->depth == 1); 575 576 trans = calloc(1, sizeof(*trans)); 577 if (!trans) 578 return NULL; 579 580 ptrans = &trans->base; 581 pipe_resource_reference(&ptrans->resource, prsc); 582 ptrans->level = level; 583 ptrans->usage = usage; 584 ptrans->box = *box; 585 ptrans->stride = util_format_get_stride(format, box->width); 586 ptrans->layer_stride = ptrans->stride * box->height; 587 588 trans->staging = malloc(ptrans->layer_stride); 589 if (!trans->staging) 590 goto fail; 591 592 trans->ptr = helper->vtbl->transfer_map(pctx, prsc, level, usage | PIPE_MAP_DEPTH_ONLY, box, 593 &trans->trans); 594 if (!trans->ptr) 595 goto fail; 596 597 trans->ptr2 = helper->vtbl->transfer_map(pctx, prsc, level, 598 usage | PIPE_MAP_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, 603 ptrans->stride, 604 trans->ptr, 605 trans->trans->stride, 606 width, height); 607 util_format_z32_float_s8x24_uint_pack_s_8uint(trans->staging, 608 ptrans->stride, 609 trans->ptr2, 610 trans->trans2->stride, 611 width, height); 612 break; 613 case PIPE_FORMAT_Z24_UNORM_S8_UINT: 614 util_format_z24_unorm_s8_uint_pack_separate(trans->staging, 615 ptrans->stride, 616 trans->ptr, 617 trans->trans->stride, 618 trans->ptr2, 619 trans->trans2->stride, 620 width, height); 621 break; 622 default: 623 unreachable("Unexpected format"); 624 } 625 } 626 627 *pptrans = ptrans; 628 return trans->staging; 629 630fail: 631 if (trans->trans) 632 helper->vtbl->transfer_unmap(pctx, trans->trans); 633 if (trans->trans2) 634 helper->vtbl->transfer_unmap(pctx, trans->trans2); 635 pipe_resource_reference(&ptrans->resource, NULL); 636 free(trans->staging); 637 free(trans); 638 return NULL; 639} 640 641void 642u_transfer_helper_deinterleave_transfer_unmap(struct pipe_context *pctx, 643 struct pipe_transfer *ptrans) 644{ 645 struct u_transfer_helper *helper = pctx->screen->transfer_helper; 646 enum pipe_format format = ptrans->resource->format; 647 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; 651 652 if (!(ptrans->usage & PIPE_MAP_FLUSH_EXPLICIT)) { 653 struct pipe_box box; 654 u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box); 655 flush_region(pctx, ptrans, &box); 656 } 657 658 helper->vtbl->transfer_unmap(pctx, trans->trans); 659 if (trans->trans2) 660 helper->vtbl->transfer_unmap(pctx, trans->trans2); 661 662 pipe_resource_reference(&ptrans->resource, NULL); 663 664 free(trans->staging); 665 free(trans); 666 } else { 667 helper->vtbl->transfer_unmap(pctx, ptrans); 668 } 669} 670