vc4_resource.c revision 7ec681f3
1/* 2 * Copyright © 2014 Broadcom 3 * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#include "pipe/p_defines.h" 26#include "util/u_memory.h" 27#include "util/format/u_format.h" 28#include "util/u_inlines.h" 29#include "util/u_surface.h" 30#include "util/u_transfer_helper.h" 31#include "util/u_upload_mgr.h" 32#include "util/u_drm.h" 33 34#include "drm-uapi/drm_fourcc.h" 35#include "drm-uapi/vc4_drm.h" 36#include "vc4_screen.h" 37#include "vc4_context.h" 38#include "vc4_resource.h" 39#include "vc4_tiling.h" 40 41static bool 42vc4_resource_bo_alloc(struct vc4_resource *rsc) 43{ 44 struct pipe_resource *prsc = &rsc->base; 45 struct pipe_screen *pscreen = prsc->screen; 46 struct vc4_bo *bo; 47 48 if (vc4_debug & VC4_DEBUG_SURFACE) { 49 fprintf(stderr, "alloc %p: size %d + offset %d -> %d\n", 50 rsc, 51 rsc->slices[0].size, 52 rsc->slices[0].offset, 53 rsc->slices[0].offset + 54 rsc->slices[0].size + 55 rsc->cube_map_stride * (prsc->array_size - 1)); 56 } 57 58 bo = vc4_bo_alloc(vc4_screen(pscreen), 59 rsc->slices[0].offset + 60 rsc->slices[0].size + 61 rsc->cube_map_stride * (prsc->array_size - 1), 62 "resource"); 63 if (bo) { 64 vc4_bo_unreference(&rsc->bo); 65 rsc->bo = bo; 66 return true; 67 } else { 68 return false; 69 } 70} 71 72static void 73vc4_resource_transfer_unmap(struct pipe_context *pctx, 74 struct pipe_transfer *ptrans) 75{ 76 struct vc4_context *vc4 = vc4_context(pctx); 77 struct vc4_transfer *trans = vc4_transfer(ptrans); 78 79 if (trans->map) { 80 struct vc4_resource *rsc = vc4_resource(ptrans->resource); 81 struct vc4_resource_slice *slice = &rsc->slices[ptrans->level]; 82 83 if (ptrans->usage & PIPE_MAP_WRITE) { 84 vc4_store_tiled_image(rsc->bo->map + slice->offset + 85 ptrans->box.z * rsc->cube_map_stride, 86 slice->stride, 87 trans->map, ptrans->stride, 88 slice->tiling, rsc->cpp, 89 &ptrans->box); 90 } 91 free(trans->map); 92 } 93 94 pipe_resource_reference(&ptrans->resource, NULL); 95 slab_free(&vc4->transfer_pool, ptrans); 96} 97 98static void * 99vc4_resource_transfer_map(struct pipe_context *pctx, 100 struct pipe_resource *prsc, 101 unsigned level, unsigned usage, 102 const struct pipe_box *box, 103 struct pipe_transfer **pptrans) 104{ 105 struct vc4_context *vc4 = vc4_context(pctx); 106 struct vc4_resource *rsc = vc4_resource(prsc); 107 struct vc4_transfer *trans; 108 struct pipe_transfer *ptrans; 109 enum pipe_format format = prsc->format; 110 char *buf; 111 112 /* Upgrade DISCARD_RANGE to WHOLE_RESOURCE if the whole resource is 113 * being mapped. 114 */ 115 if ((usage & PIPE_MAP_DISCARD_RANGE) && 116 !(usage & PIPE_MAP_UNSYNCHRONIZED) && 117 !(prsc->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT) && 118 prsc->last_level == 0 && 119 prsc->width0 == box->width && 120 prsc->height0 == box->height && 121 prsc->depth0 == box->depth && 122 prsc->array_size == 1 && 123 rsc->bo->private) { 124 usage |= PIPE_MAP_DISCARD_WHOLE_RESOURCE; 125 } 126 127 if (usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) { 128 if (vc4_resource_bo_alloc(rsc)) { 129 /* If it might be bound as one of our vertex buffers, 130 * make sure we re-emit vertex buffer state. 131 */ 132 if (prsc->bind & PIPE_BIND_VERTEX_BUFFER) 133 vc4->dirty |= VC4_DIRTY_VTXBUF; 134 } else { 135 /* If we failed to reallocate, flush users so that we 136 * don't violate any syncing requirements. 137 */ 138 vc4_flush_jobs_reading_resource(vc4, prsc); 139 } 140 } else if (!(usage & PIPE_MAP_UNSYNCHRONIZED)) { 141 /* If we're writing and the buffer is being used by the CL, we 142 * have to flush the CL first. If we're only reading, we need 143 * to flush if the CL has written our buffer. 144 */ 145 if (usage & PIPE_MAP_WRITE) 146 vc4_flush_jobs_reading_resource(vc4, prsc); 147 else 148 vc4_flush_jobs_writing_resource(vc4, prsc); 149 } 150 151 if (usage & PIPE_MAP_WRITE) { 152 rsc->writes++; 153 rsc->initialized_buffers = ~0; 154 } 155 156 trans = slab_alloc(&vc4->transfer_pool); 157 if (!trans) 158 return NULL; 159 160 /* XXX: Handle DONTBLOCK, DISCARD_RANGE, PERSISTENT, COHERENT. */ 161 162 /* slab_alloc_st() doesn't zero: */ 163 memset(trans, 0, sizeof(*trans)); 164 ptrans = &trans->base; 165 166 pipe_resource_reference(&ptrans->resource, prsc); 167 ptrans->level = level; 168 ptrans->usage = usage; 169 ptrans->box = *box; 170 171 if (usage & PIPE_MAP_UNSYNCHRONIZED) 172 buf = vc4_bo_map_unsynchronized(rsc->bo); 173 else 174 buf = vc4_bo_map(rsc->bo); 175 if (!buf) { 176 fprintf(stderr, "Failed to map bo\n"); 177 goto fail; 178 } 179 180 *pptrans = ptrans; 181 182 struct vc4_resource_slice *slice = &rsc->slices[level]; 183 if (rsc->tiled) { 184 /* No direct mappings of tiled, since we need to manually 185 * tile/untile. 186 */ 187 if (usage & PIPE_MAP_DIRECTLY) 188 return NULL; 189 190 if (format == PIPE_FORMAT_ETC1_RGB8) { 191 /* ETC1 is arranged as 64-bit blocks, where each block 192 * is 4x4 pixels. Texture tiling operates on the 193 * 64-bit block the way it would an uncompressed 194 * pixels. 195 */ 196 assert(!(ptrans->box.x & 3)); 197 assert(!(ptrans->box.y & 3)); 198 ptrans->box.x >>= 2; 199 ptrans->box.y >>= 2; 200 ptrans->box.width = (ptrans->box.width + 3) >> 2; 201 ptrans->box.height = (ptrans->box.height + 3) >> 2; 202 } 203 204 ptrans->stride = ptrans->box.width * rsc->cpp; 205 ptrans->layer_stride = ptrans->stride * ptrans->box.height; 206 207 trans->map = malloc(ptrans->layer_stride * ptrans->box.depth); 208 209 if (usage & PIPE_MAP_READ) { 210 vc4_load_tiled_image(trans->map, ptrans->stride, 211 buf + slice->offset + 212 ptrans->box.z * rsc->cube_map_stride, 213 slice->stride, 214 slice->tiling, rsc->cpp, 215 &ptrans->box); 216 } 217 return trans->map; 218 } else { 219 ptrans->stride = slice->stride; 220 ptrans->layer_stride = ptrans->stride; 221 222 return buf + slice->offset + 223 ptrans->box.y / util_format_get_blockheight(format) * ptrans->stride + 224 ptrans->box.x / util_format_get_blockwidth(format) * rsc->cpp + 225 ptrans->box.z * rsc->cube_map_stride; 226 } 227 228 229fail: 230 vc4_resource_transfer_unmap(pctx, ptrans); 231 return NULL; 232} 233 234static void 235vc4_texture_subdata(struct pipe_context *pctx, 236 struct pipe_resource *prsc, 237 unsigned level, 238 unsigned usage, 239 const struct pipe_box *box, 240 const void *data, 241 unsigned stride, 242 unsigned layer_stride) 243{ 244 struct vc4_resource *rsc = vc4_resource(prsc); 245 struct vc4_resource_slice *slice = &rsc->slices[level]; 246 247 /* For a direct mapping, we can just take the u_transfer path. */ 248 if (!rsc->tiled || 249 box->depth != 1 || 250 (usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE)) { 251 return u_default_texture_subdata(pctx, prsc, level, usage, box, 252 data, stride, layer_stride); 253 } 254 255 /* Otherwise, map and store the texture data directly into the tiled 256 * texture. 257 */ 258 void *buf; 259 if (usage & PIPE_MAP_UNSYNCHRONIZED) 260 buf = vc4_bo_map_unsynchronized(rsc->bo); 261 else 262 buf = vc4_bo_map(rsc->bo); 263 264 vc4_store_tiled_image(buf + slice->offset + 265 box->z * rsc->cube_map_stride, 266 slice->stride, 267 (void *)data, stride, 268 slice->tiling, rsc->cpp, 269 box); 270} 271 272static void 273vc4_resource_destroy(struct pipe_screen *pscreen, 274 struct pipe_resource *prsc) 275{ 276 struct vc4_screen *screen = vc4_screen(pscreen); 277 struct vc4_resource *rsc = vc4_resource(prsc); 278 vc4_bo_unreference(&rsc->bo); 279 280 if (rsc->scanout) 281 renderonly_scanout_destroy(rsc->scanout, screen->ro); 282 283 free(rsc); 284} 285 286static uint64_t 287vc4_resource_modifier(struct vc4_resource *rsc) 288{ 289 if (rsc->tiled) 290 return DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED; 291 else 292 return DRM_FORMAT_MOD_LINEAR; 293} 294 295static bool 296vc4_resource_get_handle(struct pipe_screen *pscreen, 297 struct pipe_context *pctx, 298 struct pipe_resource *prsc, 299 struct winsys_handle *whandle, 300 unsigned usage) 301{ 302 struct vc4_screen *screen = vc4_screen(pscreen); 303 struct vc4_resource *rsc = vc4_resource(prsc); 304 305 whandle->stride = rsc->slices[0].stride; 306 whandle->offset = 0; 307 whandle->modifier = vc4_resource_modifier(rsc); 308 309 /* If we're passing some reference to our BO out to some other part of 310 * the system, then we can't do any optimizations about only us being 311 * the ones seeing it (like BO caching or shadow update avoidance). 312 */ 313 rsc->bo->private = false; 314 315 switch (whandle->type) { 316 case WINSYS_HANDLE_TYPE_SHARED: 317 if (screen->ro) { 318 /* This could probably be supported, assuming that a 319 * control node was used for pl111. 320 */ 321 fprintf(stderr, "flink unsupported with pl111\n"); 322 return false; 323 } 324 325 return vc4_bo_flink(rsc->bo, &whandle->handle); 326 case WINSYS_HANDLE_TYPE_KMS: 327 if (screen->ro) { 328 return renderonly_get_handle(rsc->scanout, whandle); 329 } 330 whandle->handle = rsc->bo->handle; 331 return true; 332 case WINSYS_HANDLE_TYPE_FD: 333 /* FDs are cross-device, so we can export directly from vc4. 334 */ 335 whandle->handle = vc4_bo_get_dmabuf(rsc->bo); 336 return whandle->handle != -1; 337 } 338 339 return false; 340} 341 342static bool 343vc4_resource_get_param(struct pipe_screen *pscreen, 344 struct pipe_context *pctx, struct pipe_resource *prsc, 345 unsigned plane, unsigned layer, unsigned level, 346 enum pipe_resource_param param, 347 unsigned usage, uint64_t *value) 348{ 349 struct vc4_resource *rsc = vc4_resource(prsc); 350 351 switch (param) { 352 case PIPE_RESOURCE_PARAM_STRIDE: 353 *value = rsc->slices[level].stride; 354 return true; 355 case PIPE_RESOURCE_PARAM_OFFSET: 356 *value = 0; 357 return true; 358 case PIPE_RESOURCE_PARAM_MODIFIER: 359 *value = vc4_resource_modifier(rsc); 360 return true; 361 default: 362 return false; 363 } 364} 365 366static void 367vc4_setup_slices(struct vc4_resource *rsc, const char *caller) 368{ 369 struct pipe_resource *prsc = &rsc->base; 370 uint32_t width = prsc->width0; 371 uint32_t height = prsc->height0; 372 if (prsc->format == PIPE_FORMAT_ETC1_RGB8) { 373 width = (width + 3) >> 2; 374 height = (height + 3) >> 2; 375 } 376 377 uint32_t pot_width = util_next_power_of_two(width); 378 uint32_t pot_height = util_next_power_of_two(height); 379 uint32_t offset = 0; 380 uint32_t utile_w = vc4_utile_width(rsc->cpp); 381 uint32_t utile_h = vc4_utile_height(rsc->cpp); 382 383 for (int i = prsc->last_level; i >= 0; i--) { 384 struct vc4_resource_slice *slice = &rsc->slices[i]; 385 386 uint32_t level_width, level_height; 387 if (i == 0) { 388 level_width = width; 389 level_height = height; 390 } else { 391 level_width = u_minify(pot_width, i); 392 level_height = u_minify(pot_height, i); 393 } 394 395 if (!rsc->tiled) { 396 slice->tiling = VC4_TILING_FORMAT_LINEAR; 397 if (prsc->nr_samples > 1) { 398 /* MSAA (4x) surfaces are stored as raw tile buffer contents. */ 399 level_width = align(level_width, 32); 400 level_height = align(level_height, 32); 401 } else { 402 level_width = align(level_width, utile_w); 403 } 404 } else { 405 if (vc4_size_is_lt(level_width, level_height, 406 rsc->cpp)) { 407 slice->tiling = VC4_TILING_FORMAT_LT; 408 level_width = align(level_width, utile_w); 409 level_height = align(level_height, utile_h); 410 } else { 411 slice->tiling = VC4_TILING_FORMAT_T; 412 level_width = align(level_width, 413 4 * 2 * utile_w); 414 level_height = align(level_height, 415 4 * 2 * utile_h); 416 } 417 } 418 419 slice->offset = offset; 420 slice->stride = (level_width * rsc->cpp * 421 MAX2(prsc->nr_samples, 1)); 422 slice->size = level_height * slice->stride; 423 424 offset += slice->size; 425 426 if (vc4_debug & VC4_DEBUG_SURFACE) { 427 static const char tiling_chars[] = { 428 [VC4_TILING_FORMAT_LINEAR] = 'R', 429 [VC4_TILING_FORMAT_LT] = 'L', 430 [VC4_TILING_FORMAT_T] = 'T' 431 }; 432 fprintf(stderr, 433 "rsc %s %p (format %s: vc4 %d), %dx%d: " 434 "level %d (%c) -> %dx%d, stride %d@0x%08x\n", 435 caller, rsc, 436 util_format_short_name(prsc->format), 437 rsc->vc4_format, 438 prsc->width0, prsc->height0, 439 i, tiling_chars[slice->tiling], 440 level_width, level_height, 441 slice->stride, slice->offset); 442 } 443 } 444 445 /* The texture base pointer that has to point to level 0 doesn't have 446 * intra-page bits, so we have to align it, and thus shift up all the 447 * smaller slices. 448 */ 449 uint32_t page_align_offset = (align(rsc->slices[0].offset, 4096) - 450 rsc->slices[0].offset); 451 if (page_align_offset) { 452 for (int i = 0; i <= prsc->last_level; i++) 453 rsc->slices[i].offset += page_align_offset; 454 } 455 456 /* Cube map faces appear as whole miptrees at a page-aligned offset 457 * from the first face's miptree. 458 */ 459 if (prsc->target == PIPE_TEXTURE_CUBE) { 460 rsc->cube_map_stride = align(rsc->slices[0].offset + 461 rsc->slices[0].size, 4096); 462 } 463} 464 465static struct vc4_resource * 466vc4_resource_setup(struct pipe_screen *pscreen, 467 const struct pipe_resource *tmpl) 468{ 469 struct vc4_resource *rsc = CALLOC_STRUCT(vc4_resource); 470 if (!rsc) 471 return NULL; 472 struct pipe_resource *prsc = &rsc->base; 473 474 *prsc = *tmpl; 475 476 pipe_reference_init(&prsc->reference, 1); 477 prsc->screen = pscreen; 478 479 if (prsc->nr_samples <= 1) 480 rsc->cpp = util_format_get_blocksize(tmpl->format); 481 else 482 rsc->cpp = sizeof(uint32_t); 483 484 assert(rsc->cpp); 485 486 return rsc; 487} 488 489static enum vc4_texture_data_type 490get_resource_texture_format(struct pipe_resource *prsc) 491{ 492 struct vc4_resource *rsc = vc4_resource(prsc); 493 uint8_t format = vc4_get_tex_format(prsc->format); 494 495 if (!rsc->tiled) { 496 if (prsc->nr_samples > 1) { 497 return ~0; 498 } else { 499 if (format == VC4_TEXTURE_TYPE_RGBA8888) 500 return VC4_TEXTURE_TYPE_RGBA32R; 501 else 502 return ~0; 503 } 504 } 505 506 return format; 507} 508 509static struct pipe_resource * 510vc4_resource_create_with_modifiers(struct pipe_screen *pscreen, 511 const struct pipe_resource *tmpl, 512 const uint64_t *modifiers, 513 int count) 514{ 515 struct vc4_screen *screen = vc4_screen(pscreen); 516 struct vc4_resource *rsc = vc4_resource_setup(pscreen, tmpl); 517 struct pipe_resource *prsc = &rsc->base; 518 bool linear_ok = drm_find_modifier(DRM_FORMAT_MOD_LINEAR, modifiers, count); 519 /* Use a tiled layout if we can, for better 3D performance. */ 520 bool should_tile = true; 521 522 /* VBOs/PBOs are untiled (and 1 height). */ 523 if (tmpl->target == PIPE_BUFFER) 524 should_tile = false; 525 526 /* MSAA buffers are linear. */ 527 if (tmpl->nr_samples > 1) 528 should_tile = false; 529 530 /* No tiling when we're sharing with another device (pl111). */ 531 if (screen->ro && (tmpl->bind & PIPE_BIND_SCANOUT)) 532 should_tile = false; 533 534 /* Cursors are always linear, and the user can request linear as well. 535 */ 536 if (tmpl->bind & (PIPE_BIND_LINEAR | PIPE_BIND_CURSOR)) 537 should_tile = false; 538 539 /* No shared objects with LT format -- the kernel only has T-format 540 * metadata. LT objects are small enough it's not worth the trouble to 541 * give them metadata to tile. 542 */ 543 if ((tmpl->bind & (PIPE_BIND_SHARED | PIPE_BIND_SCANOUT)) && 544 vc4_size_is_lt(prsc->width0, prsc->height0, rsc->cpp)) 545 should_tile = false; 546 547 /* If we're sharing or scanning out, we need the ioctl present to 548 * inform the kernel or the other side. 549 */ 550 if ((tmpl->bind & (PIPE_BIND_SHARED | 551 PIPE_BIND_SCANOUT)) && !screen->has_tiling_ioctl) 552 should_tile = false; 553 554 /* No user-specified modifier; determine our own. */ 555 if (count == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { 556 linear_ok = true; 557 rsc->tiled = should_tile; 558 } else if (should_tile && 559 drm_find_modifier(DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED, 560 modifiers, count)) { 561 rsc->tiled = true; 562 } else if (linear_ok) { 563 rsc->tiled = false; 564 } else { 565 fprintf(stderr, "Unsupported modifier requested\n"); 566 return NULL; 567 } 568 569 if (tmpl->target != PIPE_BUFFER) 570 rsc->vc4_format = get_resource_texture_format(prsc); 571 572 vc4_setup_slices(rsc, "create"); 573 if (!vc4_resource_bo_alloc(rsc)) 574 goto fail; 575 576 if (screen->has_tiling_ioctl) { 577 uint64_t modifier; 578 if (rsc->tiled) 579 modifier = DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED; 580 else 581 modifier = DRM_FORMAT_MOD_LINEAR; 582 struct drm_vc4_set_tiling set_tiling = { 583 .handle = rsc->bo->handle, 584 .modifier = modifier, 585 }; 586 int ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_SET_TILING, 587 &set_tiling); 588 if (ret != 0) 589 goto fail; 590 } 591 592 /* Set up the "scanout resource" (the dmabuf export of our buffer to 593 * the KMS handle) if the buffer might ever have 594 * resource_get_handle(WINSYS_HANDLE_TYPE_KMS) called on it. 595 * create_with_modifiers() doesn't give us usage flags, so we have to 596 * assume that all calls with modifiers are scanout-possible. 597 */ 598 if (screen->ro && 599 ((tmpl->bind & PIPE_BIND_SCANOUT) || 600 !(count == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID))) { 601 rsc->scanout = 602 renderonly_scanout_for_resource(prsc, screen->ro, NULL); 603 if (!rsc->scanout) 604 goto fail; 605 } 606 607 vc4_bo_label(screen, rsc->bo, "%sresource %dx%d@%d/%d", 608 (tmpl->bind & PIPE_BIND_SCANOUT) ? "scanout " : "", 609 tmpl->width0, tmpl->height0, 610 rsc->cpp * 8, prsc->last_level); 611 612 return prsc; 613fail: 614 vc4_resource_destroy(pscreen, prsc); 615 return NULL; 616} 617 618struct pipe_resource * 619vc4_resource_create(struct pipe_screen *pscreen, 620 const struct pipe_resource *tmpl) 621{ 622 const uint64_t mod = DRM_FORMAT_MOD_INVALID; 623 return vc4_resource_create_with_modifiers(pscreen, tmpl, &mod, 1); 624} 625 626static struct pipe_resource * 627vc4_resource_from_handle(struct pipe_screen *pscreen, 628 const struct pipe_resource *tmpl, 629 struct winsys_handle *whandle, 630 unsigned usage) 631{ 632 struct vc4_screen *screen = vc4_screen(pscreen); 633 struct vc4_resource *rsc = vc4_resource_setup(pscreen, tmpl); 634 struct pipe_resource *prsc = &rsc->base; 635 struct vc4_resource_slice *slice = &rsc->slices[0]; 636 637 if (!rsc) 638 return NULL; 639 640 switch (whandle->type) { 641 case WINSYS_HANDLE_TYPE_SHARED: 642 rsc->bo = vc4_bo_open_name(screen, whandle->handle); 643 break; 644 case WINSYS_HANDLE_TYPE_FD: 645 rsc->bo = vc4_bo_open_dmabuf(screen, whandle->handle); 646 break; 647 default: 648 fprintf(stderr, 649 "Attempt to import unsupported handle type %d\n", 650 whandle->type); 651 } 652 653 if (!rsc->bo) 654 goto fail; 655 656 struct drm_vc4_get_tiling get_tiling = { 657 .handle = rsc->bo->handle, 658 }; 659 int ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_GET_TILING, &get_tiling); 660 661 if (ret != 0) { 662 whandle->modifier = DRM_FORMAT_MOD_LINEAR; 663 } else if (whandle->modifier == DRM_FORMAT_MOD_INVALID) { 664 whandle->modifier = get_tiling.modifier; 665 } else if (whandle->modifier != get_tiling.modifier) { 666 fprintf(stderr, 667 "Modifier 0x%llx vs. tiling (0x%llx) mismatch\n", 668 (long long)whandle->modifier, get_tiling.modifier); 669 goto fail; 670 } 671 672 switch (whandle->modifier) { 673 case DRM_FORMAT_MOD_LINEAR: 674 rsc->tiled = false; 675 break; 676 case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: 677 rsc->tiled = true; 678 break; 679 default: 680 fprintf(stderr, 681 "Attempt to import unsupported modifier 0x%llx\n", 682 (long long)whandle->modifier); 683 goto fail; 684 } 685 686 rsc->vc4_format = get_resource_texture_format(prsc); 687 vc4_setup_slices(rsc, "import"); 688 689 if (whandle->offset != 0) { 690 if (rsc->tiled) { 691 fprintf(stderr, 692 "Attempt to import unsupported " 693 "winsys offset %u\n", 694 whandle->offset); 695 goto fail; 696 } 697 698 rsc->slices[0].offset += whandle->offset; 699 700 if (rsc->slices[0].offset + rsc->slices[0].size > 701 rsc->bo->size) { 702 fprintf(stderr, "Attempt to import " 703 "with overflowing offset (%d + %d > %d)\n", 704 whandle->offset, 705 rsc->slices[0].size, 706 rsc->bo->size); 707 goto fail; 708 } 709 } 710 711 if (screen->ro) { 712 /* Make sure that renderonly has a handle to our buffer in the 713 * display's fd, so that a later renderonly_get_handle() 714 * returns correct handles or GEM names. 715 */ 716 rsc->scanout = 717 renderonly_create_gpu_import_for_resource(prsc, 718 screen->ro, 719 NULL); 720 } 721 722 if (rsc->tiled && whandle->stride != slice->stride) { 723 static bool warned = false; 724 if (!warned) { 725 warned = true; 726 fprintf(stderr, 727 "Attempting to import %dx%d %s with " 728 "unsupported stride %d instead of %d\n", 729 prsc->width0, prsc->height0, 730 util_format_short_name(prsc->format), 731 whandle->stride, 732 slice->stride); 733 } 734 goto fail; 735 } else if (!rsc->tiled) { 736 slice->stride = whandle->stride; 737 } 738 739 return prsc; 740 741fail: 742 vc4_resource_destroy(pscreen, prsc); 743 return NULL; 744} 745 746static struct pipe_surface * 747vc4_create_surface(struct pipe_context *pctx, 748 struct pipe_resource *ptex, 749 const struct pipe_surface *surf_tmpl) 750{ 751 struct vc4_surface *surface = CALLOC_STRUCT(vc4_surface); 752 struct vc4_resource *rsc = vc4_resource(ptex); 753 754 if (!surface) 755 return NULL; 756 757 assert(surf_tmpl->u.tex.first_layer == surf_tmpl->u.tex.last_layer); 758 759 struct pipe_surface *psurf = &surface->base; 760 unsigned level = surf_tmpl->u.tex.level; 761 762 pipe_reference_init(&psurf->reference, 1); 763 pipe_resource_reference(&psurf->texture, ptex); 764 765 psurf->context = pctx; 766 psurf->format = surf_tmpl->format; 767 psurf->width = u_minify(ptex->width0, level); 768 psurf->height = u_minify(ptex->height0, level); 769 psurf->u.tex.level = level; 770 psurf->u.tex.first_layer = surf_tmpl->u.tex.first_layer; 771 psurf->u.tex.last_layer = surf_tmpl->u.tex.last_layer; 772 surface->offset = (rsc->slices[level].offset + 773 psurf->u.tex.first_layer * rsc->cube_map_stride); 774 surface->tiling = rsc->slices[level].tiling; 775 776 return &surface->base; 777} 778 779static void 780vc4_surface_destroy(struct pipe_context *pctx, struct pipe_surface *psurf) 781{ 782 pipe_resource_reference(&psurf->texture, NULL); 783 FREE(psurf); 784} 785 786static void 787vc4_dump_surface_non_msaa(struct pipe_surface *psurf) 788{ 789 struct pipe_resource *prsc = psurf->texture; 790 struct vc4_resource *rsc = vc4_resource(prsc); 791 uint32_t *map = vc4_bo_map(rsc->bo); 792 uint32_t stride = rsc->slices[0].stride / 4; 793 uint32_t width = psurf->width; 794 uint32_t height = psurf->height; 795 uint32_t chunk_w = width / 79; 796 uint32_t chunk_h = height / 40; 797 uint32_t found_colors[10] = { 0 }; 798 uint32_t num_found_colors = 0; 799 800 if (rsc->vc4_format != VC4_TEXTURE_TYPE_RGBA32R) { 801 fprintf(stderr, "%s: Unsupported format %s\n", 802 __func__, util_format_short_name(psurf->format)); 803 return; 804 } 805 806 for (int by = 0; by < height; by += chunk_h) { 807 for (int bx = 0; bx < width; bx += chunk_w) { 808 int all_found_color = -1; /* nothing found */ 809 810 for (int y = by; y < MIN2(height, by + chunk_h); y++) { 811 for (int x = bx; x < MIN2(width, bx + chunk_w); x++) { 812 uint32_t pix = map[y * stride + x]; 813 814 int i; 815 for (i = 0; i < num_found_colors; i++) { 816 if (pix == found_colors[i]) 817 break; 818 } 819 if (i == num_found_colors && 820 num_found_colors < 821 ARRAY_SIZE(found_colors)) { 822 found_colors[num_found_colors++] = pix; 823 } 824 825 if (i < num_found_colors) { 826 if (all_found_color == -1) 827 all_found_color = i; 828 else if (i != all_found_color) 829 all_found_color = ARRAY_SIZE(found_colors); 830 } 831 } 832 } 833 /* If all pixels for this chunk have a consistent 834 * value, then print a character for it. Either a 835 * fixed name (particularly common for piglit tests), 836 * or a runtime-generated number. 837 */ 838 if (all_found_color >= 0 && 839 all_found_color < ARRAY_SIZE(found_colors)) { 840 static const struct { 841 uint32_t val; 842 const char *c; 843 } named_colors[] = { 844 { 0xff000000, "█" }, 845 { 0x00000000, "█" }, 846 { 0xffff0000, "r" }, 847 { 0xff00ff00, "g" }, 848 { 0xff0000ff, "b" }, 849 { 0xffffffff, "w" }, 850 }; 851 int i; 852 for (i = 0; i < ARRAY_SIZE(named_colors); i++) { 853 if (named_colors[i].val == 854 found_colors[all_found_color]) { 855 fprintf(stderr, "%s", 856 named_colors[i].c); 857 break; 858 } 859 } 860 /* For unnamed colors, print a number and the 861 * numbers will have values printed at the 862 * end. 863 */ 864 if (i == ARRAY_SIZE(named_colors)) { 865 fprintf(stderr, "%c", 866 '0' + all_found_color); 867 } 868 } else { 869 /* If there's no consistent color, print this. 870 */ 871 fprintf(stderr, "."); 872 } 873 } 874 fprintf(stderr, "\n"); 875 } 876 877 for (int i = 0; i < num_found_colors; i++) { 878 fprintf(stderr, "color %d: 0x%08x\n", i, found_colors[i]); 879 } 880} 881 882static uint32_t 883vc4_surface_msaa_get_sample(struct pipe_surface *psurf, 884 uint32_t x, uint32_t y, uint32_t sample) 885{ 886 struct pipe_resource *prsc = psurf->texture; 887 struct vc4_resource *rsc = vc4_resource(prsc); 888 uint32_t tile_w = 32, tile_h = 32; 889 uint32_t tiles_w = DIV_ROUND_UP(psurf->width, 32); 890 891 uint32_t tile_x = x / tile_w; 892 uint32_t tile_y = y / tile_h; 893 uint32_t *tile = (vc4_bo_map(rsc->bo) + 894 VC4_TILE_BUFFER_SIZE * (tile_y * tiles_w + tile_x)); 895 uint32_t subtile_x = x % tile_w; 896 uint32_t subtile_y = y % tile_h; 897 898 uint32_t quad_samples = VC4_MAX_SAMPLES * 4; 899 uint32_t tile_stride = quad_samples * tile_w / 2; 900 901 return *((uint32_t *)tile + 902 (subtile_y >> 1) * tile_stride + 903 (subtile_x >> 1) * quad_samples + 904 ((subtile_y & 1) << 1) + 905 (subtile_x & 1) + 906 sample); 907} 908 909static void 910vc4_dump_surface_msaa_char(struct pipe_surface *psurf, 911 uint32_t start_x, uint32_t start_y, 912 uint32_t w, uint32_t h) 913{ 914 bool all_same_color = true; 915 uint32_t all_pix = 0; 916 917 for (int y = start_y; y < start_y + h; y++) { 918 for (int x = start_x; x < start_x + w; x++) { 919 for (int s = 0; s < VC4_MAX_SAMPLES; s++) { 920 uint32_t pix = vc4_surface_msaa_get_sample(psurf, 921 x, y, 922 s); 923 if (x == start_x && y == start_y) 924 all_pix = pix; 925 else if (all_pix != pix) 926 all_same_color = false; 927 } 928 } 929 } 930 if (all_same_color) { 931 static const struct { 932 uint32_t val; 933 const char *c; 934 } named_colors[] = { 935 { 0xff000000, "█" }, 936 { 0x00000000, "█" }, 937 { 0xffff0000, "r" }, 938 { 0xff00ff00, "g" }, 939 { 0xff0000ff, "b" }, 940 { 0xffffffff, "w" }, 941 }; 942 int i; 943 for (i = 0; i < ARRAY_SIZE(named_colors); i++) { 944 if (named_colors[i].val == all_pix) { 945 fprintf(stderr, "%s", 946 named_colors[i].c); 947 return; 948 } 949 } 950 fprintf(stderr, "x"); 951 } else { 952 fprintf(stderr, "."); 953 } 954} 955 956static void 957vc4_dump_surface_msaa(struct pipe_surface *psurf) 958{ 959 uint32_t tile_w = 32, tile_h = 32; 960 uint32_t tiles_w = DIV_ROUND_UP(psurf->width, tile_w); 961 uint32_t tiles_h = DIV_ROUND_UP(psurf->height, tile_h); 962 uint32_t char_w = 140, char_h = 60; 963 uint32_t char_w_per_tile = char_w / tiles_w - 1; 964 uint32_t char_h_per_tile = char_h / tiles_h - 1; 965 966 fprintf(stderr, "Surface: %dx%d (%dx MSAA)\n", 967 psurf->width, psurf->height, psurf->texture->nr_samples); 968 969 for (int x = 0; x < (char_w_per_tile + 1) * tiles_w; x++) 970 fprintf(stderr, "-"); 971 fprintf(stderr, "\n"); 972 973 for (int ty = 0; ty < psurf->height; ty += tile_h) { 974 for (int y = 0; y < char_h_per_tile; y++) { 975 976 for (int tx = 0; tx < psurf->width; tx += tile_w) { 977 for (int x = 0; x < char_w_per_tile; x++) { 978 uint32_t bx1 = (x * tile_w / 979 char_w_per_tile); 980 uint32_t bx2 = ((x + 1) * tile_w / 981 char_w_per_tile); 982 uint32_t by1 = (y * tile_h / 983 char_h_per_tile); 984 uint32_t by2 = ((y + 1) * tile_h / 985 char_h_per_tile); 986 987 vc4_dump_surface_msaa_char(psurf, 988 tx + bx1, 989 ty + by1, 990 bx2 - bx1, 991 by2 - by1); 992 } 993 fprintf(stderr, "|"); 994 } 995 fprintf(stderr, "\n"); 996 } 997 998 for (int x = 0; x < (char_w_per_tile + 1) * tiles_w; x++) 999 fprintf(stderr, "-"); 1000 fprintf(stderr, "\n"); 1001 } 1002} 1003 1004/** Debug routine to dump the contents of an 8888 surface to the console */ 1005void 1006vc4_dump_surface(struct pipe_surface *psurf) 1007{ 1008 if (!psurf) 1009 return; 1010 1011 if (psurf->texture->nr_samples > 1) 1012 vc4_dump_surface_msaa(psurf); 1013 else 1014 vc4_dump_surface_non_msaa(psurf); 1015} 1016 1017static void 1018vc4_flush_resource(struct pipe_context *pctx, struct pipe_resource *resource) 1019{ 1020 /* All calls to flush_resource are followed by a flush of the context, 1021 * so there's nothing to do. 1022 */ 1023} 1024 1025void 1026vc4_update_shadow_baselevel_texture(struct pipe_context *pctx, 1027 struct pipe_sampler_view *pview) 1028{ 1029 struct vc4_context *vc4 = vc4_context(pctx); 1030 struct vc4_sampler_view *view = vc4_sampler_view(pview); 1031 struct vc4_resource *shadow = vc4_resource(view->texture); 1032 struct vc4_resource *orig = vc4_resource(pview->texture); 1033 1034 assert(view->texture != pview->texture); 1035 1036 if (shadow->writes == orig->writes && orig->bo->private) 1037 return; 1038 1039 perf_debug("Updating %dx%d@%d shadow texture due to %s\n", 1040 orig->base.width0, orig->base.height0, 1041 pview->u.tex.first_level, 1042 pview->u.tex.first_level ? "base level" : "raster layout"); 1043 1044 for (int i = 0; i <= shadow->base.last_level; i++) { 1045 unsigned width = u_minify(shadow->base.width0, i); 1046 unsigned height = u_minify(shadow->base.height0, i); 1047 struct pipe_blit_info info = { 1048 .dst = { 1049 .resource = &shadow->base, 1050 .level = i, 1051 .box = { 1052 .x = 0, 1053 .y = 0, 1054 .z = 0, 1055 .width = width, 1056 .height = height, 1057 .depth = 1, 1058 }, 1059 .format = shadow->base.format, 1060 }, 1061 .src = { 1062 .resource = &orig->base, 1063 .level = pview->u.tex.first_level + i, 1064 .box = { 1065 .x = 0, 1066 .y = 0, 1067 .z = 0, 1068 .width = width, 1069 .height = height, 1070 .depth = 1, 1071 }, 1072 .format = orig->base.format, 1073 }, 1074 .mask = ~0, 1075 }; 1076 pctx->blit(pctx, &info); 1077 } 1078 1079 shadow->writes = orig->writes; 1080} 1081 1082/** 1083 * Converts a 4-byte index buffer to 2 bytes. 1084 * 1085 * Since GLES2 only has support for 1 and 2-byte indices, the hardware doesn't 1086 * include 4-byte index support, and we have to shrink it down. 1087 * 1088 * There's no fallback support for when indices end up being larger than 2^16, 1089 * though it will at least assertion fail. Also, if the original index data 1090 * was in user memory, it would be nice to not have uploaded it to a VBO 1091 * before translating. 1092 */ 1093struct pipe_resource * 1094vc4_get_shadow_index_buffer(struct pipe_context *pctx, 1095 const struct pipe_draw_info *info, 1096 uint32_t offset, 1097 uint32_t count, 1098 uint32_t *shadow_offset) 1099{ 1100 struct vc4_context *vc4 = vc4_context(pctx); 1101 struct vc4_resource *orig = vc4_resource(info->index.resource); 1102 perf_debug("Fallback conversion for %d uint indices\n", count); 1103 1104 void *data; 1105 struct pipe_resource *shadow_rsc = NULL; 1106 u_upload_alloc(vc4->uploader, 0, count * 2, 4, 1107 shadow_offset, &shadow_rsc, &data); 1108 uint16_t *dst = data; 1109 1110 struct pipe_transfer *src_transfer = NULL; 1111 const uint32_t *src; 1112 if (info->has_user_indices) { 1113 src = (uint32_t*)((char*)info->index.user + offset); 1114 } else { 1115 src = pipe_buffer_map_range(pctx, &orig->base, 1116 offset, 1117 count * 4, 1118 PIPE_MAP_READ, &src_transfer); 1119 } 1120 1121 for (int i = 0; i < count; i++) { 1122 uint32_t src_index = src[i]; 1123 assert(src_index <= 0xffff); 1124 dst[i] = src_index; 1125 } 1126 1127 if (src_transfer) 1128 pctx->buffer_unmap(pctx, src_transfer); 1129 1130 return shadow_rsc; 1131} 1132 1133static const struct u_transfer_vtbl transfer_vtbl = { 1134 .resource_create = vc4_resource_create, 1135 .resource_destroy = vc4_resource_destroy, 1136 .transfer_map = vc4_resource_transfer_map, 1137 .transfer_unmap = vc4_resource_transfer_unmap, 1138 .transfer_flush_region = u_default_transfer_flush_region, 1139}; 1140 1141void 1142vc4_resource_screen_init(struct pipe_screen *pscreen) 1143{ 1144 struct vc4_screen *screen = vc4_screen(pscreen); 1145 1146 pscreen->resource_create = vc4_resource_create; 1147 pscreen->resource_create_with_modifiers = 1148 vc4_resource_create_with_modifiers; 1149 pscreen->resource_from_handle = vc4_resource_from_handle; 1150 pscreen->resource_get_handle = vc4_resource_get_handle; 1151 pscreen->resource_get_param = vc4_resource_get_param; 1152 pscreen->resource_destroy = vc4_resource_destroy; 1153 pscreen->transfer_helper = u_transfer_helper_create(&transfer_vtbl, 1154 false, false, 1155 false, true); 1156 1157 /* Test if the kernel has GET_TILING; it will return -EINVAL if the 1158 * ioctl does not exist, but -ENOENT if we pass an impossible handle. 1159 * 0 cannot be a valid GEM object, so use that. 1160 */ 1161 struct drm_vc4_get_tiling get_tiling = { 1162 .handle = 0x0, 1163 }; 1164 int ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_GET_TILING, &get_tiling); 1165 if (ret == -1 && errno == ENOENT) 1166 screen->has_tiling_ioctl = true; 1167} 1168 1169void 1170vc4_resource_context_init(struct pipe_context *pctx) 1171{ 1172 pctx->buffer_map = u_transfer_helper_transfer_map; 1173 pctx->texture_map = u_transfer_helper_transfer_map; 1174 pctx->transfer_flush_region = u_transfer_helper_transfer_flush_region; 1175 pctx->buffer_unmap = u_transfer_helper_transfer_unmap; 1176 pctx->texture_unmap = u_transfer_helper_transfer_unmap; 1177 pctx->buffer_subdata = u_default_buffer_subdata; 1178 pctx->texture_subdata = vc4_texture_subdata; 1179 pctx->create_surface = vc4_create_surface; 1180 pctx->surface_destroy = vc4_surface_destroy; 1181 pctx->resource_copy_region = util_resource_copy_region; 1182 pctx->blit = vc4_blit; 1183 pctx->flush_resource = vc4_flush_resource; 1184} 1185