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 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 * 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 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 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 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 * 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 499 u_transfer_helper_destroy(struct u_transfer_helper *helper) 500 { 501 free(helper); 502 } 503