Path: blob/21.2-virgl/src/gallium/drivers/panfrost/pan_resource.c
4570 views
/*1* Copyright (C) 2008 VMware, Inc.2* Copyright (C) 2012 Rob Clark <[email protected]>3* Copyright (C) 2014-2017 Broadcom4* Copyright (C) 2018-2019 Alyssa Rosenzweig5* Copyright (C) 2019 Collabora, Ltd.6*7* Permission is hereby granted, free of charge, to any person obtaining a8* copy of this software and associated documentation files (the "Software"),9* to deal in the Software without restriction, including without limitation10* the rights to use, copy, modify, merge, publish, distribute, sublicense,11* and/or sell copies of the Software, and to permit persons to whom the12* Software is furnished to do so, subject to the following conditions:13*14* The above copyright notice and this permission notice (including the next15* paragraph) shall be included in all copies or substantial portions of the16* Software.17*18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR19* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,20* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL21* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER22* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,23* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE24* SOFTWARE.25*26* Authors (Collabora):27* Tomeu Vizoso <[email protected]>28* Alyssa Rosenzweig <[email protected]>29*30*/3132#include <xf86drm.h>33#include <fcntl.h>34#include "drm-uapi/drm_fourcc.h"3536#include "frontend/winsys_handle.h"37#include "util/format/u_format.h"38#include "util/u_memory.h"39#include "util/u_surface.h"40#include "util/u_transfer.h"41#include "util/u_transfer_helper.h"42#include "util/u_gen_mipmap.h"43#include "util/u_drm.h"4445#include "pan_bo.h"46#include "pan_context.h"47#include "pan_screen.h"48#include "pan_resource.h"49#include "pan_util.h"50#include "pan_tiling.h"51#include "decode.h"52#include "panfrost-quirks.h"5354static bool55panfrost_should_checksum(const struct panfrost_device *dev, const struct panfrost_resource *pres);5657static struct pipe_resource *58panfrost_resource_from_handle(struct pipe_screen *pscreen,59const struct pipe_resource *templat,60struct winsys_handle *whandle,61unsigned usage)62{63struct panfrost_device *dev = pan_device(pscreen);64struct panfrost_resource *rsc;65struct pipe_resource *prsc;6667assert(whandle->type == WINSYS_HANDLE_TYPE_FD);6869rsc = rzalloc(pscreen, struct panfrost_resource);70if (!rsc)71return NULL;7273prsc = &rsc->base;7475*prsc = *templat;7677pipe_reference_init(&prsc->reference, 1);78prsc->screen = pscreen;7980uint64_t mod = whandle->modifier == DRM_FORMAT_MOD_INVALID ?81DRM_FORMAT_MOD_LINEAR : whandle->modifier;82enum mali_texture_dimension dim =83panfrost_translate_texture_dimension(templat->target);84enum pan_image_crc_mode crc_mode =85panfrost_should_checksum(dev, rsc) ?86PAN_IMAGE_CRC_OOB : PAN_IMAGE_CRC_NONE;87struct pan_image_explicit_layout explicit_layout = {88.offset = whandle->offset,89.line_stride = whandle->stride,90};9192bool valid = pan_image_layout_init(dev, &rsc->image.layout, mod,93templat->format, dim,94prsc->width0, prsc->height0,95prsc->depth0, prsc->array_size,96MAX2(prsc->nr_samples, 1), 1,97crc_mode, &explicit_layout);9899if (!valid) {100ralloc_free(rsc);101return NULL;102}103104rsc->image.data.bo = panfrost_bo_import(dev, whandle->handle);105/* Sometimes an import can fail e.g. on an invalid buffer fd, out of106* memory space to mmap it etc.107*/108if (!rsc->image.data.bo) {109ralloc_free(rsc);110return NULL;111}112if (rsc->image.layout.crc_mode == PAN_IMAGE_CRC_OOB)113rsc->image.crc.bo = panfrost_bo_create(dev, rsc->image.layout.crc_size, 0, "CRC data");114115rsc->modifier_constant = true;116117BITSET_SET(rsc->valid.data, 0);118panfrost_resource_set_damage_region(pscreen, &rsc->base, 0, NULL);119120if (dev->ro) {121rsc->scanout =122renderonly_create_gpu_import_for_resource(prsc, dev->ro, NULL);123/* failure is expected in some cases.. */124}125126return prsc;127}128129static bool130panfrost_resource_get_handle(struct pipe_screen *pscreen,131struct pipe_context *ctx,132struct pipe_resource *pt,133struct winsys_handle *handle,134unsigned usage)135{136struct panfrost_device *dev = pan_device(pscreen);137struct panfrost_resource *rsrc = (struct panfrost_resource *) pt;138struct renderonly_scanout *scanout = rsrc->scanout;139140handle->modifier = rsrc->image.layout.modifier;141rsrc->modifier_constant = true;142143if (handle->type == WINSYS_HANDLE_TYPE_SHARED) {144return false;145} else if (handle->type == WINSYS_HANDLE_TYPE_KMS) {146if (dev->ro) {147return renderonly_get_handle(scanout, handle);148} else {149handle->handle = rsrc->image.data.bo->gem_handle;150handle->stride = rsrc->image.layout.slices[0].line_stride;151handle->offset = rsrc->image.layout.slices[0].offset;152return true;153}154} else if (handle->type == WINSYS_HANDLE_TYPE_FD) {155if (scanout) {156struct drm_prime_handle args = {157.handle = scanout->handle,158.flags = DRM_CLOEXEC,159};160161int ret = drmIoctl(dev->ro->kms_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);162if (ret == -1)163return false;164165handle->stride = scanout->stride;166handle->handle = args.fd;167168return true;169} else {170int fd = panfrost_bo_export(rsrc->image.data.bo);171172if (fd < 0)173return false;174175handle->handle = fd;176handle->stride = rsrc->image.layout.slices[0].line_stride;177handle->offset = rsrc->image.layout.slices[0].offset;178return true;179}180}181182return false;183}184185static void186panfrost_flush_resource(struct pipe_context *pctx, struct pipe_resource *prsc)187{188/* TODO */189}190191static struct pipe_surface *192panfrost_create_surface(struct pipe_context *pipe,193struct pipe_resource *pt,194const struct pipe_surface *surf_tmpl)195{196struct pipe_surface *ps = NULL;197198ps = CALLOC_STRUCT(pipe_surface);199200if (ps) {201pipe_reference_init(&ps->reference, 1);202pipe_resource_reference(&ps->texture, pt);203ps->context = pipe;204ps->format = surf_tmpl->format;205206if (pt->target != PIPE_BUFFER) {207assert(surf_tmpl->u.tex.level <= pt->last_level);208ps->width = u_minify(pt->width0, surf_tmpl->u.tex.level);209ps->height = u_minify(pt->height0, surf_tmpl->u.tex.level);210ps->nr_samples = surf_tmpl->nr_samples;211ps->u.tex.level = surf_tmpl->u.tex.level;212ps->u.tex.first_layer = surf_tmpl->u.tex.first_layer;213ps->u.tex.last_layer = surf_tmpl->u.tex.last_layer;214} else {215/* setting width as number of elements should get us correct renderbuffer width */216ps->width = surf_tmpl->u.buf.last_element - surf_tmpl->u.buf.first_element + 1;217ps->height = pt->height0;218ps->u.buf.first_element = surf_tmpl->u.buf.first_element;219ps->u.buf.last_element = surf_tmpl->u.buf.last_element;220assert(ps->u.buf.first_element <= ps->u.buf.last_element);221assert(ps->u.buf.last_element < ps->width);222}223}224225return ps;226}227228static void229panfrost_surface_destroy(struct pipe_context *pipe,230struct pipe_surface *surf)231{232assert(surf->texture);233pipe_resource_reference(&surf->texture, NULL);234free(surf);235}236237static struct pipe_resource *238panfrost_create_scanout_res(struct pipe_screen *screen,239const struct pipe_resource *template,240uint64_t modifier)241{242struct panfrost_device *dev = pan_device(screen);243struct renderonly_scanout *scanout;244struct winsys_handle handle;245struct pipe_resource *res;246struct pipe_resource scanout_templat = *template;247248/* Tiled formats need to be tile aligned */249if (modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {250scanout_templat.width0 = ALIGN_POT(template->width0, 16);251scanout_templat.height0 = ALIGN_POT(template->height0, 16);252}253254/* AFBC formats need a header. Thankfully we don't care about the255* stride so we can just use wonky dimensions as long as the right256* number of bytes are allocated at the end of the day... this implies257* that stride/pitch is invalid for AFBC buffers */258259if (drm_is_afbc(modifier)) {260/* Space for the header. We need to keep vaguely similar261* dimensions because... reasons... to allocate with renderonly262* as a dumb buffer. To do so, after the usual 16x16 alignment,263* we add on extra rows for the header. The order of operations264* matters here, the extra rows of padding can in fact be265* needed and missing them can lead to faults. */266267unsigned header_size = panfrost_afbc_header_size(268template->width0, template->height0);269270unsigned pitch = ALIGN_POT(template->width0, 16) *271util_format_get_blocksize(template->format);272273unsigned header_rows =274DIV_ROUND_UP(header_size, pitch);275276scanout_templat.width0 = ALIGN_POT(template->width0, 16);277scanout_templat.height0 = ALIGN_POT(template->height0, 16) + header_rows;278}279280scanout = renderonly_scanout_for_resource(&scanout_templat,281dev->ro, &handle);282if (!scanout)283return NULL;284285assert(handle.type == WINSYS_HANDLE_TYPE_FD);286handle.modifier = modifier;287res = screen->resource_from_handle(screen, template, &handle,288PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE);289close(handle.handle);290if (!res)291return NULL;292293struct panfrost_resource *pres = pan_resource(res);294295pres->scanout = scanout;296297return res;298}299300static inline bool301panfrost_is_2d(const struct panfrost_resource *pres)302{303return (pres->base.target == PIPE_TEXTURE_2D)304|| (pres->base.target == PIPE_TEXTURE_RECT);305}306307/* Based on the usage, determine if it makes sense to use u-inteleaved tiling.308* We only have routines to tile 2D textures of sane bpps. On the hardware309* level, not all usages are valid for tiling. Finally, if the app is hinting310* that the contents frequently change, tiling will be a loss.311*312* On platforms where it is supported, AFBC is even better. */313314static bool315panfrost_should_afbc(struct panfrost_device *dev,316const struct panfrost_resource *pres,317enum pipe_format fmt)318{319/* AFBC resources may be rendered to, textured from, or shared across320* processes, but may not be used as e.g buffers */321const unsigned valid_binding =322PIPE_BIND_DEPTH_STENCIL |323PIPE_BIND_RENDER_TARGET |324PIPE_BIND_BLENDABLE |325PIPE_BIND_SAMPLER_VIEW |326PIPE_BIND_DISPLAY_TARGET |327PIPE_BIND_SCANOUT |328PIPE_BIND_SHARED;329330if (pres->base.bind & ~valid_binding)331return false;332333/* AFBC introduced with Mali T760 */334if (dev->quirks & MIDGARD_NO_AFBC)335return false;336337/* AFBC<-->staging is expensive */338if (pres->base.usage == PIPE_USAGE_STREAM)339return false;340341/* Only a small selection of formats are AFBC'able */342if (!panfrost_format_supports_afbc(dev, fmt))343return false;344345/* AFBC does not support layered (GLES3 style) multisampling. Use346* EXT_multisampled_render_to_texture instead */347if (pres->base.nr_samples > 1)348return false;349350switch (pres->base.target) {351case PIPE_TEXTURE_2D:352case PIPE_TEXTURE_2D_ARRAY:353case PIPE_TEXTURE_RECT:354break;355356case PIPE_TEXTURE_3D:357/* 3D AFBC is only supported on Bifrost v7+. It's supposed to358* be supported on Midgard but it doesn't seem to work */359if (dev->arch < 7)360return false;361362break;363364default:365return false;366}367368/* For one tile, AFBC is a loss compared to u-interleaved */369if (pres->base.width0 <= 16 && pres->base.height0 <= 16)370return false;371372/* Otherwise, we'd prefer AFBC as it is dramatically more efficient373* than linear or usually even u-interleaved */374return true;375}376377static bool378panfrost_should_tile(struct panfrost_device *dev,379const struct panfrost_resource *pres,380enum pipe_format fmt)381{382const unsigned valid_binding =383PIPE_BIND_DEPTH_STENCIL |384PIPE_BIND_RENDER_TARGET |385PIPE_BIND_BLENDABLE |386PIPE_BIND_SAMPLER_VIEW |387PIPE_BIND_DISPLAY_TARGET |388PIPE_BIND_SCANOUT |389PIPE_BIND_SHARED;390391unsigned bpp = util_format_get_blocksizebits(fmt);392393bool is_sane_bpp =394bpp == 8 || bpp == 16 || bpp == 24 || bpp == 32 ||395bpp == 64 || bpp == 128;396397bool can_tile = panfrost_is_2d(pres)398&& is_sane_bpp399&& ((pres->base.bind & ~valid_binding) == 0);400401return can_tile && (pres->base.usage != PIPE_USAGE_STREAM);402}403404static uint64_t405panfrost_best_modifier(struct panfrost_device *dev,406const struct panfrost_resource *pres,407enum pipe_format fmt)408{409if (panfrost_should_afbc(dev, pres, fmt)) {410uint64_t afbc =411AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |412AFBC_FORMAT_MOD_SPARSE;413414if (panfrost_afbc_can_ytr(pres->base.format))415afbc |= AFBC_FORMAT_MOD_YTR;416417return DRM_FORMAT_MOD_ARM_AFBC(afbc);418} else if (panfrost_should_tile(dev, pres, fmt))419return DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED;420else421return DRM_FORMAT_MOD_LINEAR;422}423424static bool425panfrost_should_checksum(const struct panfrost_device *dev, const struct panfrost_resource *pres)426{427/* When checksumming is enabled, the tile data must fit in the428* size of the writeback buffer, so don't checksum formats429* that use too much space. */430431unsigned bytes_per_pixel_max = (dev->arch == 6) ? 6 : 4;432433unsigned bytes_per_pixel = MAX2(pres->base.nr_samples, 1) *434util_format_get_blocksize(pres->base.format);435436return pres->base.bind & PIPE_BIND_RENDER_TARGET &&437panfrost_is_2d(pres) &&438bytes_per_pixel <= bytes_per_pixel_max &&439pres->base.last_level == 0 &&440!(dev->debug & PAN_DBG_NO_CRC);441}442443static void444panfrost_resource_setup(struct panfrost_device *dev,445struct panfrost_resource *pres,446uint64_t modifier, enum pipe_format fmt)447{448uint64_t chosen_mod = modifier != DRM_FORMAT_MOD_INVALID ?449modifier : panfrost_best_modifier(dev, pres, fmt);450enum pan_image_crc_mode crc_mode =451panfrost_should_checksum(dev, pres) ?452PAN_IMAGE_CRC_INBAND : PAN_IMAGE_CRC_NONE;453enum mali_texture_dimension dim =454panfrost_translate_texture_dimension(pres->base.target);455456/* We can only switch tiled->linear if the resource isn't already457* linear and if we control the modifier */458pres->modifier_constant =459!(chosen_mod != DRM_FORMAT_MOD_LINEAR &&460modifier == DRM_FORMAT_MOD_INVALID);461462/* Z32_S8X24 variants are actually stored in 2 planes (one per463* component), we have to adjust the format on the first plane.464*/465if (fmt == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT)466fmt = PIPE_FORMAT_Z32_FLOAT;467468ASSERTED bool valid =469pan_image_layout_init(dev, &pres->image.layout,470chosen_mod, fmt, dim,471pres->base.width0,472pres->base.height0,473pres->base.depth0,474pres->base.array_size,475MAX2(pres->base.nr_samples, 1),476pres->base.last_level + 1,477crc_mode, NULL);478assert(valid);479}480481static void482panfrost_resource_init_afbc_headers(struct panfrost_resource *pres)483{484panfrost_bo_mmap(pres->image.data.bo);485486unsigned nr_samples = MAX2(pres->base.nr_samples, 1);487488for (unsigned i = 0; i < pres->base.array_size; ++i) {489for (unsigned l = 0; l <= pres->base.last_level; ++l) {490struct pan_image_slice_layout *slice = &pres->image.layout.slices[l];491492for (unsigned s = 0; s < nr_samples; ++s) {493void *ptr = pres->image.data.bo->ptr.cpu +494(i * pres->image.layout.array_stride) +495slice->offset +496(s * slice->afbc.surface_stride);497498/* Zero-ed AFBC headers seem to encode a plain499* black. Let's use this pattern to keep the500* initialization simple.501*/502memset(ptr, 0, slice->afbc.header_size);503}504}505}506}507508void509panfrost_resource_set_damage_region(struct pipe_screen *screen,510struct pipe_resource *res,511unsigned int nrects,512const struct pipe_box *rects)513{514struct panfrost_device *dev = pan_device(screen);515struct panfrost_resource *pres = pan_resource(res);516struct pipe_scissor_state *damage_extent = &pres->damage.extent;517unsigned int i;518519if (!pan_is_bifrost(dev) && !(dev->quirks & NO_TILE_ENABLE_MAP) &&520nrects > 1) {521if (!pres->damage.tile_map.data) {522pres->damage.tile_map.stride =523ALIGN_POT(DIV_ROUND_UP(res->width0, 32 * 8), 64);524pres->damage.tile_map.size =525pres->damage.tile_map.stride *526DIV_ROUND_UP(res->height0, 32);527pres->damage.tile_map.data =528ralloc_size(pres, pres->damage.tile_map.size);529}530531memset(pres->damage.tile_map.data, 0, pres->damage.tile_map.size);532pres->damage.tile_map.enable = true;533} else {534pres->damage.tile_map.enable = false;535}536537/* Track the damage extent: the quad including all damage regions. Will538* be used restrict the rendering area */539540damage_extent->minx = 0xffff;541damage_extent->miny = 0xffff;542543unsigned enable_count = 0;544545for (i = 0; i < nrects; i++) {546int x = rects[i].x, w = rects[i].width, h = rects[i].height;547int y = res->height0 - (rects[i].y + h);548549damage_extent->minx = MIN2(damage_extent->minx, x);550damage_extent->miny = MIN2(damage_extent->miny, y);551damage_extent->maxx = MAX2(damage_extent->maxx,552MIN2(x + w, res->width0));553damage_extent->maxy = MAX2(damage_extent->maxy,554MIN2(y + h, res->height0));555556if (!pres->damage.tile_map.enable)557continue;558559unsigned t_x_start = x / 32;560unsigned t_x_end = (x + w - 1) / 32;561unsigned t_y_start = y / 32;562unsigned t_y_end = (y + h - 1) / 32;563564for (unsigned t_y = t_y_start; t_y <= t_y_end; t_y++) {565for (unsigned t_x = t_x_start; t_x <= t_x_end; t_x++) {566unsigned b = (t_y * pres->damage.tile_map.stride * 8) + t_x;567568if (BITSET_TEST(pres->damage.tile_map.data, b))569continue;570571BITSET_SET(pres->damage.tile_map.data, b);572enable_count++;573}574}575}576577if (nrects == 0) {578damage_extent->minx = 0;579damage_extent->miny = 0;580damage_extent->maxx = res->width0;581damage_extent->maxy = res->height0;582}583584if (pres->damage.tile_map.enable) {585unsigned t_x_start = damage_extent->minx / 32;586unsigned t_x_end = damage_extent->maxx / 32;587unsigned t_y_start = damage_extent->miny / 32;588unsigned t_y_end = damage_extent->maxy / 32;589unsigned tile_count = (t_x_end - t_x_start + 1) *590(t_y_end - t_y_start + 1);591592/* Don't bother passing a tile-enable-map if the amount of593* tiles to reload is to close to the total number of tiles.594*/595if (tile_count - enable_count < 10)596pres->damage.tile_map.enable = false;597}598599}600601static struct pipe_resource *602panfrost_resource_create_with_modifier(struct pipe_screen *screen,603const struct pipe_resource *template,604uint64_t modifier)605{606struct panfrost_device *dev = pan_device(screen);607608if (dev->ro && (template->bind &609(PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_SCANOUT | PIPE_BIND_SHARED)))610return panfrost_create_scanout_res(screen, template, modifier);611612struct panfrost_resource *so = rzalloc(screen, struct panfrost_resource);613so->base = *template;614so->base.screen = screen;615616pipe_reference_init(&so->base.reference, 1);617618util_range_init(&so->valid_buffer_range);619620panfrost_resource_setup(dev, so, modifier, template->format);621622/* Guess a label based on the bind */623unsigned bind = template->bind;624const char *label =625(bind & PIPE_BIND_INDEX_BUFFER) ? "Index buffer" :626(bind & PIPE_BIND_SCANOUT) ? "Scanout" :627(bind & PIPE_BIND_DISPLAY_TARGET) ? "Display target" :628(bind & PIPE_BIND_SHARED) ? "Shared resource" :629(bind & PIPE_BIND_RENDER_TARGET) ? "Render target" :630(bind & PIPE_BIND_DEPTH_STENCIL) ? "Depth/stencil buffer" :631(bind & PIPE_BIND_SAMPLER_VIEW) ? "Texture" :632(bind & PIPE_BIND_VERTEX_BUFFER) ? "Vertex buffer" :633(bind & PIPE_BIND_CONSTANT_BUFFER) ? "Constant buffer" :634(bind & PIPE_BIND_GLOBAL) ? "Global memory" :635(bind & PIPE_BIND_SHADER_BUFFER) ? "Shader buffer" :636(bind & PIPE_BIND_SHADER_IMAGE) ? "Shader image" :637"Other resource";638639/* We create a BO immediately but don't bother mapping, since we don't640* care to map e.g. FBOs which the CPU probably won't touch */641so->image.data.bo =642panfrost_bo_create(dev, so->image.layout.data_size, PAN_BO_DELAY_MMAP, label);643644if (drm_is_afbc(so->image.layout.modifier))645panfrost_resource_init_afbc_headers(so);646647panfrost_resource_set_damage_region(screen, &so->base, 0, NULL);648649if (template->bind & PIPE_BIND_INDEX_BUFFER)650so->index_cache = rzalloc(so, struct panfrost_minmax_cache);651652return (struct pipe_resource *)so;653}654655/* Default is to create a resource as don't care */656657static struct pipe_resource *658panfrost_resource_create(struct pipe_screen *screen,659const struct pipe_resource *template)660{661return panfrost_resource_create_with_modifier(screen, template,662DRM_FORMAT_MOD_INVALID);663}664665/* If no modifier is specified, we'll choose. Otherwise, the order of666* preference is compressed, tiled, linear. */667668static struct pipe_resource *669panfrost_resource_create_with_modifiers(struct pipe_screen *screen,670const struct pipe_resource *template,671const uint64_t *modifiers, int count)672{673for (unsigned i = 0; i < PAN_MODIFIER_COUNT; ++i) {674if (drm_find_modifier(pan_best_modifiers[i], modifiers, count)) {675return panfrost_resource_create_with_modifier(screen, template,676pan_best_modifiers[i]);677}678}679680/* If we didn't find one, app specified invalid */681assert(count == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID);682return panfrost_resource_create(screen, template);683}684685static void686panfrost_resource_destroy(struct pipe_screen *screen,687struct pipe_resource *pt)688{689struct panfrost_device *dev = pan_device(screen);690struct panfrost_resource *rsrc = (struct panfrost_resource *) pt;691692if (rsrc->scanout)693renderonly_scanout_destroy(rsrc->scanout, dev->ro);694695if (rsrc->image.data.bo)696panfrost_bo_unreference(rsrc->image.data.bo);697698if (rsrc->image.crc.bo)699panfrost_bo_unreference(rsrc->image.crc.bo);700701util_range_destroy(&rsrc->valid_buffer_range);702ralloc_free(rsrc);703}704705/* Most of the time we can do CPU-side transfers, but sometimes we need to use706* the 3D pipe for this. Let's wrap u_blitter to blit to/from staging textures.707* Code adapted from freedreno */708709static struct panfrost_resource *710pan_alloc_staging(struct panfrost_context *ctx, struct panfrost_resource *rsc,711unsigned level, const struct pipe_box *box)712{713struct pipe_context *pctx = &ctx->base;714struct pipe_resource tmpl = rsc->base;715716tmpl.width0 = box->width;717tmpl.height0 = box->height;718/* for array textures, box->depth is the array_size, otherwise719* for 3d textures, it is the depth:720*/721if (tmpl.array_size > 1) {722if (tmpl.target == PIPE_TEXTURE_CUBE)723tmpl.target = PIPE_TEXTURE_2D_ARRAY;724tmpl.array_size = box->depth;725tmpl.depth0 = 1;726} else {727tmpl.array_size = 1;728tmpl.depth0 = box->depth;729}730tmpl.last_level = 0;731tmpl.bind |= PIPE_BIND_LINEAR;732tmpl.bind &= ~(PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_SCANOUT | PIPE_BIND_SHARED);733734struct pipe_resource *pstaging =735pctx->screen->resource_create(pctx->screen, &tmpl);736if (!pstaging)737return NULL;738739return pan_resource(pstaging);740}741742static enum pipe_format743pan_blit_format(enum pipe_format fmt)744{745const struct util_format_description *desc;746desc = util_format_description(fmt);747748/* This must be an emulated format (using u_transfer_helper) as if it749* was real RGTC we wouldn't have used AFBC and needed a blit. */750if (desc->layout == UTIL_FORMAT_LAYOUT_RGTC)751fmt = PIPE_FORMAT_R8G8B8A8_UNORM;752753return fmt;754}755756static void757pan_blit_from_staging(struct pipe_context *pctx, struct panfrost_transfer *trans)758{759struct pipe_resource *dst = trans->base.resource;760struct pipe_blit_info blit = {0};761762blit.dst.resource = dst;763blit.dst.format = pan_blit_format(dst->format);764blit.dst.level = trans->base.level;765blit.dst.box = trans->base.box;766blit.src.resource = trans->staging.rsrc;767blit.src.format = pan_blit_format(trans->staging.rsrc->format);768blit.src.level = 0;769blit.src.box = trans->staging.box;770blit.mask = util_format_get_mask(blit.src.format);771blit.filter = PIPE_TEX_FILTER_NEAREST;772773panfrost_blit(pctx, &blit);774}775776static void777pan_blit_to_staging(struct pipe_context *pctx, struct panfrost_transfer *trans)778{779struct pipe_resource *src = trans->base.resource;780struct pipe_blit_info blit = {0};781782blit.src.resource = src;783blit.src.format = pan_blit_format(src->format);784blit.src.level = trans->base.level;785blit.src.box = trans->base.box;786blit.dst.resource = trans->staging.rsrc;787blit.dst.format = pan_blit_format(trans->staging.rsrc->format);788blit.dst.level = 0;789blit.dst.box = trans->staging.box;790blit.mask = util_format_get_mask(blit.dst.format);791blit.filter = PIPE_TEX_FILTER_NEAREST;792793panfrost_blit(pctx, &blit);794}795796static void *797panfrost_ptr_map(struct pipe_context *pctx,798struct pipe_resource *resource,799unsigned level,800unsigned usage, /* a combination of PIPE_MAP_x */801const struct pipe_box *box,802struct pipe_transfer **out_transfer)803{804struct panfrost_context *ctx = pan_context(pctx);805struct panfrost_device *dev = pan_device(pctx->screen);806struct panfrost_resource *rsrc = pan_resource(resource);807int bytes_per_pixel = util_format_get_blocksize(rsrc->image.layout.format);808struct panfrost_bo *bo = rsrc->image.data.bo;809810/* Can't map tiled/compressed directly */811if ((usage & PIPE_MAP_DIRECTLY) && rsrc->image.layout.modifier != DRM_FORMAT_MOD_LINEAR)812return NULL;813814struct panfrost_transfer *transfer = rzalloc(pctx, struct panfrost_transfer);815transfer->base.level = level;816transfer->base.usage = usage;817transfer->base.box = *box;818819pipe_resource_reference(&transfer->base.resource, resource);820*out_transfer = &transfer->base;821822/* We don't have s/w routines for AFBC, so use a staging texture */823if (drm_is_afbc(rsrc->image.layout.modifier)) {824struct panfrost_resource *staging = pan_alloc_staging(ctx, rsrc, level, box);825assert(staging);826827/* Staging resources have one LOD: level 0. Query the strides828* on this LOD.829*/830transfer->base.stride = staging->image.layout.slices[0].line_stride;831transfer->base.layer_stride =832panfrost_get_layer_stride(&staging->image.layout, 0);833834transfer->staging.rsrc = &staging->base;835836transfer->staging.box = *box;837transfer->staging.box.x = 0;838transfer->staging.box.y = 0;839transfer->staging.box.z = 0;840841assert(transfer->staging.rsrc != NULL);842843bool valid = BITSET_TEST(rsrc->valid.data, level);844845if ((usage & PIPE_MAP_READ) && (valid || rsrc->track.writer)) {846pan_blit_to_staging(pctx, transfer);847panfrost_flush_writer(ctx, staging);848panfrost_bo_wait(staging->image.data.bo, INT64_MAX, false);849}850851panfrost_bo_mmap(staging->image.data.bo);852return staging->image.data.bo->ptr.cpu;853}854855/* If we haven't already mmaped, now's the time */856panfrost_bo_mmap(bo);857858if (dev->debug & (PAN_DBG_TRACE | PAN_DBG_SYNC))859pandecode_inject_mmap(bo->ptr.gpu, bo->ptr.cpu, bo->size, NULL);860861bool create_new_bo = usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE;862bool copy_resource = false;863864if (!create_new_bo &&865!(usage & PIPE_MAP_UNSYNCHRONIZED) &&866(usage & PIPE_MAP_WRITE) &&867!(resource->target == PIPE_BUFFER868&& !util_ranges_intersect(&rsrc->valid_buffer_range, box->x, box->x + box->width)) &&869BITSET_COUNT(rsrc->track.users) != 0) {870871/* When a resource to be modified is already being used by a872* pending batch, it is often faster to copy the whole BO than873* to flush and split the frame in two.874*/875876panfrost_flush_writer(ctx, rsrc);877panfrost_bo_wait(bo, INT64_MAX, false);878879create_new_bo = true;880copy_resource = true;881}882883if (create_new_bo) {884/* If the BO is used by one of the pending batches or if it's885* not ready yet (still accessed by one of the already flushed886* batches), we try to allocate a new one to avoid waiting.887*/888if (BITSET_COUNT(rsrc->track.users) ||889!panfrost_bo_wait(bo, 0, true)) {890/* We want the BO to be MMAPed. */891uint32_t flags = bo->flags & ~PAN_BO_DELAY_MMAP;892struct panfrost_bo *newbo = NULL;893894/* When the BO has been imported/exported, we can't895* replace it by another one, otherwise the896* importer/exporter wouldn't see the change we're897* doing to it.898*/899if (!(bo->flags & PAN_BO_SHARED))900newbo = panfrost_bo_create(dev, bo->size,901flags, bo->label);902903if (newbo) {904if (copy_resource)905memcpy(newbo->ptr.cpu, rsrc->image.data.bo->ptr.cpu, bo->size);906907panfrost_bo_unreference(bo);908rsrc->image.data.bo = newbo;909910if (!copy_resource &&911drm_is_afbc(rsrc->image.layout.modifier))912panfrost_resource_init_afbc_headers(rsrc);913914bo = newbo;915} else {916/* Allocation failed or was impossible, let's917* fall back on a flush+wait.918*/919panfrost_flush_batches_accessing_rsrc(ctx, rsrc);920panfrost_bo_wait(bo, INT64_MAX, true);921}922}923} else if ((usage & PIPE_MAP_WRITE)924&& resource->target == PIPE_BUFFER925&& !util_ranges_intersect(&rsrc->valid_buffer_range, box->x, box->x + box->width)) {926/* No flush for writes to uninitialized */927} else if (!(usage & PIPE_MAP_UNSYNCHRONIZED)) {928if (usage & PIPE_MAP_WRITE) {929panfrost_flush_batches_accessing_rsrc(ctx, rsrc);930panfrost_bo_wait(bo, INT64_MAX, true);931} else if (usage & PIPE_MAP_READ) {932panfrost_flush_writer(ctx, rsrc);933panfrost_bo_wait(bo, INT64_MAX, false);934}935}936937if (rsrc->image.layout.modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {938transfer->base.stride = box->width * bytes_per_pixel;939transfer->base.layer_stride = transfer->base.stride * box->height;940transfer->map = ralloc_size(transfer, transfer->base.layer_stride * box->depth);941assert(box->depth == 1);942943if ((usage & PIPE_MAP_READ) && BITSET_TEST(rsrc->valid.data, level)) {944panfrost_load_tiled_image(945transfer->map,946bo->ptr.cpu + rsrc->image.layout.slices[level].offset,947box->x, box->y, box->width, box->height,948transfer->base.stride,949rsrc->image.layout.slices[level].line_stride,950rsrc->image.layout.format);951}952953return transfer->map;954} else {955assert (rsrc->image.layout.modifier == DRM_FORMAT_MOD_LINEAR);956957/* Direct, persistent writes create holes in time for958* caching... I don't know if this is actually possible but we959* should still get it right */960961unsigned dpw = PIPE_MAP_DIRECTLY | PIPE_MAP_WRITE | PIPE_MAP_PERSISTENT;962963if ((usage & dpw) == dpw && rsrc->index_cache)964return NULL;965966transfer->base.stride = rsrc->image.layout.slices[level].line_stride;967transfer->base.layer_stride =968panfrost_get_layer_stride(&rsrc->image.layout, level);969970/* By mapping direct-write, we're implicitly already971* initialized (maybe), so be conservative */972973if (usage & PIPE_MAP_WRITE) {974BITSET_SET(rsrc->valid.data, level);975panfrost_minmax_cache_invalidate(rsrc->index_cache, &transfer->base);976}977978return bo->ptr.cpu979+ rsrc->image.layout.slices[level].offset980+ transfer->base.box.z * transfer->base.layer_stride981+ transfer->base.box.y * rsrc->image.layout.slices[level].line_stride982+ transfer->base.box.x * bytes_per_pixel;983}984}985986void987pan_resource_modifier_convert(struct panfrost_context *ctx,988struct panfrost_resource *rsrc,989uint64_t modifier)990{991assert(!rsrc->modifier_constant);992993struct pipe_resource *tmp_prsrc =994panfrost_resource_create_with_modifier(995ctx->base.screen, &rsrc->base, modifier);996struct panfrost_resource *tmp_rsrc = pan_resource(tmp_prsrc);997enum pipe_format blit_fmt = pan_blit_format(tmp_rsrc->base.format);998999unsigned depth = rsrc->base.target == PIPE_TEXTURE_3D ?1000rsrc->base.depth0 : rsrc->base.array_size;10011002struct pipe_box box =1003{ 0, 0, 0, rsrc->base.width0, rsrc->base.height0, depth };10041005struct pipe_blit_info blit = {1006.dst.resource = &tmp_rsrc->base,1007.dst.format = blit_fmt,1008.dst.box = box,1009.src.resource = &rsrc->base,1010.src.format = pan_blit_format(rsrc->base.format),1011.src.box = box,1012.mask = util_format_get_mask(blit_fmt),1013.filter = PIPE_TEX_FILTER_NEAREST1014};10151016for (int i = 0; i <= rsrc->base.last_level; i++) {1017if (BITSET_TEST(rsrc->valid.data, i)) {1018blit.dst.level = blit.src.level = i;1019panfrost_blit(&ctx->base, &blit);1020}1021}10221023panfrost_bo_unreference(rsrc->image.data.bo);1024if (rsrc->image.crc.bo)1025panfrost_bo_unreference(rsrc->image.crc.bo);10261027rsrc->image.data.bo = tmp_rsrc->image.data.bo;1028panfrost_bo_reference(rsrc->image.data.bo);10291030panfrost_resource_setup(pan_device(ctx->base.screen), rsrc, modifier,1031blit.dst.format);1032pipe_resource_reference(&tmp_prsrc, NULL);1033}10341035static bool1036panfrost_should_linear_convert(struct panfrost_resource *prsrc,1037struct pipe_transfer *transfer)1038{1039if (prsrc->modifier_constant)1040return false;10411042/* Overwriting the entire resource indicates streaming, for which1043* linear layout is most efficient due to the lack of expensive1044* conversion.1045*1046* For now we just switch to linear after a number of complete1047* overwrites to keep things simple, but we could do better.1048*/10491050unsigned depth = prsrc->base.target == PIPE_TEXTURE_3D ?1051prsrc->base.depth0 : prsrc->base.array_size;1052bool entire_overwrite =1053prsrc->base.last_level == 0 &&1054transfer->box.width == prsrc->base.width0 &&1055transfer->box.height == prsrc->base.height0 &&1056transfer->box.depth == depth &&1057transfer->box.x == 0 &&1058transfer->box.y == 0 &&1059transfer->box.z == 0;10601061if (entire_overwrite)1062++prsrc->modifier_updates;10631064return prsrc->modifier_updates >= LAYOUT_CONVERT_THRESHOLD;1065}10661067static void1068panfrost_ptr_unmap(struct pipe_context *pctx,1069struct pipe_transfer *transfer)1070{1071/* Gallium expects writeback here, so we tile */10721073struct panfrost_transfer *trans = pan_transfer(transfer);1074struct panfrost_resource *prsrc = (struct panfrost_resource *) transfer->resource;1075struct panfrost_device *dev = pan_device(pctx->screen);10761077if (transfer->usage & PIPE_MAP_WRITE)1078prsrc->valid.crc = false;10791080/* AFBC will use a staging resource. `initialized` will be set when the1081* fragment job is created; this is deferred to prevent useless surface1082* reloads that can cascade into DATA_INVALID_FAULTs due to reading1083* malformed AFBC data if uninitialized */10841085if (trans->staging.rsrc) {1086if (transfer->usage & PIPE_MAP_WRITE) {1087if (panfrost_should_linear_convert(prsrc, transfer)) {10881089panfrost_bo_unreference(prsrc->image.data.bo);1090if (prsrc->image.crc.bo)1091panfrost_bo_unreference(prsrc->image.crc.bo);10921093panfrost_resource_setup(dev, prsrc, DRM_FORMAT_MOD_LINEAR,1094prsrc->image.layout.format);10951096prsrc->image.data.bo = pan_resource(trans->staging.rsrc)->image.data.bo;1097panfrost_bo_reference(prsrc->image.data.bo);1098} else {1099pan_blit_from_staging(pctx, trans);1100panfrost_flush_batches_accessing_rsrc(pan_context(pctx), pan_resource(trans->staging.rsrc));1101}1102}11031104pipe_resource_reference(&trans->staging.rsrc, NULL);1105}11061107/* Tiling will occur in software from a staging cpu buffer */1108if (trans->map) {1109struct panfrost_bo *bo = prsrc->image.data.bo;11101111if (transfer->usage & PIPE_MAP_WRITE) {1112BITSET_SET(prsrc->valid.data, transfer->level);11131114if (prsrc->image.layout.modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) {1115assert(transfer->box.depth == 1);11161117if (panfrost_should_linear_convert(prsrc, transfer)) {1118panfrost_resource_setup(dev, prsrc, DRM_FORMAT_MOD_LINEAR,1119prsrc->image.layout.format);1120if (prsrc->image.layout.data_size > bo->size) {1121const char *label = bo->label;1122panfrost_bo_unreference(bo);1123bo = prsrc->image.data.bo =1124panfrost_bo_create(dev, prsrc->image.layout.data_size, 0, label);1125assert(bo);1126}11271128util_copy_rect(1129bo->ptr.cpu + prsrc->image.layout.slices[0].offset,1130prsrc->base.format,1131prsrc->image.layout.slices[0].line_stride,11320, 0,1133transfer->box.width,1134transfer->box.height,1135trans->map,1136transfer->stride,11370, 0);1138} else {1139panfrost_store_tiled_image(1140bo->ptr.cpu + prsrc->image.layout.slices[transfer->level].offset,1141trans->map,1142transfer->box.x, transfer->box.y,1143transfer->box.width, transfer->box.height,1144prsrc->image.layout.slices[transfer->level].line_stride,1145transfer->stride,1146prsrc->image.layout.format);1147}1148}1149}1150}115111521153util_range_add(&prsrc->base, &prsrc->valid_buffer_range,1154transfer->box.x,1155transfer->box.x + transfer->box.width);11561157panfrost_minmax_cache_invalidate(prsrc->index_cache, transfer);11581159/* Derefence the resource */1160pipe_resource_reference(&transfer->resource, NULL);11611162/* Transfer itself is RALLOCed at the moment */1163ralloc_free(transfer);1164}11651166static void1167panfrost_ptr_flush_region(struct pipe_context *pctx,1168struct pipe_transfer *transfer,1169const struct pipe_box *box)1170{1171struct panfrost_resource *rsc = pan_resource(transfer->resource);11721173if (transfer->resource->target == PIPE_BUFFER) {1174util_range_add(&rsc->base, &rsc->valid_buffer_range,1175transfer->box.x + box->x,1176transfer->box.x + box->x + box->width);1177} else {1178BITSET_SET(rsc->valid.data, transfer->level);1179}1180}11811182static void1183panfrost_invalidate_resource(struct pipe_context *pctx, struct pipe_resource *prsrc)1184{1185struct panfrost_context *ctx = pan_context(pctx);1186struct panfrost_batch *batch = panfrost_get_batch_for_fbo(ctx);11871188/* Handle the glInvalidateFramebuffer case */1189if (batch->key.zsbuf && batch->key.zsbuf->texture == prsrc)1190batch->resolve &= ~PIPE_CLEAR_DEPTHSTENCIL;11911192for (unsigned i = 0; i < batch->key.nr_cbufs; ++i) {1193struct pipe_surface *surf = batch->key.cbufs[i];11941195if (surf && surf->texture == prsrc)1196batch->resolve &= ~(PIPE_CLEAR_COLOR0 << i);1197}1198}11991200static enum pipe_format1201panfrost_resource_get_internal_format(struct pipe_resource *rsrc)1202{1203struct panfrost_resource *prsrc = (struct panfrost_resource *) rsrc;1204return prsrc->image.layout.format;1205}12061207static bool1208panfrost_generate_mipmap(1209struct pipe_context *pctx,1210struct pipe_resource *prsrc,1211enum pipe_format format,1212unsigned base_level,1213unsigned last_level,1214unsigned first_layer,1215unsigned last_layer)1216{1217struct panfrost_resource *rsrc = pan_resource(prsrc);12181219/* Generating a mipmap invalidates the written levels, so make that1220* explicit so we don't try to wallpaper them back and end up with1221* u_blitter recursion */12221223assert(rsrc->image.data.bo);1224for (unsigned l = base_level + 1; l <= last_level; ++l)1225BITSET_CLEAR(rsrc->valid.data, l);12261227/* Beyond that, we just delegate the hard stuff. */12281229bool blit_res = util_gen_mipmap(1230pctx, prsrc, format,1231base_level, last_level,1232first_layer, last_layer,1233PIPE_TEX_FILTER_LINEAR);12341235return blit_res;1236}12371238/* Computes the address to a texture at a particular slice */12391240mali_ptr1241panfrost_get_texture_address(struct panfrost_resource *rsrc,1242unsigned level, unsigned layer,1243unsigned sample)1244{1245bool is_3d = rsrc->base.target == PIPE_TEXTURE_3D;1246unsigned array_idx = is_3d ? 0 : layer;1247unsigned surface_idx = is_3d ? layer : sample;1248return rsrc->image.data.bo->ptr.gpu +1249panfrost_texture_offset(&rsrc->image.layout, level,1250array_idx, surface_idx);1251}12521253void1254panfrost_get_afbc_pointers(struct panfrost_resource *rsrc,1255unsigned level, unsigned layer,1256mali_ptr *header, mali_ptr *body)1257{1258assert(drm_is_afbc(rsrc->image.layout.modifier));12591260struct pan_image_slice_layout *slice = &rsrc->image.layout.slices[level];12611262if (rsrc->base.target == PIPE_TEXTURE_3D) {1263*header = rsrc->image.data.bo->ptr.gpu + slice->offset +1264(layer * slice->afbc.surface_stride);1265*body = rsrc->image.data.bo->ptr.gpu + slice->offset +1266slice->afbc.header_size +1267(slice->surface_stride * layer);1268} else {1269*header = rsrc->image.data.bo->ptr.gpu +1270panfrost_texture_offset(&rsrc->image.layout,1271level, layer, 0);1272*body = *header + slice->afbc.header_size;1273}1274}12751276static void1277panfrost_resource_set_stencil(struct pipe_resource *prsrc,1278struct pipe_resource *stencil)1279{1280pan_resource(prsrc)->separate_stencil = pan_resource(stencil);1281}12821283static struct pipe_resource *1284panfrost_resource_get_stencil(struct pipe_resource *prsrc)1285{1286if (!pan_resource(prsrc)->separate_stencil)1287return NULL;12881289return &pan_resource(prsrc)->separate_stencil->base;1290}12911292static const struct u_transfer_vtbl transfer_vtbl = {1293.resource_create = panfrost_resource_create,1294.resource_destroy = panfrost_resource_destroy,1295.transfer_map = panfrost_ptr_map,1296.transfer_unmap = panfrost_ptr_unmap,1297.transfer_flush_region = panfrost_ptr_flush_region,1298.get_internal_format = panfrost_resource_get_internal_format,1299.set_stencil = panfrost_resource_set_stencil,1300.get_stencil = panfrost_resource_get_stencil,1301};13021303void1304panfrost_resource_screen_init(struct pipe_screen *pscreen)1305{1306struct panfrost_device *dev = pan_device(pscreen);13071308bool fake_rgtc = !panfrost_supports_compressed_format(dev, MALI_BC4_UNORM);13091310pscreen->resource_create_with_modifiers =1311panfrost_resource_create_with_modifiers;1312pscreen->resource_create = u_transfer_helper_resource_create;1313pscreen->resource_destroy = u_transfer_helper_resource_destroy;1314pscreen->resource_from_handle = panfrost_resource_from_handle;1315pscreen->resource_get_handle = panfrost_resource_get_handle;1316pscreen->transfer_helper = u_transfer_helper_create(&transfer_vtbl,1317true, false,1318fake_rgtc, true);1319}13201321void1322panfrost_resource_context_init(struct pipe_context *pctx)1323{1324pctx->buffer_map = u_transfer_helper_transfer_map;1325pctx->buffer_unmap = u_transfer_helper_transfer_unmap;1326pctx->texture_map = u_transfer_helper_transfer_map;1327pctx->texture_unmap = u_transfer_helper_transfer_unmap;1328pctx->create_surface = panfrost_create_surface;1329pctx->surface_destroy = panfrost_surface_destroy;1330pctx->resource_copy_region = util_resource_copy_region;1331pctx->blit = panfrost_blit;1332pctx->generate_mipmap = panfrost_generate_mipmap;1333pctx->flush_resource = panfrost_flush_resource;1334pctx->invalidate_resource = panfrost_invalidate_resource;1335pctx->transfer_flush_region = u_transfer_helper_transfer_flush_region;1336pctx->buffer_subdata = u_default_buffer_subdata;1337pctx->texture_subdata = u_default_texture_subdata;1338}133913401341