lima_resource.c revision 7ec681f3
1/* 2 * Copyright (c) 2017-2019 Lima Project 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, sub license, 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 12 * next paragraph) shall be included in all copies or substantial portions 13 * of the 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 NON-INFRINGEMENT. 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 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 */ 24 25#include "util/u_memory.h" 26#include "util/u_blitter.h" 27#include "util/format/u_format.h" 28#include "util/u_inlines.h" 29#include "util/u_math.h" 30#include "util/u_debug.h" 31#include "util/u_transfer.h" 32#include "util/u_surface.h" 33#include "util/hash_table.h" 34#include "util/ralloc.h" 35#include "util/u_drm.h" 36#include "renderonly/renderonly.h" 37 38#include "frontend/drm_driver.h" 39 40#include "drm-uapi/drm_fourcc.h" 41#include "drm-uapi/lima_drm.h" 42 43#include "lima_screen.h" 44#include "lima_context.h" 45#include "lima_resource.h" 46#include "lima_bo.h" 47#include "lima_util.h" 48 49#include "pan_minmax_cache.h" 50#include "pan_tiling.h" 51 52static struct pipe_resource * 53lima_resource_create_scanout(struct pipe_screen *pscreen, 54 const struct pipe_resource *templat, 55 unsigned width, unsigned height) 56{ 57 struct lima_screen *screen = lima_screen(pscreen); 58 struct renderonly_scanout *scanout; 59 struct winsys_handle handle; 60 struct pipe_resource *pres; 61 62 struct pipe_resource scanout_templat = *templat; 63 scanout_templat.width0 = width; 64 scanout_templat.height0 = height; 65 scanout_templat.screen = pscreen; 66 67 scanout = renderonly_scanout_for_resource(&scanout_templat, 68 screen->ro, &handle); 69 if (!scanout) 70 return NULL; 71 72 assert(handle.type == WINSYS_HANDLE_TYPE_FD); 73 pres = pscreen->resource_from_handle(pscreen, templat, &handle, 74 PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE); 75 76 close(handle.handle); 77 if (!pres) { 78 renderonly_scanout_destroy(scanout, screen->ro); 79 return NULL; 80 } 81 82 struct lima_resource *res = lima_resource(pres); 83 res->scanout = scanout; 84 85 return pres; 86} 87 88static uint32_t 89setup_miptree(struct lima_resource *res, 90 unsigned width0, unsigned height0, 91 bool should_align_dimensions) 92{ 93 struct pipe_resource *pres = &res->base; 94 unsigned level; 95 unsigned width = width0; 96 unsigned height = height0; 97 unsigned depth = pres->depth0; 98 uint32_t size = 0; 99 100 for (level = 0; level <= pres->last_level; level++) { 101 uint32_t actual_level_size; 102 uint32_t stride; 103 unsigned aligned_width; 104 unsigned aligned_height; 105 106 if (should_align_dimensions) { 107 aligned_width = align(width, 16); 108 aligned_height = align(height, 16); 109 } else { 110 aligned_width = width; 111 aligned_height = height; 112 } 113 114 stride = util_format_get_stride(pres->format, aligned_width); 115 actual_level_size = stride * 116 util_format_get_nblocksy(pres->format, aligned_height) * 117 pres->array_size * depth; 118 119 res->levels[level].width = aligned_width; 120 res->levels[level].stride = stride; 121 res->levels[level].offset = size; 122 res->levels[level].layer_stride = util_format_get_stride(pres->format, align(width, 16)) * align(height, 16); 123 124 if (util_format_is_compressed(pres->format)) 125 res->levels[level].layer_stride /= 4; 126 127 /* The start address of each level except the last level 128 * must be 64-aligned in order to be able to pass the 129 * addresses to the hardware. */ 130 if (level != pres->last_level) 131 size += align(actual_level_size, 64); 132 else 133 size += actual_level_size; /* Save some memory */ 134 135 width = u_minify(width, 1); 136 height = u_minify(height, 1); 137 depth = u_minify(depth, 1); 138 } 139 140 return size; 141} 142 143static struct pipe_resource * 144lima_resource_create_bo(struct pipe_screen *pscreen, 145 const struct pipe_resource *templat, 146 unsigned width, unsigned height, 147 bool should_align_dimensions) 148{ 149 struct lima_screen *screen = lima_screen(pscreen); 150 struct lima_resource *res; 151 struct pipe_resource *pres; 152 153 res = CALLOC_STRUCT(lima_resource); 154 if (!res) 155 return NULL; 156 157 res->base = *templat; 158 res->base.screen = pscreen; 159 pipe_reference_init(&res->base.reference, 1); 160 161 pres = &res->base; 162 163 uint32_t size = setup_miptree(res, width, height, should_align_dimensions); 164 size = align(size, LIMA_PAGE_SIZE); 165 166 res->bo = lima_bo_create(screen, size, 0); 167 if (!res->bo) { 168 FREE(res); 169 return NULL; 170 } 171 172 return pres; 173} 174 175static struct pipe_resource * 176_lima_resource_create_with_modifiers(struct pipe_screen *pscreen, 177 const struct pipe_resource *templat, 178 const uint64_t *modifiers, 179 int count) 180{ 181 struct lima_screen *screen = lima_screen(pscreen); 182 bool should_tile = lima_debug & LIMA_DEBUG_NO_TILING ? false : true; 183 unsigned width, height; 184 bool should_align_dimensions; 185 bool has_user_modifiers = true; 186 187 if (count == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) 188 has_user_modifiers = false; 189 190 /* VBOs/PBOs are untiled (and 1 height). */ 191 if (templat->target == PIPE_BUFFER) 192 should_tile = false; 193 194 if (templat->bind & (PIPE_BIND_LINEAR | PIPE_BIND_SCANOUT)) 195 should_tile = false; 196 197 /* If there's no user modifiers and buffer is shared we use linear */ 198 if (!has_user_modifiers && (templat->bind & PIPE_BIND_SHARED)) 199 should_tile = false; 200 201 if (has_user_modifiers && 202 !drm_find_modifier(DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED, 203 modifiers, count)) 204 should_tile = false; 205 206 if (should_tile || (templat->bind & PIPE_BIND_RENDER_TARGET) || 207 (templat->bind & PIPE_BIND_DEPTH_STENCIL)) { 208 should_align_dimensions = true; 209 width = align(templat->width0, 16); 210 height = align(templat->height0, 16); 211 } 212 else { 213 should_align_dimensions = false; 214 width = templat->width0; 215 height = templat->height0; 216 } 217 218 struct pipe_resource *pres; 219 if (screen->ro && (templat->bind & PIPE_BIND_SCANOUT)) 220 pres = lima_resource_create_scanout(pscreen, templat, width, height); 221 else 222 pres = lima_resource_create_bo(pscreen, templat, width, height, 223 should_align_dimensions); 224 225 if (pres) { 226 struct lima_resource *res = lima_resource(pres); 227 res->tiled = should_tile; 228 229 if (templat->bind & PIPE_BIND_INDEX_BUFFER) 230 res->index_cache = CALLOC_STRUCT(panfrost_minmax_cache); 231 232 debug_printf("%s: pres=%p width=%u height=%u depth=%u target=%d " 233 "bind=%x usage=%d tile=%d last_level=%d\n", __func__, 234 pres, pres->width0, pres->height0, pres->depth0, 235 pres->target, pres->bind, pres->usage, should_tile, templat->last_level); 236 } 237 return pres; 238} 239 240static struct pipe_resource * 241lima_resource_create(struct pipe_screen *pscreen, 242 const struct pipe_resource *templat) 243{ 244 const uint64_t mod = DRM_FORMAT_MOD_INVALID; 245 246 return _lima_resource_create_with_modifiers(pscreen, templat, &mod, 1); 247} 248 249static struct pipe_resource * 250lima_resource_create_with_modifiers(struct pipe_screen *pscreen, 251 const struct pipe_resource *templat, 252 const uint64_t *modifiers, 253 int count) 254{ 255 struct pipe_resource tmpl = *templat; 256 257 /* gbm_bo_create_with_modifiers & gbm_surface_create_with_modifiers 258 * don't have usage parameter, but buffer created by these functions 259 * may be used for scanout. So we assume buffer created by this 260 * function always enable scanout if linear modifier is permitted. 261 */ 262 if (drm_find_modifier(DRM_FORMAT_MOD_LINEAR, modifiers, count)) 263 tmpl.bind |= PIPE_BIND_SCANOUT; 264 265 return _lima_resource_create_with_modifiers(pscreen, &tmpl, modifiers, count); 266} 267 268static void 269lima_resource_destroy(struct pipe_screen *pscreen, struct pipe_resource *pres) 270{ 271 struct lima_screen *screen = lima_screen(pscreen); 272 struct lima_resource *res = lima_resource(pres); 273 274 if (res->bo) 275 lima_bo_unreference(res->bo); 276 277 if (res->scanout) 278 renderonly_scanout_destroy(res->scanout, screen->ro); 279 280 if (res->damage.region) 281 FREE(res->damage.region); 282 283 if (res->index_cache) 284 FREE(res->index_cache); 285 286 FREE(res); 287} 288 289static struct pipe_resource * 290lima_resource_from_handle(struct pipe_screen *pscreen, 291 const struct pipe_resource *templat, 292 struct winsys_handle *handle, unsigned usage) 293{ 294 if (templat->bind & (PIPE_BIND_SAMPLER_VIEW | 295 PIPE_BIND_RENDER_TARGET | 296 PIPE_BIND_DEPTH_STENCIL)) { 297 /* sampler hardware need offset alignment 64, while render hardware 298 * need offset alignment 8, but due to render target may be reloaded 299 * which uses the sampler, set alignment requrement to 64 for all 300 */ 301 if (handle->offset & 0x3f) { 302 debug_error("import buffer offset not properly aligned\n"); 303 return NULL; 304 } 305 } 306 307 struct lima_resource *res = CALLOC_STRUCT(lima_resource); 308 if (!res) 309 return NULL; 310 311 struct pipe_resource *pres = &res->base; 312 *pres = *templat; 313 pres->screen = pscreen; 314 pipe_reference_init(&pres->reference, 1); 315 res->levels[0].offset = handle->offset; 316 res->levels[0].stride = handle->stride; 317 318 struct lima_screen *screen = lima_screen(pscreen); 319 res->bo = lima_bo_import(screen, handle); 320 if (!res->bo) { 321 FREE(res); 322 return NULL; 323 } 324 325 res->modifier_constant = true; 326 327 switch (handle->modifier) { 328 case DRM_FORMAT_MOD_LINEAR: 329 res->tiled = false; 330 break; 331 case DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED: 332 res->tiled = true; 333 break; 334 case DRM_FORMAT_MOD_INVALID: 335 /* Modifier wasn't specified and it's shared buffer. We create these 336 * as linear, so disable tiling. 337 */ 338 res->tiled = false; 339 break; 340 default: 341 fprintf(stderr, "Attempted to import unsupported modifier 0x%llx\n", 342 (long long)handle->modifier); 343 goto err_out; 344 } 345 346 /* check alignment for the buffer */ 347 if (res->tiled || 348 (pres->bind & (PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL))) { 349 unsigned width, height, stride, size; 350 351 width = align(pres->width0, 16); 352 height = align(pres->height0, 16); 353 stride = util_format_get_stride(pres->format, width); 354 size = util_format_get_2d_size(pres->format, stride, height); 355 356 if (res->tiled && res->levels[0].stride != stride) { 357 fprintf(stderr, "tiled imported buffer has mismatching stride: %d (BO) != %d (expected)", 358 res->levels[0].stride, stride); 359 goto err_out; 360 } 361 362 if (!res->tiled && (res->levels[0].stride % 8)) { 363 fprintf(stderr, "linear imported buffer stride is not aligned to 8 bytes: %d\n", 364 res->levels[0].stride); 365 } 366 367 if (!res->tiled && res->levels[0].stride < stride) { 368 fprintf(stderr, "linear imported buffer stride is smaller than minimal: %d (BO) < %d (min)", 369 res->levels[0].stride, stride); 370 goto err_out; 371 } 372 373 if ((res->bo->size - res->levels[0].offset) < size) { 374 fprintf(stderr, "imported bo size is smaller than expected: %d (BO) < %d (expected)\n", 375 (res->bo->size - res->levels[0].offset), size); 376 goto err_out; 377 } 378 379 res->levels[0].width = width; 380 } 381 else 382 res->levels[0].width = pres->width0; 383 384 if (screen->ro) { 385 /* Make sure that renderonly has a handle to our buffer in the 386 * display's fd, so that a later renderonly_get_handle() 387 * returns correct handles or GEM names. 388 */ 389 res->scanout = 390 renderonly_create_gpu_import_for_resource(pres, 391 screen->ro, 392 NULL); 393 /* ignore failiure to allow importing non-displayable buffer */ 394 } 395 396 return pres; 397 398err_out: 399 lima_resource_destroy(pscreen, pres); 400 return NULL; 401} 402 403static bool 404lima_resource_get_handle(struct pipe_screen *pscreen, 405 struct pipe_context *pctx, 406 struct pipe_resource *pres, 407 struct winsys_handle *handle, unsigned usage) 408{ 409 struct lima_screen *screen = lima_screen(pscreen); 410 struct lima_resource *res = lima_resource(pres); 411 412 if (res->tiled) 413 handle->modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED; 414 else 415 handle->modifier = DRM_FORMAT_MOD_LINEAR; 416 417 res->modifier_constant = true; 418 419 if (handle->type == WINSYS_HANDLE_TYPE_KMS && screen->ro) 420 return renderonly_get_handle(res->scanout, handle); 421 422 if (!lima_bo_export(res->bo, handle)) 423 return false; 424 425 handle->offset = res->levels[0].offset; 426 handle->stride = res->levels[0].stride; 427 return true; 428} 429 430static bool 431lima_resource_get_param(struct pipe_screen *pscreen, 432 struct pipe_context *pctx, 433 struct pipe_resource *pres, 434 unsigned plane, unsigned layer, unsigned level, 435 enum pipe_resource_param param, 436 unsigned usage, uint64_t *value) 437{ 438 struct lima_resource *res = lima_resource(pres); 439 440 switch (param) { 441 case PIPE_RESOURCE_PARAM_STRIDE: 442 *value = res->levels[level].stride; 443 return true; 444 case PIPE_RESOURCE_PARAM_OFFSET: 445 *value = res->levels[level].offset; 446 return true; 447 case PIPE_RESOURCE_PARAM_MODIFIER: 448 if (res->tiled) 449 *value = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED; 450 else 451 *value = DRM_FORMAT_MOD_LINEAR; 452 453 return true; 454 default: 455 return false; 456 } 457} 458 459static void 460get_scissor_from_box(struct pipe_scissor_state *s, 461 const struct pipe_box *b, int h) 462{ 463 int y = h - (b->y + b->height); 464 /* region in tile unit */ 465 s->minx = b->x >> 4; 466 s->miny = y >> 4; 467 s->maxx = (b->x + b->width + 0xf) >> 4; 468 s->maxy = (y + b->height + 0xf) >> 4; 469} 470 471static void 472get_damage_bound_box(struct pipe_resource *pres, 473 const struct pipe_box *rects, 474 unsigned int nrects, 475 struct pipe_scissor_state *bound) 476{ 477 struct pipe_box b = rects[0]; 478 479 for (int i = 1; i < nrects; i++) 480 u_box_union_2d(&b, &b, rects + i); 481 482 int ret = u_box_clip_2d(&b, &b, pres->width0, pres->height0); 483 if (ret < 0) 484 memset(bound, 0, sizeof(*bound)); 485 else 486 get_scissor_from_box(bound, &b, pres->height0); 487} 488 489static void 490lima_resource_set_damage_region(struct pipe_screen *pscreen, 491 struct pipe_resource *pres, 492 unsigned int nrects, 493 const struct pipe_box *rects) 494{ 495 struct lima_resource *res = lima_resource(pres); 496 struct lima_damage_region *damage = &res->damage; 497 int i; 498 499 if (damage->region) { 500 FREE(damage->region); 501 damage->region = NULL; 502 damage->num_region = 0; 503 } 504 505 if (!nrects) 506 return; 507 508 /* check full damage 509 * 510 * TODO: currently only check if there is any single damage 511 * region that can cover the full render target; there may 512 * be some accurate way, but a single window size damage 513 * region is most of the case from weston 514 */ 515 for (i = 0; i < nrects; i++) { 516 if (rects[i].x <= 0 && rects[i].y <= 0 && 517 rects[i].x + rects[i].width >= pres->width0 && 518 rects[i].y + rects[i].height >= pres->height0) 519 return; 520 } 521 522 struct pipe_scissor_state *bound = &damage->bound; 523 get_damage_bound_box(pres, rects, nrects, bound); 524 525 damage->region = CALLOC(nrects, sizeof(*damage->region)); 526 if (!damage->region) 527 return; 528 529 for (i = 0; i < nrects; i++) 530 get_scissor_from_box(damage->region + i, rects + i, 531 pres->height0); 532 533 /* is region aligned to tiles? */ 534 damage->aligned = true; 535 for (i = 0; i < nrects; i++) { 536 if (rects[i].x & 0xf || rects[i].y & 0xf || 537 rects[i].width & 0xf || rects[i].height & 0xf) { 538 damage->aligned = false; 539 break; 540 } 541 } 542 543 damage->num_region = nrects; 544} 545 546void 547lima_resource_screen_init(struct lima_screen *screen) 548{ 549 screen->base.resource_create = lima_resource_create; 550 screen->base.resource_create_with_modifiers = lima_resource_create_with_modifiers; 551 screen->base.resource_from_handle = lima_resource_from_handle; 552 screen->base.resource_destroy = lima_resource_destroy; 553 screen->base.resource_get_handle = lima_resource_get_handle; 554 screen->base.resource_get_param = lima_resource_get_param; 555 screen->base.set_damage_region = lima_resource_set_damage_region; 556} 557 558static struct pipe_surface * 559lima_surface_create(struct pipe_context *pctx, 560 struct pipe_resource *pres, 561 const struct pipe_surface *surf_tmpl) 562{ 563 struct lima_surface *surf = CALLOC_STRUCT(lima_surface); 564 565 if (!surf) 566 return NULL; 567 568 assert(surf_tmpl->u.tex.first_layer == surf_tmpl->u.tex.last_layer); 569 570 struct pipe_surface *psurf = &surf->base; 571 unsigned level = surf_tmpl->u.tex.level; 572 573 pipe_reference_init(&psurf->reference, 1); 574 pipe_resource_reference(&psurf->texture, pres); 575 576 psurf->context = pctx; 577 psurf->format = surf_tmpl->format; 578 psurf->width = u_minify(pres->width0, level); 579 psurf->height = u_minify(pres->height0, level); 580 psurf->u.tex.level = level; 581 psurf->u.tex.first_layer = surf_tmpl->u.tex.first_layer; 582 psurf->u.tex.last_layer = surf_tmpl->u.tex.last_layer; 583 584 surf->tiled_w = align(psurf->width, 16) >> 4; 585 surf->tiled_h = align(psurf->height, 16) >> 4; 586 587 surf->reload = 0; 588 if (util_format_has_stencil(util_format_description(psurf->format))) 589 surf->reload |= PIPE_CLEAR_STENCIL; 590 if (util_format_has_depth(util_format_description(psurf->format))) 591 surf->reload |= PIPE_CLEAR_DEPTH; 592 if (!util_format_is_depth_or_stencil(psurf->format)) 593 surf->reload |= PIPE_CLEAR_COLOR0; 594 595 return &surf->base; 596} 597 598static void 599lima_surface_destroy(struct pipe_context *pctx, struct pipe_surface *psurf) 600{ 601 struct lima_surface *surf = lima_surface(psurf); 602 603 pipe_resource_reference(&psurf->texture, NULL); 604 FREE(surf); 605} 606 607static void * 608lima_transfer_map(struct pipe_context *pctx, 609 struct pipe_resource *pres, 610 unsigned level, 611 unsigned usage, 612 const struct pipe_box *box, 613 struct pipe_transfer **pptrans) 614{ 615 struct lima_screen *screen = lima_screen(pres->screen); 616 struct lima_context *ctx = lima_context(pctx); 617 struct lima_resource *res = lima_resource(pres); 618 struct lima_bo *bo = res->bo; 619 struct lima_transfer *trans; 620 struct pipe_transfer *ptrans; 621 622 /* No direct mappings of tiled, since we need to manually 623 * tile/untile. 624 */ 625 if (res->tiled && (usage & PIPE_MAP_DIRECTLY)) 626 return NULL; 627 628 /* bo might be in use in a previous stream draw. Allocate a new 629 * one for the resource to avoid overwriting data in use. */ 630 if (usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) { 631 struct lima_bo *new_bo; 632 assert(res->bo && res->bo->size); 633 634 new_bo = lima_bo_create(screen, res->bo->size, res->bo->flags); 635 if (!new_bo) 636 return NULL; 637 638 lima_bo_unreference(res->bo); 639 res->bo = new_bo; 640 641 if (pres->bind & PIPE_BIND_VERTEX_BUFFER) 642 ctx->dirty |= LIMA_CONTEXT_DIRTY_VERTEX_BUFF; 643 644 bo = res->bo; 645 } 646 else if (!(usage & PIPE_MAP_UNSYNCHRONIZED) && 647 (usage & PIPE_MAP_READ_WRITE)) { 648 /* use once buffers are made sure to not read/write overlapped 649 * range, so no need to sync */ 650 lima_flush_job_accessing_bo(ctx, bo, usage & PIPE_MAP_WRITE); 651 652 unsigned op = usage & PIPE_MAP_WRITE ? 653 LIMA_GEM_WAIT_WRITE : LIMA_GEM_WAIT_READ; 654 lima_bo_wait(bo, op, PIPE_TIMEOUT_INFINITE); 655 } 656 657 if (!lima_bo_map(bo)) 658 return NULL; 659 660 trans = slab_alloc(&ctx->transfer_pool); 661 if (!trans) 662 return NULL; 663 664 memset(trans, 0, sizeof(*trans)); 665 ptrans = &trans->base; 666 667 pipe_resource_reference(&ptrans->resource, pres); 668 ptrans->level = level; 669 ptrans->usage = usage; 670 ptrans->box = *box; 671 672 *pptrans = ptrans; 673 674 if (res->tiled) { 675 ptrans->stride = util_format_get_stride(pres->format, ptrans->box.width); 676 ptrans->layer_stride = ptrans->stride * ptrans->box.height; 677 678 trans->staging = malloc(ptrans->stride * ptrans->box.height * ptrans->box.depth); 679 680 if (usage & PIPE_MAP_READ) { 681 unsigned i; 682 for (i = 0; i < ptrans->box.depth; i++) 683 panfrost_load_tiled_image( 684 trans->staging + i * ptrans->stride * ptrans->box.height, 685 bo->map + res->levels[level].offset + (i + box->z) * res->levels[level].layer_stride, 686 ptrans->box.x, ptrans->box.y, 687 ptrans->box.width, ptrans->box.height, 688 ptrans->stride, 689 res->levels[level].stride, 690 pres->format); 691 } 692 693 return trans->staging; 694 } else { 695 unsigned dpw = PIPE_MAP_DIRECTLY | PIPE_MAP_WRITE | 696 PIPE_MAP_PERSISTENT; 697 if ((usage & dpw) == dpw && res->index_cache) 698 return NULL; 699 700 ptrans->stride = res->levels[level].stride; 701 ptrans->layer_stride = res->levels[level].layer_stride; 702 703 if ((usage & PIPE_MAP_WRITE) && (usage & PIPE_MAP_DIRECTLY)) 704 panfrost_minmax_cache_invalidate(res->index_cache, ptrans); 705 706 return bo->map + res->levels[level].offset + 707 box->z * res->levels[level].layer_stride + 708 box->y / util_format_get_blockheight(pres->format) * ptrans->stride + 709 box->x / util_format_get_blockwidth(pres->format) * 710 util_format_get_blocksize(pres->format); 711 } 712} 713 714static void 715lima_transfer_flush_region(struct pipe_context *pctx, 716 struct pipe_transfer *ptrans, 717 const struct pipe_box *box) 718{ 719 720} 721 722static bool 723lima_should_convert_linear(struct lima_resource *res, 724 struct pipe_transfer *ptrans) 725{ 726 if (res->modifier_constant) 727 return false; 728 729 /* Overwriting the entire resource indicates streaming, for which 730 * linear layout is most efficient due to the lack of expensive 731 * conversion. 732 * 733 * For now we just switch to linear after a number of complete 734 * overwrites to keep things simple, but we could do better. 735 */ 736 737 unsigned depth = res->base.target == PIPE_TEXTURE_3D ? 738 res->base.depth0 : res->base.array_size; 739 bool entire_overwrite = 740 res->base.last_level == 0 && 741 ptrans->box.width == res->base.width0 && 742 ptrans->box.height == res->base.height0 && 743 ptrans->box.depth == depth && 744 ptrans->box.x == 0 && 745 ptrans->box.y == 0 && 746 ptrans->box.z == 0; 747 748 if (entire_overwrite) 749 ++res->full_updates; 750 751 return res->full_updates >= LAYOUT_CONVERT_THRESHOLD; 752} 753 754static void 755lima_transfer_unmap_inner(struct lima_context *ctx, 756 struct pipe_transfer *ptrans) 757{ 758 struct lima_resource *res = lima_resource(ptrans->resource); 759 struct lima_transfer *trans = lima_transfer(ptrans); 760 struct lima_bo *bo = res->bo; 761 struct pipe_resource *pres; 762 763 if (trans->staging) { 764 pres = &res->base; 765 if (trans->base.usage & PIPE_MAP_WRITE) { 766 unsigned i; 767 if (lima_should_convert_linear(res, ptrans)) { 768 /* It's safe to re-use the same BO since tiled BO always has 769 * aligned dimensions */ 770 for (i = 0; i < trans->base.box.depth; i++) { 771 util_copy_rect(bo->map + res->levels[0].offset + 772 (i + trans->base.box.z) * res->levels[0].stride, 773 res->base.format, 774 res->levels[0].stride, 775 0, 0, 776 ptrans->box.width, 777 ptrans->box.height, 778 trans->staging + i * ptrans->stride * ptrans->box.height, 779 ptrans->stride, 780 0, 0); 781 } 782 res->tiled = false; 783 res->modifier_constant = true; 784 /* Update texture descriptor */ 785 ctx->dirty |= LIMA_CONTEXT_DIRTY_TEXTURES; 786 } else { 787 for (i = 0; i < trans->base.box.depth; i++) 788 panfrost_store_tiled_image( 789 bo->map + res->levels[trans->base.level].offset + (i + trans->base.box.z) * res->levels[trans->base.level].layer_stride, 790 trans->staging + i * ptrans->stride * ptrans->box.height, 791 ptrans->box.x, ptrans->box.y, 792 ptrans->box.width, ptrans->box.height, 793 res->levels[ptrans->level].stride, 794 ptrans->stride, 795 pres->format); 796 } 797 } 798 } 799} 800 801static void 802lima_transfer_unmap(struct pipe_context *pctx, 803 struct pipe_transfer *ptrans) 804{ 805 struct lima_context *ctx = lima_context(pctx); 806 struct lima_transfer *trans = lima_transfer(ptrans); 807 struct lima_resource *res = lima_resource(ptrans->resource); 808 809 lima_transfer_unmap_inner(ctx, ptrans); 810 if (trans->staging) 811 free(trans->staging); 812 panfrost_minmax_cache_invalidate(res->index_cache, ptrans); 813 814 pipe_resource_reference(&ptrans->resource, NULL); 815 slab_free(&ctx->transfer_pool, trans); 816} 817 818static void 819lima_util_blitter_save_states(struct lima_context *ctx) 820{ 821 util_blitter_save_blend(ctx->blitter, (void *)ctx->blend); 822 util_blitter_save_depth_stencil_alpha(ctx->blitter, (void *)ctx->zsa); 823 util_blitter_save_stencil_ref(ctx->blitter, &ctx->stencil_ref); 824 util_blitter_save_rasterizer(ctx->blitter, (void *)ctx->rasterizer); 825 util_blitter_save_fragment_shader(ctx->blitter, ctx->uncomp_fs); 826 util_blitter_save_vertex_shader(ctx->blitter, ctx->uncomp_vs); 827 util_blitter_save_viewport(ctx->blitter, 828 &ctx->viewport.transform); 829 util_blitter_save_scissor(ctx->blitter, &ctx->scissor); 830 util_blitter_save_vertex_elements(ctx->blitter, 831 ctx->vertex_elements); 832 util_blitter_save_vertex_buffer_slot(ctx->blitter, 833 ctx->vertex_buffers.vb); 834 835 util_blitter_save_framebuffer(ctx->blitter, &ctx->framebuffer.base); 836 837 util_blitter_save_fragment_sampler_states(ctx->blitter, 838 ctx->tex_stateobj.num_samplers, 839 (void**)ctx->tex_stateobj.samplers); 840 util_blitter_save_fragment_sampler_views(ctx->blitter, 841 ctx->tex_stateobj.num_textures, 842 ctx->tex_stateobj.textures); 843} 844 845static void 846lima_blit(struct pipe_context *pctx, const struct pipe_blit_info *blit_info) 847{ 848 struct lima_context *ctx = lima_context(pctx); 849 struct pipe_blit_info info = *blit_info; 850 851 if (util_try_blit_via_copy_region(pctx, &info)) { 852 return; /* done */ 853 } 854 855 if (info.mask & PIPE_MASK_S) { 856 debug_printf("lima: cannot blit stencil, skipping\n"); 857 info.mask &= ~PIPE_MASK_S; 858 } 859 860 if (!util_blitter_is_blit_supported(ctx->blitter, &info)) { 861 debug_printf("lima: blit unsupported %s -> %s\n", 862 util_format_short_name(info.src.resource->format), 863 util_format_short_name(info.dst.resource->format)); 864 return; 865 } 866 867 lima_util_blitter_save_states(ctx); 868 869 util_blitter_blit(ctx->blitter, &info); 870} 871 872static void 873lima_flush_resource(struct pipe_context *pctx, struct pipe_resource *resource) 874{ 875 876} 877 878static void 879lima_texture_subdata(struct pipe_context *pctx, 880 struct pipe_resource *prsc, 881 unsigned level, 882 unsigned usage, 883 const struct pipe_box *box, 884 const void *data, 885 unsigned stride, 886 unsigned layer_stride) 887{ 888 struct lima_context *ctx = lima_context(pctx); 889 struct lima_resource *res = lima_resource(prsc); 890 891 if (!res->tiled) { 892 u_default_texture_subdata(pctx, prsc, level, usage, box, 893 data, stride, layer_stride); 894 return; 895 } 896 897 assert(!(usage & PIPE_MAP_READ)); 898 899 struct lima_transfer t = { 900 .base = { 901 .resource = prsc, 902 .usage = PIPE_MAP_WRITE, 903 .level = level, 904 .box = *box, 905 .stride = stride, 906 .layer_stride = layer_stride, 907 }, 908 .staging = (void *)data, 909 }; 910 911 lima_flush_job_accessing_bo(ctx, res->bo, true); 912 lima_bo_wait(res->bo, LIMA_GEM_WAIT_WRITE, PIPE_TIMEOUT_INFINITE); 913 if (!lima_bo_map(res->bo)) 914 return; 915 916 lima_transfer_unmap_inner(ctx, &t.base); 917} 918 919void 920lima_resource_context_init(struct lima_context *ctx) 921{ 922 ctx->base.create_surface = lima_surface_create; 923 ctx->base.surface_destroy = lima_surface_destroy; 924 925 ctx->base.buffer_subdata = u_default_buffer_subdata; 926 ctx->base.texture_subdata = lima_texture_subdata; 927 /* TODO: optimize resource_copy_region to do copy directly 928 * between 2 tiled or tiled and linear resources instead of 929 * using staging buffer. 930 */ 931 ctx->base.resource_copy_region = util_resource_copy_region; 932 933 ctx->base.blit = lima_blit; 934 935 ctx->base.buffer_map = lima_transfer_map; 936 ctx->base.texture_map = lima_transfer_map; 937 ctx->base.transfer_flush_region = lima_transfer_flush_region; 938 ctx->base.buffer_unmap = lima_transfer_unmap; 939 ctx->base.texture_unmap = lima_transfer_unmap; 940 941 ctx->base.flush_resource = lima_flush_resource; 942} 943