| /* |
| * Copyright (C) 2008 VMware, Inc. |
| * Copyright (C) 2014 Broadcom |
| * Copyright (C) 2018-2019 Alyssa Rosenzweig |
| * Copyright (C) 2019 Collabora, Ltd. |
| * |
| * 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, sublicense, |
| * 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 NONINFRINGEMENT. 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. |
| * |
| * Authors (Collabora): |
| * Tomeu Vizoso <tomeu.vizoso@collabora.com> |
| * Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com> |
| * |
| */ |
| |
| #include <xf86drm.h> |
| #include <fcntl.h> |
| #include "drm-uapi/drm_fourcc.h" |
| |
| #include "frontend/winsys_handle.h" |
| #include "util/format/u_format.h" |
| #include "util/u_memory.h" |
| #include "util/u_surface.h" |
| #include "util/u_transfer.h" |
| #include "util/u_transfer_helper.h" |
| #include "util/u_gen_mipmap.h" |
| |
| #include "pan_bo.h" |
| #include "pan_context.h" |
| #include "pan_screen.h" |
| #include "pan_resource.h" |
| #include "pan_util.h" |
| #include "pan_tiling.h" |
| #include "decode.h" |
| #include "panfrost-quirks.h" |
| |
| static struct pipe_resource * |
| panfrost_resource_from_handle(struct pipe_screen *pscreen, |
| const struct pipe_resource *templat, |
| struct winsys_handle *whandle, |
| unsigned usage) |
| { |
| struct panfrost_device *dev = pan_device(pscreen); |
| struct panfrost_resource *rsc; |
| struct pipe_resource *prsc; |
| |
| assert(whandle->type == WINSYS_HANDLE_TYPE_FD); |
| |
| rsc = rzalloc(pscreen, struct panfrost_resource); |
| if (!rsc) |
| return NULL; |
| |
| prsc = &rsc->base; |
| |
| *prsc = *templat; |
| |
| pipe_reference_init(&prsc->reference, 1); |
| prsc->screen = pscreen; |
| |
| rsc->bo = panfrost_bo_import(dev, whandle->handle); |
| rsc->internal_format = templat->format; |
| rsc->modifier = DRM_FORMAT_MOD_LINEAR; |
| rsc->slices[0].stride = whandle->stride; |
| rsc->slices[0].offset = whandle->offset; |
| rsc->slices[0].initialized = true; |
| panfrost_resource_set_damage_region(NULL, &rsc->base, 0, NULL); |
| |
| if (dev->quirks & IS_BIFROST && |
| templat->bind & PIPE_BIND_RENDER_TARGET) { |
| unsigned size = panfrost_compute_checksum_size( |
| &rsc->slices[0], templat->width0, templat->height0); |
| rsc->slices[0].checksum_bo = panfrost_bo_create(dev, size, 0); |
| rsc->checksummed = true; |
| } |
| |
| if (dev->ro) { |
| rsc->scanout = |
| renderonly_create_gpu_import_for_resource(prsc, dev->ro, NULL); |
| /* failure is expected in some cases.. */ |
| } |
| |
| return prsc; |
| } |
| |
| static bool |
| panfrost_resource_get_handle(struct pipe_screen *pscreen, |
| struct pipe_context *ctx, |
| struct pipe_resource *pt, |
| struct winsys_handle *handle, |
| unsigned usage) |
| { |
| struct panfrost_device *dev = pan_device(pscreen); |
| struct panfrost_resource *rsrc = (struct panfrost_resource *) pt; |
| struct renderonly_scanout *scanout = rsrc->scanout; |
| |
| handle->modifier = DRM_FORMAT_MOD_INVALID; |
| |
| if (handle->type == WINSYS_HANDLE_TYPE_SHARED) { |
| return false; |
| } else if (handle->type == WINSYS_HANDLE_TYPE_KMS) { |
| if (renderonly_get_handle(scanout, handle)) |
| return true; |
| |
| handle->handle = rsrc->bo->gem_handle; |
| handle->stride = rsrc->slices[0].stride; |
| handle->offset = rsrc->slices[0].offset; |
| return TRUE; |
| } else if (handle->type == WINSYS_HANDLE_TYPE_FD) { |
| if (scanout) { |
| struct drm_prime_handle args = { |
| .handle = scanout->handle, |
| .flags = DRM_CLOEXEC, |
| }; |
| |
| int ret = drmIoctl(dev->ro->kms_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args); |
| if (ret == -1) |
| return false; |
| |
| handle->stride = scanout->stride; |
| handle->handle = args.fd; |
| |
| return true; |
| } else { |
| int fd = panfrost_bo_export(rsrc->bo); |
| |
| if (fd < 0) |
| return false; |
| |
| handle->handle = fd; |
| handle->stride = rsrc->slices[0].stride; |
| handle->offset = rsrc->slices[0].offset; |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static void |
| panfrost_flush_resource(struct pipe_context *pctx, struct pipe_resource *prsc) |
| { |
| /* TODO */ |
| } |
| |
| static struct pipe_surface * |
| panfrost_create_surface(struct pipe_context *pipe, |
| struct pipe_resource *pt, |
| const struct pipe_surface *surf_tmpl) |
| { |
| struct pipe_surface *ps = NULL; |
| |
| ps = rzalloc(pipe, struct pipe_surface); |
| |
| if (ps) { |
| pipe_reference_init(&ps->reference, 1); |
| pipe_resource_reference(&ps->texture, pt); |
| ps->context = pipe; |
| ps->format = surf_tmpl->format; |
| |
| if (pt->target != PIPE_BUFFER) { |
| assert(surf_tmpl->u.tex.level <= pt->last_level); |
| ps->width = u_minify(pt->width0, surf_tmpl->u.tex.level); |
| ps->height = u_minify(pt->height0, surf_tmpl->u.tex.level); |
| ps->nr_samples = surf_tmpl->nr_samples; |
| ps->u.tex.level = surf_tmpl->u.tex.level; |
| ps->u.tex.first_layer = surf_tmpl->u.tex.first_layer; |
| ps->u.tex.last_layer = surf_tmpl->u.tex.last_layer; |
| } else { |
| /* setting width as number of elements should get us correct renderbuffer width */ |
| ps->width = surf_tmpl->u.buf.last_element - surf_tmpl->u.buf.first_element + 1; |
| ps->height = pt->height0; |
| ps->u.buf.first_element = surf_tmpl->u.buf.first_element; |
| ps->u.buf.last_element = surf_tmpl->u.buf.last_element; |
| assert(ps->u.buf.first_element <= ps->u.buf.last_element); |
| assert(ps->u.buf.last_element < ps->width); |
| } |
| } |
| |
| return ps; |
| } |
| |
| static void |
| panfrost_surface_destroy(struct pipe_context *pipe, |
| struct pipe_surface *surf) |
| { |
| assert(surf->texture); |
| pipe_resource_reference(&surf->texture, NULL); |
| ralloc_free(surf); |
| } |
| |
| static struct pipe_resource * |
| panfrost_create_scanout_res(struct pipe_screen *screen, |
| const struct pipe_resource *template) |
| { |
| struct panfrost_device *dev = pan_device(screen); |
| struct pipe_resource scanout_templat = *template; |
| struct renderonly_scanout *scanout; |
| struct winsys_handle handle; |
| struct pipe_resource *res; |
| |
| scanout = renderonly_scanout_for_resource(&scanout_templat, |
| dev->ro, &handle); |
| if (!scanout) |
| return NULL; |
| |
| assert(handle.type == WINSYS_HANDLE_TYPE_FD); |
| /* TODO: handle modifiers? */ |
| res = screen->resource_from_handle(screen, template, &handle, |
| PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE); |
| close(handle.handle); |
| if (!res) |
| return NULL; |
| |
| struct panfrost_resource *pres = pan_resource(res); |
| |
| pres->scanout = scanout; |
| |
| return res; |
| } |
| |
| /* Setup the mip tree given a particular modifier, possibly with checksumming */ |
| |
| static void |
| panfrost_setup_slices(struct panfrost_resource *pres, size_t *bo_size) |
| { |
| struct pipe_resource *res = &pres->base; |
| unsigned width = res->width0; |
| unsigned height = res->height0; |
| unsigned depth = res->depth0; |
| unsigned bytes_per_pixel = util_format_get_blocksize(pres->internal_format); |
| |
| /* MSAA is implemented as a 3D texture with z corresponding to the |
| * sample #, horrifyingly enough */ |
| |
| bool msaa = res->nr_samples > 1; |
| |
| if (msaa) { |
| assert(depth == 1); |
| depth = res->nr_samples; |
| } |
| |
| assert(depth > 0); |
| |
| /* Tiled operates blockwise; linear is packed. Also, anything |
| * we render to has to be tile-aligned. Maybe not strictly |
| * necessary, but we're not *that* pressed for memory and it |
| * makes code a lot simpler */ |
| |
| bool renderable = res->bind & |
| (PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL) && |
| res->target != PIPE_BUFFER; |
| bool afbc = drm_is_afbc(pres->modifier); |
| bool tiled = pres->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED; |
| bool linear = pres->modifier == DRM_FORMAT_MOD_LINEAR; |
| bool should_align = renderable || tiled; |
| |
| /* We don't know how to specify a 2D stride for 3D textures */ |
| |
| bool can_align_stride = |
| res->target != PIPE_TEXTURE_3D; |
| |
| should_align &= can_align_stride; |
| |
| unsigned offset = 0; |
| unsigned size_2d = 0; |
| |
| for (unsigned l = 0; l <= res->last_level; ++l) { |
| struct panfrost_slice *slice = &pres->slices[l]; |
| |
| unsigned effective_width = width; |
| unsigned effective_height = height; |
| unsigned effective_depth = depth; |
| |
| if (should_align) { |
| effective_width = ALIGN_POT(effective_width, 16); |
| effective_height = ALIGN_POT(effective_height, 16); |
| |
| /* We don't need to align depth */ |
| } |
| |
| /* Align levels to cache-line as a performance improvement for |
| * linear/tiled and as a requirement for AFBC */ |
| |
| offset = ALIGN_POT(offset, 64); |
| |
| slice->offset = offset; |
| |
| /* Compute the would-be stride */ |
| unsigned stride = bytes_per_pixel * effective_width; |
| |
| if (util_format_is_compressed(pres->internal_format)) |
| stride /= 4; |
| |
| /* ..but cache-line align it for performance */ |
| if (can_align_stride && linear) |
| stride = ALIGN_POT(stride, 64); |
| |
| slice->stride = stride; |
| |
| unsigned slice_one_size = slice->stride * effective_height; |
| unsigned slice_full_size = slice_one_size * effective_depth; |
| |
| slice->size0 = slice_one_size; |
| |
| /* Report 2D size for 3D texturing */ |
| |
| if (l == 0) |
| size_2d = slice_one_size; |
| |
| /* Compute AFBC sizes if necessary */ |
| if (afbc) { |
| slice->header_size = |
| panfrost_afbc_header_size(width, height); |
| |
| offset += slice->header_size; |
| } |
| |
| offset += slice_full_size; |
| |
| /* Add a checksum region if necessary */ |
| if (pres->checksummed) { |
| slice->checksum_offset = offset; |
| |
| unsigned size = panfrost_compute_checksum_size( |
| slice, width, height); |
| |
| offset += size; |
| } |
| |
| width = u_minify(width, 1); |
| height = u_minify(height, 1); |
| |
| /* Don't mipmap the sample count */ |
| if (!msaa) |
| depth = u_minify(depth, 1); |
| } |
| |
| assert(res->array_size); |
| |
| if (res->target != PIPE_TEXTURE_3D) { |
| /* Arrays and cubemaps have the entire miptree duplicated */ |
| |
| pres->cubemap_stride = ALIGN_POT(offset, 64); |
| *bo_size = ALIGN_POT(pres->cubemap_stride * res->array_size, 4096); |
| } else { |
| /* 3D strides across the 2D layers */ |
| assert(res->array_size == 1); |
| |
| pres->cubemap_stride = size_2d; |
| *bo_size = ALIGN_POT(offset, 4096); |
| } |
| } |
| |
| static void |
| panfrost_resource_create_bo(struct panfrost_device *dev, struct panfrost_resource *pres) |
| { |
| struct pipe_resource *res = &pres->base; |
| |
| /* Based on the usage, figure out what storing will be used. There are |
| * various tradeoffs: |
| * |
| * Linear: the basic format, bad for memory bandwidth, bad for cache |
| * use. Zero-copy, though. Renderable. |
| * |
| * Tiled: Not compressed, but cache-optimized. Expensive to write into |
| * (due to software tiling), but cheap to sample from. Ideal for most |
| * textures. |
| * |
| * AFBC: Compressed and renderable (so always desirable for non-scanout |
| * rendertargets). Cheap to sample from. The format is black box, so we |
| * can't read/write from software. |
| * |
| * Tiling textures is almost always faster, unless we only use it once. |
| * Only a few types of resources can be tiled, ensure the bind is only |
| * (a combination of) one of the following */ |
| |
| const unsigned valid_binding = |
| PIPE_BIND_DEPTH_STENCIL | |
| PIPE_BIND_RENDER_TARGET | |
| PIPE_BIND_BLENDABLE | |
| PIPE_BIND_SAMPLER_VIEW | |
| PIPE_BIND_DISPLAY_TARGET; |
| |
| unsigned bpp = util_format_get_blocksizebits(pres->internal_format); |
| bool is_2d = (res->target == PIPE_TEXTURE_2D) || (res->target == PIPE_TEXTURE_RECT); |
| bool is_sane_bpp = bpp == 8 || bpp == 16 || bpp == 24 || bpp == 32 || bpp == 64 || bpp == 128; |
| bool should_tile = (res->usage != PIPE_USAGE_STREAM); |
| bool must_tile = (res->bind & PIPE_BIND_DEPTH_STENCIL) && |
| (dev->quirks & (MIDGARD_SFBD | IS_BIFROST)); |
| bool can_tile = is_2d && is_sane_bpp && ((res->bind & ~valid_binding) == 0); |
| |
| /* FBOs we would like to checksum, if at all possible */ |
| bool can_checksum = !(res->bind & ~valid_binding); |
| bool should_checksum = res->bind & PIPE_BIND_RENDER_TARGET; |
| |
| pres->checksummed = can_checksum && should_checksum; |
| |
| /* Set the modifier appropriately */ |
| assert(!(must_tile && !can_tile)); /* must_tile => can_tile */ |
| pres->modifier = ((can_tile && should_tile) || must_tile) ? |
| DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED : |
| DRM_FORMAT_MOD_LINEAR; |
| pres->modifier_constant = must_tile || !can_tile; |
| |
| size_t bo_size; |
| |
| panfrost_setup_slices(pres, &bo_size); |
| |
| /* We create a BO immediately but don't bother mapping, since we don't |
| * care to map e.g. FBOs which the CPU probably won't touch */ |
| pres->bo = panfrost_bo_create(dev, bo_size, PAN_BO_DELAY_MMAP); |
| } |
| |
| void |
| panfrost_resource_set_damage_region(struct pipe_screen *screen, |
| struct pipe_resource *res, |
| unsigned int nrects, |
| const struct pipe_box *rects) |
| { |
| struct panfrost_resource *pres = pan_resource(res); |
| struct pipe_scissor_state *damage_extent = &pres->damage.extent; |
| unsigned int i; |
| |
| if (pres->damage.inverted_rects) |
| ralloc_free(pres->damage.inverted_rects); |
| |
| memset(&pres->damage, 0, sizeof(pres->damage)); |
| |
| pres->damage.inverted_rects = |
| pan_subtract_damage(pres, |
| res->width0, res->height0, |
| nrects, rects, &pres->damage.inverted_len); |
| |
| /* Track the damage extent: the quad including all damage regions. Will |
| * be used restrict the rendering area */ |
| |
| damage_extent->minx = 0xffff; |
| damage_extent->miny = 0xffff; |
| |
| for (i = 0; i < nrects; i++) { |
| int x = rects[i].x, w = rects[i].width, h = rects[i].height; |
| int y = res->height0 - (rects[i].y + h); |
| |
| damage_extent->minx = MIN2(damage_extent->minx, x); |
| damage_extent->miny = MIN2(damage_extent->miny, y); |
| damage_extent->maxx = MAX2(damage_extent->maxx, |
| MIN2(x + w, res->width0)); |
| damage_extent->maxy = MAX2(damage_extent->maxy, |
| MIN2(y + h, res->height0)); |
| } |
| |
| if (nrects == 0) { |
| damage_extent->minx = 0; |
| damage_extent->miny = 0; |
| damage_extent->maxx = res->width0; |
| damage_extent->maxy = res->height0; |
| } |
| |
| } |
| |
| static struct pipe_resource * |
| panfrost_resource_create_with_modifier(struct pipe_screen *screen, |
| const struct pipe_resource *template, |
| uint64_t modifier) |
| { |
| struct panfrost_device *dev = pan_device(screen); |
| |
| /* Make sure we're familiar */ |
| switch (template->target) { |
| case PIPE_BUFFER: |
| case PIPE_TEXTURE_1D: |
| case PIPE_TEXTURE_2D: |
| case PIPE_TEXTURE_3D: |
| case PIPE_TEXTURE_CUBE: |
| case PIPE_TEXTURE_RECT: |
| case PIPE_TEXTURE_1D_ARRAY: |
| case PIPE_TEXTURE_2D_ARRAY: |
| break; |
| default: |
| unreachable("Unknown texture target\n"); |
| } |
| |
| if (dev->ro && (template->bind & |
| (PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_SCANOUT | PIPE_BIND_SHARED))) |
| return panfrost_create_scanout_res(screen, template); |
| |
| struct panfrost_resource *so = rzalloc(screen, struct panfrost_resource); |
| so->base = *template; |
| so->base.screen = screen; |
| so->internal_format = template->format; |
| |
| pipe_reference_init(&so->base.reference, 1); |
| |
| util_range_init(&so->valid_buffer_range); |
| |
| panfrost_resource_create_bo(dev, so); |
| panfrost_resource_set_damage_region(NULL, &so->base, 0, NULL); |
| |
| if (template->bind & PIPE_BIND_INDEX_BUFFER) |
| so->index_cache = rzalloc(so, struct panfrost_minmax_cache); |
| |
| return (struct pipe_resource *)so; |
| } |
| |
| /* Default is to create a resource as don't care */ |
| |
| static struct pipe_resource * |
| panfrost_resource_create(struct pipe_screen *screen, |
| const struct pipe_resource *template) |
| { |
| return panfrost_resource_create_with_modifier(screen, template, |
| DRM_FORMAT_MOD_INVALID); |
| } |
| |
| static void |
| panfrost_resource_destroy(struct pipe_screen *screen, |
| struct pipe_resource *pt) |
| { |
| struct panfrost_device *dev = pan_device(screen); |
| struct panfrost_resource *rsrc = (struct panfrost_resource *) pt; |
| |
| if (rsrc->scanout) |
| renderonly_scanout_destroy(rsrc->scanout, dev->ro); |
| |
| if (rsrc->bo) |
| panfrost_bo_unreference(rsrc->bo); |
| |
| if (rsrc->slices[0].checksum_bo) |
| panfrost_bo_unreference(rsrc->slices[0].checksum_bo); |
| |
| util_range_destroy(&rsrc->valid_buffer_range); |
| ralloc_free(rsrc); |
| } |
| |
| |
| static void * |
| panfrost_transfer_map(struct pipe_context *pctx, |
| struct pipe_resource *resource, |
| unsigned level, |
| unsigned usage, /* a combination of PIPE_TRANSFER_x */ |
| const struct pipe_box *box, |
| struct pipe_transfer **out_transfer) |
| { |
| struct panfrost_context *ctx = pan_context(pctx); |
| struct panfrost_device *dev = pan_device(pctx->screen); |
| struct panfrost_resource *rsrc = pan_resource(resource); |
| int bytes_per_pixel = util_format_get_blocksize(rsrc->internal_format); |
| struct panfrost_bo *bo = rsrc->bo; |
| |
| struct panfrost_gtransfer *transfer = rzalloc(pctx, struct panfrost_gtransfer); |
| transfer->base.level = level; |
| transfer->base.usage = usage; |
| transfer->base.box = *box; |
| |
| pipe_resource_reference(&transfer->base.resource, resource); |
| |
| *out_transfer = &transfer->base; |
| |
| /* If we haven't already mmaped, now's the time */ |
| panfrost_bo_mmap(bo); |
| |
| if (dev->debug & (PAN_DBG_TRACE | PAN_DBG_SYNC)) |
| pandecode_inject_mmap(bo->gpu, bo->cpu, bo->size, NULL); |
| |
| bool create_new_bo = usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE; |
| bool copy_resource = false; |
| |
| if (!create_new_bo && |
| !(usage & PIPE_TRANSFER_UNSYNCHRONIZED) && |
| (usage & PIPE_TRANSFER_WRITE) && |
| !(resource->target == PIPE_BUFFER |
| && !util_ranges_intersect(&rsrc->valid_buffer_range, box->x, box->x + box->width)) && |
| panfrost_pending_batches_access_bo(ctx, bo)) { |
| |
| /* When a resource to be modified is already being used by a |
| * pending batch, it is often faster to copy the whole BO than |
| * to flush and split the frame in two. This also mostly |
| * mitigates broken depth reload. |
| */ |
| |
| panfrost_flush_batches_accessing_bo(ctx, bo, false); |
| panfrost_bo_wait(bo, INT64_MAX, false); |
| |
| create_new_bo = true; |
| copy_resource = true; |
| } |
| |
| if (create_new_bo) { |
| /* If the BO is used by one of the pending batches or if it's |
| * not ready yet (still accessed by one of the already flushed |
| * batches), we try to allocate a new one to avoid waiting. |
| */ |
| if (panfrost_pending_batches_access_bo(ctx, bo) || |
| !panfrost_bo_wait(bo, 0, true)) { |
| /* We want the BO to be MMAPed. */ |
| uint32_t flags = bo->flags & ~PAN_BO_DELAY_MMAP; |
| struct panfrost_bo *newbo = NULL; |
| |
| /* When the BO has been imported/exported, we can't |
| * replace it by another one, otherwise the |
| * importer/exporter wouldn't see the change we're |
| * doing to it. |
| */ |
| if (!(bo->flags & PAN_BO_SHARED)) |
| newbo = panfrost_bo_create(dev, bo->size, |
| flags); |
| |
| if (newbo) { |
| if (copy_resource) |
| memcpy(newbo->cpu, rsrc->bo->cpu, bo->size); |
| |
| panfrost_bo_unreference(bo); |
| rsrc->bo = newbo; |
| bo = newbo; |
| } else { |
| /* Allocation failed or was impossible, let's |
| * fall back on a flush+wait. |
| */ |
| panfrost_flush_batches_accessing_bo(ctx, bo, true); |
| panfrost_bo_wait(bo, INT64_MAX, true); |
| } |
| } |
| } else if ((usage & PIPE_TRANSFER_WRITE) |
| && resource->target == PIPE_BUFFER |
| && !util_ranges_intersect(&rsrc->valid_buffer_range, box->x, box->x + box->width)) { |
| /* No flush for writes to uninitialized */ |
| } else if (!(usage & PIPE_TRANSFER_UNSYNCHRONIZED)) { |
| if (usage & PIPE_TRANSFER_WRITE) { |
| panfrost_flush_batches_accessing_bo(ctx, bo, true); |
| panfrost_bo_wait(bo, INT64_MAX, true); |
| } else if (usage & PIPE_TRANSFER_READ) { |
| panfrost_flush_batches_accessing_bo(ctx, bo, false); |
| panfrost_bo_wait(bo, INT64_MAX, false); |
| } |
| } |
| |
| if (rsrc->modifier != DRM_FORMAT_MOD_LINEAR) { |
| /* Non-linear resources need to be indirectly mapped */ |
| |
| if (usage & PIPE_TRANSFER_MAP_DIRECTLY) |
| return NULL; |
| |
| transfer->base.stride = box->width * bytes_per_pixel; |
| transfer->base.layer_stride = transfer->base.stride * box->height; |
| transfer->map = ralloc_size(transfer, transfer->base.layer_stride * box->depth); |
| assert(box->depth == 1); |
| |
| if ((usage & PIPE_TRANSFER_READ) && rsrc->slices[level].initialized) { |
| if (drm_is_afbc(rsrc->modifier)) { |
| unreachable("Unimplemented: reads from AFBC"); |
| } else if (rsrc->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) { |
| panfrost_load_tiled_image( |
| transfer->map, |
| bo->cpu + rsrc->slices[level].offset, |
| box->x, box->y, box->width, box->height, |
| transfer->base.stride, |
| rsrc->slices[level].stride, |
| rsrc->internal_format); |
| } |
| } |
| |
| return transfer->map; |
| } else { |
| /* Direct, persistent writes create holes in time for |
| * caching... I don't know if this is actually possible but we |
| * should still get it right */ |
| |
| unsigned dpw = PIPE_TRANSFER_MAP_DIRECTLY | PIPE_TRANSFER_WRITE | PIPE_TRANSFER_PERSISTENT; |
| |
| if ((usage & dpw) == dpw && rsrc->index_cache) |
| return NULL; |
| |
| transfer->base.stride = rsrc->slices[level].stride; |
| transfer->base.layer_stride = panfrost_get_layer_stride( |
| rsrc->slices, rsrc->base.target == PIPE_TEXTURE_3D, |
| rsrc->cubemap_stride, level); |
| |
| /* By mapping direct-write, we're implicitly already |
| * initialized (maybe), so be conservative */ |
| |
| if (usage & PIPE_TRANSFER_WRITE) { |
| rsrc->slices[level].initialized = true; |
| panfrost_minmax_cache_invalidate(rsrc->index_cache, &transfer->base); |
| } |
| |
| return bo->cpu |
| + rsrc->slices[level].offset |
| + transfer->base.box.z * transfer->base.layer_stride |
| + transfer->base.box.y * rsrc->slices[level].stride |
| + transfer->base.box.x * bytes_per_pixel; |
| } |
| } |
| |
| static void |
| panfrost_transfer_unmap(struct pipe_context *pctx, |
| struct pipe_transfer *transfer) |
| { |
| /* Gallium expects writeback here, so we tile */ |
| |
| struct panfrost_gtransfer *trans = pan_transfer(transfer); |
| struct panfrost_resource *prsrc = (struct panfrost_resource *) transfer->resource; |
| |
| /* Mark whatever we wrote as written */ |
| if (transfer->usage & PIPE_TRANSFER_WRITE) |
| prsrc->slices[transfer->level].initialized = true; |
| |
| if (trans->map) { |
| struct panfrost_bo *bo = prsrc->bo; |
| |
| if (transfer->usage & PIPE_TRANSFER_WRITE) { |
| if (drm_is_afbc(prsrc->modifier)) { |
| unreachable("Unimplemented: writes to AFBC\n"); |
| } else if (prsrc->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) { |
| assert(transfer->box.depth == 1); |
| |
| /* Do we overwrite the entire resource? If so, |
| * we don't need an intermediate blit so it's a |
| * good time to switch the modifier. */ |
| |
| bool discards_content = prsrc->base.last_level == 0 |
| && transfer->box.width == prsrc->base.width0 |
| && transfer->box.height == prsrc->base.height0 |
| && transfer->box.x == 0 |
| && transfer->box.y == 0 |
| && !prsrc->modifier_constant; |
| |
| /* It also serves as a good heuristic for |
| * streaming textures (e.g. in video players), |
| * but we could do better */ |
| |
| if (discards_content) |
| ++prsrc->modifier_updates; |
| |
| if (prsrc->modifier_updates >= LAYOUT_CONVERT_THRESHOLD) |
| { |
| prsrc->modifier = DRM_FORMAT_MOD_LINEAR; |
| |
| util_copy_rect( |
| bo->cpu + prsrc->slices[0].offset, |
| prsrc->base.format, |
| prsrc->slices[0].stride, |
| 0, 0, |
| transfer->box.width, |
| transfer->box.height, |
| trans->map, |
| transfer->stride, |
| 0, 0); |
| } else { |
| panfrost_store_tiled_image( |
| bo->cpu + prsrc->slices[transfer->level].offset, |
| trans->map, |
| transfer->box.x, transfer->box.y, |
| transfer->box.width, transfer->box.height, |
| prsrc->slices[transfer->level].stride, |
| transfer->stride, |
| prsrc->internal_format); |
| } |
| } |
| } |
| } |
| |
| |
| util_range_add(&prsrc->base, &prsrc->valid_buffer_range, |
| transfer->box.x, |
| transfer->box.x + transfer->box.width); |
| |
| panfrost_minmax_cache_invalidate(prsrc->index_cache, transfer); |
| |
| /* Derefence the resource */ |
| pipe_resource_reference(&transfer->resource, NULL); |
| |
| /* Transfer itself is RALLOCed at the moment */ |
| ralloc_free(transfer); |
| } |
| |
| static void |
| panfrost_transfer_flush_region(struct pipe_context *pctx, |
| struct pipe_transfer *transfer, |
| const struct pipe_box *box) |
| { |
| struct panfrost_resource *rsc = pan_resource(transfer->resource); |
| |
| if (transfer->resource->target == PIPE_BUFFER) { |
| util_range_add(&rsc->base, &rsc->valid_buffer_range, |
| transfer->box.x + box->x, |
| transfer->box.x + box->x + box->width); |
| } else { |
| unsigned level = transfer->level; |
| rsc->slices[level].initialized = true; |
| } |
| } |
| |
| static void |
| panfrost_invalidate_resource(struct pipe_context *pctx, struct pipe_resource *prsc) |
| { |
| /* TODO */ |
| } |
| |
| static enum pipe_format |
| panfrost_resource_get_internal_format(struct pipe_resource *rsrc) |
| { |
| struct panfrost_resource *prsrc = (struct panfrost_resource *) rsrc; |
| return prsrc->internal_format; |
| } |
| |
| static bool |
| panfrost_generate_mipmap( |
| struct pipe_context *pctx, |
| struct pipe_resource *prsrc, |
| enum pipe_format format, |
| unsigned base_level, |
| unsigned last_level, |
| unsigned first_layer, |
| unsigned last_layer) |
| { |
| struct panfrost_resource *rsrc = pan_resource(prsrc); |
| |
| /* Generating a mipmap invalidates the written levels, so make that |
| * explicit so we don't try to wallpaper them back and end up with |
| * u_blitter recursion */ |
| |
| assert(rsrc->bo); |
| for (unsigned l = base_level + 1; l <= last_level; ++l) |
| rsrc->slices[l].initialized = false; |
| |
| /* Beyond that, we just delegate the hard stuff. */ |
| |
| bool blit_res = util_gen_mipmap( |
| pctx, prsrc, format, |
| base_level, last_level, |
| first_layer, last_layer, |
| PIPE_TEX_FILTER_LINEAR); |
| |
| return blit_res; |
| } |
| |
| /* Computes the address to a texture at a particular slice */ |
| |
| mali_ptr |
| panfrost_get_texture_address( |
| struct panfrost_resource *rsrc, |
| unsigned level, unsigned face, unsigned sample) |
| { |
| bool is_3d = rsrc->base.target == PIPE_TEXTURE_3D; |
| return rsrc->bo->gpu + panfrost_texture_offset(rsrc->slices, is_3d, rsrc->cubemap_stride, level, face, sample); |
| } |
| |
| static void |
| panfrost_resource_set_stencil(struct pipe_resource *prsrc, |
| struct pipe_resource *stencil) |
| { |
| pan_resource(prsrc)->separate_stencil = pan_resource(stencil); |
| } |
| |
| static struct pipe_resource * |
| panfrost_resource_get_stencil(struct pipe_resource *prsrc) |
| { |
| return &pan_resource(prsrc)->separate_stencil->base; |
| } |
| |
| static const struct u_transfer_vtbl transfer_vtbl = { |
| .resource_create = panfrost_resource_create, |
| .resource_destroy = panfrost_resource_destroy, |
| .transfer_map = panfrost_transfer_map, |
| .transfer_unmap = panfrost_transfer_unmap, |
| .transfer_flush_region = panfrost_transfer_flush_region, |
| .get_internal_format = panfrost_resource_get_internal_format, |
| .set_stencil = panfrost_resource_set_stencil, |
| .get_stencil = panfrost_resource_get_stencil, |
| }; |
| |
| void |
| panfrost_resource_screen_init(struct pipe_screen *pscreen) |
| { |
| struct panfrost_device *dev = pan_device(pscreen); |
| |
| bool fake_rgtc = !panfrost_supports_compressed_format(dev, MALI_BC4_UNORM); |
| |
| //pscreen->base.resource_create_with_modifiers = |
| // panfrost_resource_create_with_modifiers; |
| pscreen->resource_create = u_transfer_helper_resource_create; |
| pscreen->resource_destroy = u_transfer_helper_resource_destroy; |
| pscreen->resource_from_handle = panfrost_resource_from_handle; |
| pscreen->resource_get_handle = panfrost_resource_get_handle; |
| pscreen->transfer_helper = u_transfer_helper_create(&transfer_vtbl, |
| true, false, |
| fake_rgtc, true); |
| } |
| |
| void |
| panfrost_resource_context_init(struct pipe_context *pctx) |
| { |
| pctx->transfer_map = u_transfer_helper_transfer_map; |
| pctx->transfer_unmap = u_transfer_helper_transfer_unmap; |
| pctx->create_surface = panfrost_create_surface; |
| pctx->surface_destroy = panfrost_surface_destroy; |
| pctx->resource_copy_region = util_resource_copy_region; |
| pctx->blit = panfrost_blit; |
| pctx->generate_mipmap = panfrost_generate_mipmap; |
| pctx->flush_resource = panfrost_flush_resource; |
| pctx->invalidate_resource = panfrost_invalidate_resource; |
| pctx->transfer_flush_region = u_transfer_helper_transfer_flush_region; |
| pctx->buffer_subdata = u_default_buffer_subdata; |
| pctx->texture_subdata = u_default_texture_subdata; |
| } |