| /* |
| * Copyright (c) 2017-2019 Lima Project |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sub license, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the |
| * next paragraph) shall be included in all copies or substantial portions |
| * of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| */ |
| |
| #include "util/u_memory.h" |
| #include "util/u_blitter.h" |
| #include "util/format/u_format.h" |
| #include "util/u_inlines.h" |
| #include "util/u_math.h" |
| #include "util/u_debug.h" |
| #include "util/u_transfer.h" |
| #include "util/u_surface.h" |
| #include "util/hash_table.h" |
| #include "util/ralloc.h" |
| #include "util/u_drm.h" |
| #include "renderonly/renderonly.h" |
| |
| #include "frontend/drm_driver.h" |
| |
| #include "drm-uapi/drm_fourcc.h" |
| #include "drm-uapi/lima_drm.h" |
| |
| #include "lima_screen.h" |
| #include "lima_context.h" |
| #include "lima_resource.h" |
| #include "lima_bo.h" |
| #include "lima_util.h" |
| |
| #include "pan_minmax_cache.h" |
| #include "pan_tiling.h" |
| |
| static struct pipe_resource * |
| lima_resource_create_scanout(struct pipe_screen *pscreen, |
| const struct pipe_resource *templat, |
| unsigned width, unsigned height) |
| { |
| struct lima_screen *screen = lima_screen(pscreen); |
| struct renderonly_scanout *scanout; |
| struct winsys_handle handle; |
| struct pipe_resource *pres; |
| |
| struct pipe_resource scanout_templat = *templat; |
| scanout_templat.width0 = width; |
| scanout_templat.height0 = height; |
| scanout_templat.screen = pscreen; |
| |
| scanout = renderonly_scanout_for_resource(&scanout_templat, |
| screen->ro, &handle); |
| if (!scanout) |
| return NULL; |
| |
| assert(handle.type == WINSYS_HANDLE_TYPE_FD); |
| pres = pscreen->resource_from_handle(pscreen, templat, &handle, |
| PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE); |
| |
| close(handle.handle); |
| if (!pres) { |
| renderonly_scanout_destroy(scanout, screen->ro); |
| return NULL; |
| } |
| |
| struct lima_resource *res = lima_resource(pres); |
| res->scanout = scanout; |
| |
| return pres; |
| } |
| |
| static uint32_t |
| setup_miptree(struct lima_resource *res, |
| unsigned width0, unsigned height0, |
| bool should_align_dimensions) |
| { |
| struct pipe_resource *pres = &res->base; |
| unsigned level; |
| unsigned width = width0; |
| unsigned height = height0; |
| unsigned depth = pres->depth0; |
| uint32_t size = 0; |
| |
| for (level = 0; level <= pres->last_level; level++) { |
| uint32_t actual_level_size; |
| uint32_t stride; |
| unsigned aligned_width; |
| unsigned aligned_height; |
| |
| if (should_align_dimensions) { |
| aligned_width = align(width, 16); |
| aligned_height = align(height, 16); |
| } else { |
| aligned_width = width; |
| aligned_height = height; |
| } |
| |
| stride = util_format_get_stride(pres->format, aligned_width); |
| actual_level_size = stride * |
| util_format_get_nblocksy(pres->format, aligned_height) * |
| pres->array_size * depth; |
| |
| res->levels[level].width = aligned_width; |
| res->levels[level].stride = stride; |
| res->levels[level].offset = size; |
| res->levels[level].layer_stride = util_format_get_stride(pres->format, align(width, 16)) * align(height, 16); |
| |
| if (util_format_is_compressed(pres->format)) |
| res->levels[level].layer_stride /= 4; |
| |
| /* The start address of each level except the last level |
| * must be 64-aligned in order to be able to pass the |
| * addresses to the hardware. */ |
| if (level != pres->last_level) |
| size += align(actual_level_size, 64); |
| else |
| size += actual_level_size; /* Save some memory */ |
| |
| width = u_minify(width, 1); |
| height = u_minify(height, 1); |
| depth = u_minify(depth, 1); |
| } |
| |
| return size; |
| } |
| |
| static struct pipe_resource * |
| lima_resource_create_bo(struct pipe_screen *pscreen, |
| const struct pipe_resource *templat, |
| unsigned width, unsigned height, |
| bool should_align_dimensions) |
| { |
| struct lima_screen *screen = lima_screen(pscreen); |
| struct lima_resource *res; |
| struct pipe_resource *pres; |
| |
| res = CALLOC_STRUCT(lima_resource); |
| if (!res) |
| return NULL; |
| |
| res->base = *templat; |
| res->base.screen = pscreen; |
| pipe_reference_init(&res->base.reference, 1); |
| |
| pres = &res->base; |
| |
| uint32_t size = setup_miptree(res, width, height, should_align_dimensions); |
| size = align(size, LIMA_PAGE_SIZE); |
| |
| res->bo = lima_bo_create(screen, size, 0); |
| if (!res->bo) { |
| FREE(res); |
| return NULL; |
| } |
| |
| return pres; |
| } |
| |
| static struct pipe_resource * |
| _lima_resource_create_with_modifiers(struct pipe_screen *pscreen, |
| const struct pipe_resource *templat, |
| const uint64_t *modifiers, |
| int count) |
| { |
| struct lima_screen *screen = lima_screen(pscreen); |
| bool should_tile = lima_debug & LIMA_DEBUG_NO_TILING ? false : true; |
| unsigned width, height; |
| bool should_align_dimensions; |
| bool has_user_modifiers = true; |
| |
| if (count == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) |
| has_user_modifiers = false; |
| |
| /* VBOs/PBOs are untiled (and 1 height). */ |
| if (templat->target == PIPE_BUFFER) |
| should_tile = false; |
| |
| if (templat->bind & (PIPE_BIND_LINEAR | PIPE_BIND_SCANOUT)) |
| should_tile = false; |
| |
| /* If there's no user modifiers and buffer is shared we use linear */ |
| if (!has_user_modifiers && (templat->bind & PIPE_BIND_SHARED)) |
| should_tile = false; |
| |
| if (has_user_modifiers && |
| !drm_find_modifier(DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED, |
| modifiers, count)) |
| should_tile = false; |
| |
| if (should_tile || (templat->bind & PIPE_BIND_RENDER_TARGET) || |
| (templat->bind & PIPE_BIND_DEPTH_STENCIL)) { |
| should_align_dimensions = true; |
| width = align(templat->width0, 16); |
| height = align(templat->height0, 16); |
| } |
| else { |
| should_align_dimensions = false; |
| width = templat->width0; |
| height = templat->height0; |
| } |
| |
| struct pipe_resource *pres; |
| if (screen->ro && (templat->bind & PIPE_BIND_SCANOUT)) |
| pres = lima_resource_create_scanout(pscreen, templat, width, height); |
| else |
| pres = lima_resource_create_bo(pscreen, templat, width, height, |
| should_align_dimensions); |
| |
| if (pres) { |
| struct lima_resource *res = lima_resource(pres); |
| res->tiled = should_tile; |
| |
| if (templat->bind & PIPE_BIND_INDEX_BUFFER) |
| res->index_cache = CALLOC_STRUCT(panfrost_minmax_cache); |
| |
| debug_printf("%s: pres=%p width=%u height=%u depth=%u target=%d " |
| "bind=%x usage=%d tile=%d last_level=%d\n", __func__, |
| pres, pres->width0, pres->height0, pres->depth0, |
| pres->target, pres->bind, pres->usage, should_tile, templat->last_level); |
| } |
| return pres; |
| } |
| |
| static struct pipe_resource * |
| lima_resource_create(struct pipe_screen *pscreen, |
| const struct pipe_resource *templat) |
| { |
| const uint64_t mod = DRM_FORMAT_MOD_INVALID; |
| |
| return _lima_resource_create_with_modifiers(pscreen, templat, &mod, 1); |
| } |
| |
| static struct pipe_resource * |
| lima_resource_create_with_modifiers(struct pipe_screen *pscreen, |
| const struct pipe_resource *templat, |
| const uint64_t *modifiers, |
| int count) |
| { |
| struct pipe_resource tmpl = *templat; |
| |
| /* gbm_bo_create_with_modifiers & gbm_surface_create_with_modifiers |
| * don't have usage parameter, but buffer created by these functions |
| * may be used for scanout. So we assume buffer created by this |
| * function always enable scanout if linear modifier is permitted. |
| */ |
| if (drm_find_modifier(DRM_FORMAT_MOD_LINEAR, modifiers, count)) |
| tmpl.bind |= PIPE_BIND_SCANOUT; |
| |
| return _lima_resource_create_with_modifiers(pscreen, &tmpl, modifiers, count); |
| } |
| |
| static void |
| lima_resource_destroy(struct pipe_screen *pscreen, struct pipe_resource *pres) |
| { |
| struct lima_screen *screen = lima_screen(pscreen); |
| struct lima_resource *res = lima_resource(pres); |
| |
| if (res->bo) |
| lima_bo_unreference(res->bo); |
| |
| if (res->scanout) |
| renderonly_scanout_destroy(res->scanout, screen->ro); |
| |
| if (res->damage.region) |
| FREE(res->damage.region); |
| |
| if (res->index_cache) |
| FREE(res->index_cache); |
| |
| FREE(res); |
| } |
| |
| static struct pipe_resource * |
| lima_resource_from_handle(struct pipe_screen *pscreen, |
| const struct pipe_resource *templat, |
| struct winsys_handle *handle, unsigned usage) |
| { |
| if (templat->bind & (PIPE_BIND_SAMPLER_VIEW | |
| PIPE_BIND_RENDER_TARGET | |
| PIPE_BIND_DEPTH_STENCIL)) { |
| /* sampler hardware need offset alignment 64, while render hardware |
| * need offset alignment 8, but due to render target may be reloaded |
| * which uses the sampler, set alignment requrement to 64 for all |
| */ |
| if (handle->offset & 0x3f) { |
| debug_error("import buffer offset not properly aligned\n"); |
| return NULL; |
| } |
| } |
| |
| struct lima_resource *res = CALLOC_STRUCT(lima_resource); |
| if (!res) |
| return NULL; |
| |
| struct pipe_resource *pres = &res->base; |
| *pres = *templat; |
| pres->screen = pscreen; |
| pipe_reference_init(&pres->reference, 1); |
| res->levels[0].offset = handle->offset; |
| res->levels[0].stride = handle->stride; |
| |
| struct lima_screen *screen = lima_screen(pscreen); |
| res->bo = lima_bo_import(screen, handle); |
| if (!res->bo) { |
| FREE(res); |
| return NULL; |
| } |
| |
| switch (handle->modifier) { |
| case DRM_FORMAT_MOD_LINEAR: |
| res->tiled = false; |
| break; |
| case DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED: |
| res->tiled = true; |
| break; |
| case DRM_FORMAT_MOD_INVALID: |
| /* Modifier wasn't specified and it's shared buffer. We create these |
| * as linear, so disable tiling. |
| */ |
| res->tiled = false; |
| break; |
| default: |
| fprintf(stderr, "Attempted to import unsupported modifier 0x%llx\n", |
| (long long)handle->modifier); |
| goto err_out; |
| } |
| |
| /* check alignment for the buffer */ |
| if (res->tiled || |
| (pres->bind & (PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL))) { |
| unsigned width, height, stride, size; |
| |
| width = align(pres->width0, 16); |
| height = align(pres->height0, 16); |
| stride = util_format_get_stride(pres->format, width); |
| size = util_format_get_2d_size(pres->format, stride, height); |
| |
| if (res->levels[0].stride != stride || res->bo->size < size) { |
| debug_error("import buffer not properly aligned\n"); |
| goto err_out; |
| } |
| |
| res->levels[0].width = width; |
| } |
| else |
| res->levels[0].width = pres->width0; |
| |
| return pres; |
| |
| err_out: |
| lima_resource_destroy(pscreen, pres); |
| return NULL; |
| } |
| |
| static bool |
| lima_resource_get_handle(struct pipe_screen *pscreen, |
| struct pipe_context *pctx, |
| struct pipe_resource *pres, |
| struct winsys_handle *handle, unsigned usage) |
| { |
| struct lima_screen *screen = lima_screen(pscreen); |
| struct lima_resource *res = lima_resource(pres); |
| |
| if (res->tiled) |
| handle->modifier = DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED; |
| else |
| handle->modifier = DRM_FORMAT_MOD_LINEAR; |
| |
| if (handle->type == WINSYS_HANDLE_TYPE_KMS && screen->ro && |
| renderonly_get_handle(res->scanout, handle)) |
| return true; |
| |
| if (!lima_bo_export(res->bo, handle)) |
| return false; |
| |
| handle->offset = res->levels[0].offset; |
| handle->stride = res->levels[0].stride; |
| return true; |
| } |
| |
| static void |
| get_scissor_from_box(struct pipe_scissor_state *s, |
| const struct pipe_box *b, int h) |
| { |
| int y = h - (b->y + b->height); |
| /* region in tile unit */ |
| s->minx = b->x >> 4; |
| s->miny = y >> 4; |
| s->maxx = (b->x + b->width + 0xf) >> 4; |
| s->maxy = (y + b->height + 0xf) >> 4; |
| } |
| |
| static void |
| get_damage_bound_box(struct pipe_resource *pres, |
| const struct pipe_box *rects, |
| unsigned int nrects, |
| struct pipe_scissor_state *bound) |
| { |
| struct pipe_box b = rects[0]; |
| |
| for (int i = 1; i < nrects; i++) |
| u_box_union_2d(&b, &b, rects + i); |
| |
| int ret = u_box_clip_2d(&b, &b, pres->width0, pres->height0); |
| if (ret < 0) |
| memset(bound, 0, sizeof(*bound)); |
| else |
| get_scissor_from_box(bound, &b, pres->height0); |
| } |
| |
| static void |
| lima_resource_set_damage_region(struct pipe_screen *pscreen, |
| struct pipe_resource *pres, |
| unsigned int nrects, |
| const struct pipe_box *rects) |
| { |
| struct lima_resource *res = lima_resource(pres); |
| struct lima_damage_region *damage = &res->damage; |
| int i; |
| |
| if (damage->region) { |
| FREE(damage->region); |
| damage->region = NULL; |
| damage->num_region = 0; |
| } |
| |
| if (!nrects) |
| return; |
| |
| /* check full damage |
| * |
| * TODO: currently only check if there is any single damage |
| * region that can cover the full render target; there may |
| * be some accurate way, but a single window size damage |
| * region is most of the case from weston |
| */ |
| for (i = 0; i < nrects; i++) { |
| if (rects[i].x <= 0 && rects[i].y <= 0 && |
| rects[i].x + rects[i].width >= pres->width0 && |
| rects[i].y + rects[i].height >= pres->height0) |
| return; |
| } |
| |
| struct pipe_scissor_state *bound = &damage->bound; |
| get_damage_bound_box(pres, rects, nrects, bound); |
| |
| damage->region = CALLOC(nrects, sizeof(*damage->region)); |
| if (!damage->region) |
| return; |
| |
| for (i = 0; i < nrects; i++) |
| get_scissor_from_box(damage->region + i, rects + i, |
| pres->height0); |
| |
| /* is region aligned to tiles? */ |
| damage->aligned = true; |
| for (i = 0; i < nrects; i++) { |
| if (rects[i].x & 0xf || rects[i].y & 0xf || |
| rects[i].width & 0xf || rects[i].height & 0xf) { |
| damage->aligned = false; |
| break; |
| } |
| } |
| |
| damage->num_region = nrects; |
| } |
| |
| void |
| lima_resource_screen_init(struct lima_screen *screen) |
| { |
| screen->base.resource_create = lima_resource_create; |
| screen->base.resource_create_with_modifiers = lima_resource_create_with_modifiers; |
| screen->base.resource_from_handle = lima_resource_from_handle; |
| screen->base.resource_destroy = lima_resource_destroy; |
| screen->base.resource_get_handle = lima_resource_get_handle; |
| screen->base.set_damage_region = lima_resource_set_damage_region; |
| } |
| |
| static struct pipe_surface * |
| lima_surface_create(struct pipe_context *pctx, |
| struct pipe_resource *pres, |
| const struct pipe_surface *surf_tmpl) |
| { |
| struct lima_surface *surf = CALLOC_STRUCT(lima_surface); |
| |
| if (!surf) |
| return NULL; |
| |
| assert(surf_tmpl->u.tex.first_layer == surf_tmpl->u.tex.last_layer); |
| |
| struct pipe_surface *psurf = &surf->base; |
| unsigned level = surf_tmpl->u.tex.level; |
| |
| pipe_reference_init(&psurf->reference, 1); |
| pipe_resource_reference(&psurf->texture, pres); |
| |
| psurf->context = pctx; |
| psurf->format = surf_tmpl->format; |
| psurf->width = u_minify(pres->width0, level); |
| psurf->height = u_minify(pres->height0, level); |
| psurf->u.tex.level = level; |
| psurf->u.tex.first_layer = surf_tmpl->u.tex.first_layer; |
| psurf->u.tex.last_layer = surf_tmpl->u.tex.last_layer; |
| |
| surf->tiled_w = align(psurf->width, 16) >> 4; |
| surf->tiled_h = align(psurf->height, 16) >> 4; |
| |
| surf->reload = 0; |
| if (util_format_has_stencil(util_format_description(psurf->format))) |
| surf->reload |= PIPE_CLEAR_STENCIL; |
| if (util_format_has_depth(util_format_description(psurf->format))) |
| surf->reload |= PIPE_CLEAR_DEPTH; |
| if (!util_format_is_depth_or_stencil(psurf->format)) |
| surf->reload |= PIPE_CLEAR_COLOR0; |
| |
| return &surf->base; |
| } |
| |
| static void |
| lima_surface_destroy(struct pipe_context *pctx, struct pipe_surface *psurf) |
| { |
| struct lima_surface *surf = lima_surface(psurf); |
| |
| pipe_resource_reference(&psurf->texture, NULL); |
| FREE(surf); |
| } |
| |
| static void * |
| lima_transfer_map(struct pipe_context *pctx, |
| struct pipe_resource *pres, |
| unsigned level, |
| unsigned usage, |
| const struct pipe_box *box, |
| struct pipe_transfer **pptrans) |
| { |
| struct lima_screen *screen = lima_screen(pres->screen); |
| struct lima_context *ctx = lima_context(pctx); |
| struct lima_resource *res = lima_resource(pres); |
| struct lima_bo *bo = res->bo; |
| struct lima_transfer *trans; |
| struct pipe_transfer *ptrans; |
| |
| /* No direct mappings of tiled, since we need to manually |
| * tile/untile. |
| */ |
| if (res->tiled && (usage & PIPE_MAP_DIRECTLY)) |
| return NULL; |
| |
| /* bo might be in use in a previous stream draw. Allocate a new |
| * one for the resource to avoid overwriting data in use. */ |
| if (usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) { |
| struct lima_bo *new_bo; |
| assert(res->bo && res->bo->size); |
| |
| new_bo = lima_bo_create(screen, res->bo->size, res->bo->flags); |
| if (!new_bo) |
| return NULL; |
| |
| lima_bo_unreference(res->bo); |
| res->bo = new_bo; |
| |
| if (pres->bind & PIPE_BIND_VERTEX_BUFFER) |
| ctx->dirty |= LIMA_CONTEXT_DIRTY_VERTEX_BUFF; |
| |
| bo = res->bo; |
| } |
| else if (!(usage & PIPE_MAP_UNSYNCHRONIZED) && |
| (usage & PIPE_MAP_READ_WRITE)) { |
| /* use once buffers are made sure to not read/write overlapped |
| * range, so no need to sync */ |
| lima_flush_job_accessing_bo(ctx, bo, usage & PIPE_MAP_WRITE); |
| |
| unsigned op = usage & PIPE_MAP_WRITE ? |
| LIMA_GEM_WAIT_WRITE : LIMA_GEM_WAIT_READ; |
| lima_bo_wait(bo, op, PIPE_TIMEOUT_INFINITE); |
| } |
| |
| if (!lima_bo_map(bo)) |
| return NULL; |
| |
| trans = slab_alloc(&ctx->transfer_pool); |
| if (!trans) |
| return NULL; |
| |
| memset(trans, 0, sizeof(*trans)); |
| ptrans = &trans->base; |
| |
| pipe_resource_reference(&ptrans->resource, pres); |
| ptrans->level = level; |
| ptrans->usage = usage; |
| ptrans->box = *box; |
| |
| *pptrans = ptrans; |
| |
| if (res->tiled) { |
| ptrans->stride = util_format_get_stride(pres->format, ptrans->box.width); |
| ptrans->layer_stride = ptrans->stride * ptrans->box.height; |
| |
| trans->staging = malloc(ptrans->stride * ptrans->box.height * ptrans->box.depth); |
| |
| if (usage & PIPE_MAP_READ) { |
| unsigned i; |
| for (i = 0; i < ptrans->box.depth; i++) |
| panfrost_load_tiled_image( |
| trans->staging + i * ptrans->stride * ptrans->box.height, |
| bo->map + res->levels[level].offset + (i + box->z) * res->levels[level].layer_stride, |
| ptrans->box.x, ptrans->box.y, |
| ptrans->box.width, ptrans->box.height, |
| ptrans->stride, |
| res->levels[level].stride, |
| pres->format); |
| } |
| |
| return trans->staging; |
| } else { |
| unsigned dpw = PIPE_MAP_DIRECTLY | PIPE_MAP_WRITE | |
| PIPE_MAP_PERSISTENT; |
| if ((usage & dpw) == dpw && res->index_cache) |
| return NULL; |
| |
| ptrans->stride = res->levels[level].stride; |
| ptrans->layer_stride = res->levels[level].layer_stride; |
| |
| if ((usage & PIPE_MAP_WRITE) && (usage & PIPE_MAP_DIRECTLY)) |
| panfrost_minmax_cache_invalidate(res->index_cache, ptrans); |
| |
| return bo->map + res->levels[level].offset + |
| box->z * res->levels[level].layer_stride + |
| box->y / util_format_get_blockheight(pres->format) * ptrans->stride + |
| box->x / util_format_get_blockwidth(pres->format) * |
| util_format_get_blocksize(pres->format); |
| } |
| } |
| |
| static void |
| lima_transfer_flush_region(struct pipe_context *pctx, |
| struct pipe_transfer *ptrans, |
| const struct pipe_box *box) |
| { |
| |
| } |
| |
| static void |
| lima_transfer_unmap_inner(struct lima_context *ctx, |
| struct pipe_transfer *ptrans) |
| { |
| struct lima_resource *res = lima_resource(ptrans->resource); |
| struct lima_transfer *trans = lima_transfer(ptrans); |
| struct lima_bo *bo = res->bo; |
| struct pipe_resource *pres; |
| |
| if (trans->staging) { |
| pres = &res->base; |
| if (trans->base.usage & PIPE_MAP_WRITE) { |
| unsigned i; |
| for (i = 0; i < trans->base.box.depth; i++) |
| panfrost_store_tiled_image( |
| bo->map + res->levels[trans->base.level].offset + (i + trans->base.box.z) * res->levels[trans->base.level].layer_stride, |
| trans->staging + i * ptrans->stride * ptrans->box.height, |
| ptrans->box.x, ptrans->box.y, |
| ptrans->box.width, ptrans->box.height, |
| res->levels[ptrans->level].stride, |
| ptrans->stride, |
| pres->format); |
| } |
| } |
| } |
| |
| static void |
| lima_transfer_unmap(struct pipe_context *pctx, |
| struct pipe_transfer *ptrans) |
| { |
| struct lima_context *ctx = lima_context(pctx); |
| struct lima_transfer *trans = lima_transfer(ptrans); |
| struct lima_resource *res = lima_resource(ptrans->resource); |
| |
| lima_transfer_unmap_inner(ctx, ptrans); |
| if (trans->staging) |
| free(trans->staging); |
| panfrost_minmax_cache_invalidate(res->index_cache, ptrans); |
| |
| pipe_resource_reference(&ptrans->resource, NULL); |
| slab_free(&ctx->transfer_pool, trans); |
| } |
| |
| static void |
| lima_util_blitter_save_states(struct lima_context *ctx) |
| { |
| util_blitter_save_blend(ctx->blitter, (void *)ctx->blend); |
| util_blitter_save_depth_stencil_alpha(ctx->blitter, (void *)ctx->zsa); |
| util_blitter_save_stencil_ref(ctx->blitter, &ctx->stencil_ref); |
| util_blitter_save_rasterizer(ctx->blitter, (void *)ctx->rasterizer); |
| util_blitter_save_fragment_shader(ctx->blitter, ctx->fs); |
| util_blitter_save_vertex_shader(ctx->blitter, ctx->vs); |
| util_blitter_save_viewport(ctx->blitter, |
| &ctx->viewport.transform); |
| util_blitter_save_scissor(ctx->blitter, &ctx->scissor); |
| util_blitter_save_vertex_elements(ctx->blitter, |
| ctx->vertex_elements); |
| util_blitter_save_vertex_buffer_slot(ctx->blitter, |
| ctx->vertex_buffers.vb); |
| |
| util_blitter_save_framebuffer(ctx->blitter, &ctx->framebuffer.base); |
| |
| util_blitter_save_fragment_sampler_states(ctx->blitter, |
| ctx->tex_stateobj.num_samplers, |
| (void**)ctx->tex_stateobj.samplers); |
| util_blitter_save_fragment_sampler_views(ctx->blitter, |
| ctx->tex_stateobj.num_textures, |
| ctx->tex_stateobj.textures); |
| } |
| |
| static void |
| lima_blit(struct pipe_context *pctx, const struct pipe_blit_info *blit_info) |
| { |
| struct lima_context *ctx = lima_context(pctx); |
| struct pipe_blit_info info = *blit_info; |
| |
| if (util_try_blit_via_copy_region(pctx, &info)) { |
| return; /* done */ |
| } |
| |
| if (info.mask & PIPE_MASK_S) { |
| debug_printf("lima: cannot blit stencil, skipping\n"); |
| info.mask &= ~PIPE_MASK_S; |
| } |
| |
| if (!util_blitter_is_blit_supported(ctx->blitter, &info)) { |
| debug_printf("lima: blit unsupported %s -> %s\n", |
| util_format_short_name(info.src.resource->format), |
| util_format_short_name(info.dst.resource->format)); |
| return; |
| } |
| |
| lima_util_blitter_save_states(ctx); |
| |
| util_blitter_blit(ctx->blitter, &info); |
| } |
| |
| static void |
| lima_flush_resource(struct pipe_context *pctx, struct pipe_resource *resource) |
| { |
| |
| } |
| |
| static void |
| lima_texture_subdata(struct pipe_context *pctx, |
| struct pipe_resource *prsc, |
| unsigned level, |
| unsigned usage, |
| const struct pipe_box *box, |
| const void *data, |
| unsigned stride, |
| unsigned layer_stride) |
| { |
| struct lima_context *ctx = lima_context(pctx); |
| struct lima_resource *res = lima_resource(prsc); |
| |
| if (!res->tiled) { |
| u_default_texture_subdata(pctx, prsc, level, usage, box, |
| data, stride, layer_stride); |
| return; |
| } |
| |
| assert(!(usage & PIPE_MAP_READ)); |
| |
| struct lima_transfer t = { |
| .base = { |
| .resource = prsc, |
| .usage = PIPE_MAP_WRITE, |
| .level = level, |
| .box = *box, |
| .stride = stride, |
| .layer_stride = layer_stride, |
| }, |
| .staging = (void *)data, |
| }; |
| |
| lima_flush_job_accessing_bo(ctx, res->bo, true); |
| lima_bo_wait(res->bo, LIMA_GEM_WAIT_WRITE, PIPE_TIMEOUT_INFINITE); |
| if (!lima_bo_map(res->bo)) |
| return; |
| |
| lima_transfer_unmap_inner(ctx, &t.base); |
| } |
| |
| void |
| lima_resource_context_init(struct lima_context *ctx) |
| { |
| ctx->base.create_surface = lima_surface_create; |
| ctx->base.surface_destroy = lima_surface_destroy; |
| |
| ctx->base.buffer_subdata = u_default_buffer_subdata; |
| ctx->base.texture_subdata = lima_texture_subdata; |
| /* TODO: optimize resource_copy_region to do copy directly |
| * between 2 tiled or tiled and linear resources instead of |
| * using staging buffer. |
| */ |
| ctx->base.resource_copy_region = util_resource_copy_region; |
| |
| ctx->base.blit = lima_blit; |
| |
| ctx->base.transfer_map = lima_transfer_map; |
| ctx->base.transfer_flush_region = lima_transfer_flush_region; |
| ctx->base.transfer_unmap = lima_transfer_unmap; |
| |
| ctx->base.flush_resource = lima_flush_resource; |
| } |