1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright (c) 2012-2015 Etnaviv Project 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sub license, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the 12bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 13bf215546Sopenharmony_ci * of the Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21bf215546Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 22bf215546Sopenharmony_ci * 23bf215546Sopenharmony_ci * Authors: 24bf215546Sopenharmony_ci * Wladimir J. van der Laan <laanwj@gmail.com> 25bf215546Sopenharmony_ci */ 26bf215546Sopenharmony_ci 27bf215546Sopenharmony_ci#include "etnaviv_transfer.h" 28bf215546Sopenharmony_ci#include "etnaviv_clear_blit.h" 29bf215546Sopenharmony_ci#include "etnaviv_context.h" 30bf215546Sopenharmony_ci#include "etnaviv_debug.h" 31bf215546Sopenharmony_ci#include "etnaviv_etc2.h" 32bf215546Sopenharmony_ci#include "etnaviv_screen.h" 33bf215546Sopenharmony_ci 34bf215546Sopenharmony_ci#include "pipe/p_defines.h" 35bf215546Sopenharmony_ci#include "pipe/p_format.h" 36bf215546Sopenharmony_ci#include "pipe/p_screen.h" 37bf215546Sopenharmony_ci#include "pipe/p_state.h" 38bf215546Sopenharmony_ci#include "util/format/u_format.h" 39bf215546Sopenharmony_ci#include "util/u_inlines.h" 40bf215546Sopenharmony_ci#include "util/u_memory.h" 41bf215546Sopenharmony_ci#include "util/u_surface.h" 42bf215546Sopenharmony_ci#include "util/u_transfer.h" 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_ci#include "hw/common_3d.xml.h" 45bf215546Sopenharmony_ci 46bf215546Sopenharmony_ci#include "drm-uapi/drm_fourcc.h" 47bf215546Sopenharmony_ci 48bf215546Sopenharmony_ci/* Compute offset into a 1D/2D/3D buffer of a certain box. 49bf215546Sopenharmony_ci * This box must be aligned to the block width and height of the 50bf215546Sopenharmony_ci * underlying format. */ 51bf215546Sopenharmony_cistatic inline size_t 52bf215546Sopenharmony_cietna_compute_offset(enum pipe_format format, const struct pipe_box *box, 53bf215546Sopenharmony_ci size_t stride, size_t layer_stride) 54bf215546Sopenharmony_ci{ 55bf215546Sopenharmony_ci return box->z * layer_stride + 56bf215546Sopenharmony_ci box->y / util_format_get_blockheight(format) * stride + 57bf215546Sopenharmony_ci box->x / util_format_get_blockwidth(format) * 58bf215546Sopenharmony_ci util_format_get_blocksize(format); 59bf215546Sopenharmony_ci} 60bf215546Sopenharmony_ci 61bf215546Sopenharmony_cistatic void etna_patch_data(void *buffer, const struct pipe_transfer *ptrans) 62bf215546Sopenharmony_ci{ 63bf215546Sopenharmony_ci struct pipe_resource *prsc = ptrans->resource; 64bf215546Sopenharmony_ci struct etna_resource *rsc = etna_resource(prsc); 65bf215546Sopenharmony_ci struct etna_resource_level *level = &rsc->levels[ptrans->level]; 66bf215546Sopenharmony_ci 67bf215546Sopenharmony_ci if (likely(!etna_etc2_needs_patching(prsc))) 68bf215546Sopenharmony_ci return; 69bf215546Sopenharmony_ci 70bf215546Sopenharmony_ci if (level->patched) 71bf215546Sopenharmony_ci return; 72bf215546Sopenharmony_ci 73bf215546Sopenharmony_ci /* do have the offsets of blocks to patch? */ 74bf215546Sopenharmony_ci if (!level->patch_offsets) { 75bf215546Sopenharmony_ci level->patch_offsets = CALLOC_STRUCT(util_dynarray); 76bf215546Sopenharmony_ci 77bf215546Sopenharmony_ci etna_etc2_calculate_blocks(buffer, ptrans->stride, 78bf215546Sopenharmony_ci ptrans->box.width, ptrans->box.height, 79bf215546Sopenharmony_ci prsc->format, level->patch_offsets); 80bf215546Sopenharmony_ci } 81bf215546Sopenharmony_ci 82bf215546Sopenharmony_ci etna_etc2_patch(buffer, level->patch_offsets); 83bf215546Sopenharmony_ci 84bf215546Sopenharmony_ci level->patched = true; 85bf215546Sopenharmony_ci} 86bf215546Sopenharmony_ci 87bf215546Sopenharmony_cistatic void etna_unpatch_data(void *buffer, const struct pipe_transfer *ptrans) 88bf215546Sopenharmony_ci{ 89bf215546Sopenharmony_ci struct pipe_resource *prsc = ptrans->resource; 90bf215546Sopenharmony_ci struct etna_resource *rsc = etna_resource(prsc); 91bf215546Sopenharmony_ci struct etna_resource_level *level = &rsc->levels[ptrans->level]; 92bf215546Sopenharmony_ci 93bf215546Sopenharmony_ci if (!level->patched) 94bf215546Sopenharmony_ci return; 95bf215546Sopenharmony_ci 96bf215546Sopenharmony_ci etna_etc2_patch(buffer, level->patch_offsets); 97bf215546Sopenharmony_ci 98bf215546Sopenharmony_ci level->patched = false; 99bf215546Sopenharmony_ci} 100bf215546Sopenharmony_ci 101bf215546Sopenharmony_cistatic void 102bf215546Sopenharmony_cietna_transfer_unmap(struct pipe_context *pctx, struct pipe_transfer *ptrans) 103bf215546Sopenharmony_ci{ 104bf215546Sopenharmony_ci struct etna_context *ctx = etna_context(pctx); 105bf215546Sopenharmony_ci struct etna_transfer *trans = etna_transfer(ptrans); 106bf215546Sopenharmony_ci struct etna_resource *rsc = etna_resource(ptrans->resource); 107bf215546Sopenharmony_ci 108bf215546Sopenharmony_ci /* XXX 109bf215546Sopenharmony_ci * When writing to a resource that is already in use, replace the resource 110bf215546Sopenharmony_ci * with a completely new buffer 111bf215546Sopenharmony_ci * and free the old one using a fenced free. 112bf215546Sopenharmony_ci * The most tricky case to implement will be: tiled or supertiled surface, 113bf215546Sopenharmony_ci * partial write, target not aligned to 4/64. */ 114bf215546Sopenharmony_ci assert(ptrans->level <= rsc->base.last_level); 115bf215546Sopenharmony_ci 116bf215546Sopenharmony_ci if (rsc->texture && !etna_resource_newer(rsc, etna_resource(rsc->texture))) 117bf215546Sopenharmony_ci rsc = etna_resource(rsc->texture); /* switch to using the texture resource */ 118bf215546Sopenharmony_ci 119bf215546Sopenharmony_ci /* 120bf215546Sopenharmony_ci * Temporary resources are always pulled into the CPU domain, must push them 121bf215546Sopenharmony_ci * back into GPU domain before the RS execs the blit to the base resource. 122bf215546Sopenharmony_ci */ 123bf215546Sopenharmony_ci if (trans->rsc) 124bf215546Sopenharmony_ci etna_bo_cpu_fini(etna_resource(trans->rsc)->bo); 125bf215546Sopenharmony_ci 126bf215546Sopenharmony_ci if (ptrans->usage & PIPE_MAP_WRITE) { 127bf215546Sopenharmony_ci if (trans->rsc) { 128bf215546Sopenharmony_ci /* We have a temporary resource due to either tile status or 129bf215546Sopenharmony_ci * tiling format. Write back the updated buffer contents. 130bf215546Sopenharmony_ci * FIXME: we need to invalidate the tile status. */ 131bf215546Sopenharmony_ci etna_copy_resource_box(pctx, ptrans->resource, trans->rsc, ptrans->level, &ptrans->box); 132bf215546Sopenharmony_ci } else if (trans->staging) { 133bf215546Sopenharmony_ci /* map buffer object */ 134bf215546Sopenharmony_ci struct etna_resource_level *res_level = &rsc->levels[ptrans->level]; 135bf215546Sopenharmony_ci 136bf215546Sopenharmony_ci if (rsc->layout == ETNA_LAYOUT_TILED) { 137bf215546Sopenharmony_ci for (unsigned z = 0; z < ptrans->box.depth; z++) { 138bf215546Sopenharmony_ci etna_texture_tile( 139bf215546Sopenharmony_ci trans->mapped + (ptrans->box.z + z) * res_level->layer_stride, 140bf215546Sopenharmony_ci trans->staging + z * ptrans->layer_stride, 141bf215546Sopenharmony_ci ptrans->box.x, ptrans->box.y, 142bf215546Sopenharmony_ci res_level->stride, ptrans->box.width, ptrans->box.height, 143bf215546Sopenharmony_ci ptrans->stride, util_format_get_blocksize(rsc->base.format)); 144bf215546Sopenharmony_ci } 145bf215546Sopenharmony_ci } else if (rsc->layout == ETNA_LAYOUT_LINEAR) { 146bf215546Sopenharmony_ci util_copy_box(trans->mapped, rsc->base.format, res_level->stride, 147bf215546Sopenharmony_ci res_level->layer_stride, ptrans->box.x, 148bf215546Sopenharmony_ci ptrans->box.y, ptrans->box.z, ptrans->box.width, 149bf215546Sopenharmony_ci ptrans->box.height, ptrans->box.depth, 150bf215546Sopenharmony_ci trans->staging, ptrans->stride, 151bf215546Sopenharmony_ci ptrans->layer_stride, 0, 0, 0 /* src x,y,z */); 152bf215546Sopenharmony_ci } else { 153bf215546Sopenharmony_ci BUG("unsupported tiling %i", rsc->layout); 154bf215546Sopenharmony_ci } 155bf215546Sopenharmony_ci 156bf215546Sopenharmony_ci FREE(trans->staging); 157bf215546Sopenharmony_ci } 158bf215546Sopenharmony_ci 159bf215546Sopenharmony_ci rsc->seqno++; 160bf215546Sopenharmony_ci 161bf215546Sopenharmony_ci if (rsc->base.bind & PIPE_BIND_SAMPLER_VIEW) { 162bf215546Sopenharmony_ci ctx->dirty |= ETNA_DIRTY_TEXTURE_CACHES; 163bf215546Sopenharmony_ci } 164bf215546Sopenharmony_ci } 165bf215546Sopenharmony_ci 166bf215546Sopenharmony_ci /* We need to have the patched data ready for the GPU. */ 167bf215546Sopenharmony_ci etna_patch_data(trans->mapped, ptrans); 168bf215546Sopenharmony_ci 169bf215546Sopenharmony_ci /* 170bf215546Sopenharmony_ci * Transfers without a temporary are only pulled into the CPU domain if they 171bf215546Sopenharmony_ci * are not mapped unsynchronized. If they are, must push them back into GPU 172bf215546Sopenharmony_ci * domain after CPU access is finished. 173bf215546Sopenharmony_ci */ 174bf215546Sopenharmony_ci if (!trans->rsc && !(ptrans->usage & PIPE_MAP_UNSYNCHRONIZED)) 175bf215546Sopenharmony_ci etna_bo_cpu_fini(rsc->bo); 176bf215546Sopenharmony_ci 177bf215546Sopenharmony_ci if ((ptrans->resource->target == PIPE_BUFFER) && 178bf215546Sopenharmony_ci (ptrans->usage & PIPE_MAP_WRITE)) { 179bf215546Sopenharmony_ci util_range_add(&rsc->base, 180bf215546Sopenharmony_ci &rsc->valid_buffer_range, 181bf215546Sopenharmony_ci ptrans->box.x, 182bf215546Sopenharmony_ci ptrans->box.x + ptrans->box.width); 183bf215546Sopenharmony_ci } 184bf215546Sopenharmony_ci 185bf215546Sopenharmony_ci pipe_resource_reference(&trans->rsc, NULL); 186bf215546Sopenharmony_ci pipe_resource_reference(&ptrans->resource, NULL); 187bf215546Sopenharmony_ci slab_free(&ctx->transfer_pool, trans); 188bf215546Sopenharmony_ci} 189bf215546Sopenharmony_ci 190bf215546Sopenharmony_cistatic void * 191bf215546Sopenharmony_cietna_transfer_map(struct pipe_context *pctx, struct pipe_resource *prsc, 192bf215546Sopenharmony_ci unsigned level, 193bf215546Sopenharmony_ci unsigned usage, 194bf215546Sopenharmony_ci const struct pipe_box *box, 195bf215546Sopenharmony_ci struct pipe_transfer **out_transfer) 196bf215546Sopenharmony_ci{ 197bf215546Sopenharmony_ci struct etna_context *ctx = etna_context(pctx); 198bf215546Sopenharmony_ci struct etna_screen *screen = ctx->screen; 199bf215546Sopenharmony_ci struct etna_resource *rsc = etna_resource(prsc); 200bf215546Sopenharmony_ci struct etna_transfer *trans; 201bf215546Sopenharmony_ci struct pipe_transfer *ptrans; 202bf215546Sopenharmony_ci enum pipe_format format = prsc->format; 203bf215546Sopenharmony_ci 204bf215546Sopenharmony_ci trans = slab_zalloc(&ctx->transfer_pool); 205bf215546Sopenharmony_ci if (!trans) 206bf215546Sopenharmony_ci return NULL; 207bf215546Sopenharmony_ci 208bf215546Sopenharmony_ci /* 209bf215546Sopenharmony_ci * Upgrade to UNSYNCHRONIZED if target is PIPE_BUFFER and range is uninitialized. 210bf215546Sopenharmony_ci */ 211bf215546Sopenharmony_ci if ((usage & PIPE_MAP_WRITE) && 212bf215546Sopenharmony_ci (prsc->target == PIPE_BUFFER) && 213bf215546Sopenharmony_ci !util_ranges_intersect(&rsc->valid_buffer_range, 214bf215546Sopenharmony_ci box->x, 215bf215546Sopenharmony_ci box->x + box->width)) { 216bf215546Sopenharmony_ci usage |= PIPE_MAP_UNSYNCHRONIZED; 217bf215546Sopenharmony_ci } 218bf215546Sopenharmony_ci 219bf215546Sopenharmony_ci /* Upgrade DISCARD_RANGE to WHOLE_RESOURCE if the whole resource is 220bf215546Sopenharmony_ci * being mapped. If we add buffer reallocation to avoid CPU/GPU sync this 221bf215546Sopenharmony_ci * check needs to be extended to coherent mappings and shared resources. 222bf215546Sopenharmony_ci */ 223bf215546Sopenharmony_ci if ((usage & PIPE_MAP_DISCARD_RANGE) && 224bf215546Sopenharmony_ci !(usage & PIPE_MAP_UNSYNCHRONIZED) && 225bf215546Sopenharmony_ci prsc->last_level == 0 && 226bf215546Sopenharmony_ci prsc->width0 == box->width && 227bf215546Sopenharmony_ci prsc->height0 == box->height && 228bf215546Sopenharmony_ci prsc->depth0 == box->depth && 229bf215546Sopenharmony_ci prsc->array_size == 1) { 230bf215546Sopenharmony_ci usage |= PIPE_MAP_DISCARD_WHOLE_RESOURCE; 231bf215546Sopenharmony_ci } 232bf215546Sopenharmony_ci 233bf215546Sopenharmony_ci ptrans = &trans->base; 234bf215546Sopenharmony_ci pipe_resource_reference(&ptrans->resource, prsc); 235bf215546Sopenharmony_ci ptrans->level = level; 236bf215546Sopenharmony_ci ptrans->usage = usage; 237bf215546Sopenharmony_ci ptrans->box = *box; 238bf215546Sopenharmony_ci 239bf215546Sopenharmony_ci assert(level <= prsc->last_level); 240bf215546Sopenharmony_ci 241bf215546Sopenharmony_ci /* This one is a little tricky: if we have a separate render resource, which 242bf215546Sopenharmony_ci * is newer than the base resource we want the transfer to target this one, 243bf215546Sopenharmony_ci * to get the most up-to-date content, but only if we don't have a texture 244bf215546Sopenharmony_ci * target of the same age, as transfering in/out of the texture target is 245bf215546Sopenharmony_ci * generally preferred for the reasons listed below */ 246bf215546Sopenharmony_ci if (rsc->render && etna_resource_newer(etna_resource(rsc->render), rsc) && 247bf215546Sopenharmony_ci (!rsc->texture || etna_resource_newer(etna_resource(rsc->render), 248bf215546Sopenharmony_ci etna_resource(rsc->texture)))) { 249bf215546Sopenharmony_ci rsc = etna_resource(rsc->render); 250bf215546Sopenharmony_ci } 251bf215546Sopenharmony_ci 252bf215546Sopenharmony_ci if (rsc->texture && !etna_resource_newer(rsc, etna_resource(rsc->texture))) { 253bf215546Sopenharmony_ci /* We have a texture resource which is the same age or newer than the 254bf215546Sopenharmony_ci * render resource. Use the texture resource, which avoids bouncing 255bf215546Sopenharmony_ci * pixels between the two resources, and we can de-tile it in s/w. */ 256bf215546Sopenharmony_ci rsc = etna_resource(rsc->texture); 257bf215546Sopenharmony_ci } else if (rsc->ts_bo || 258bf215546Sopenharmony_ci (rsc->layout != ETNA_LAYOUT_LINEAR && 259bf215546Sopenharmony_ci etna_resource_hw_tileable(screen->specs.use_blt, prsc) && 260bf215546Sopenharmony_ci /* HALIGN 4 resources are incompatible with the resolve engine, 261bf215546Sopenharmony_ci * so fall back to using software to detile this resource. */ 262bf215546Sopenharmony_ci rsc->halign != TEXTURE_HALIGN_FOUR)) { 263bf215546Sopenharmony_ci /* If the surface has tile status, we need to resolve it first. 264bf215546Sopenharmony_ci * The strategy we implement here is to use the RS to copy the 265bf215546Sopenharmony_ci * depth buffer, filling in the "holes" where the tile status 266bf215546Sopenharmony_ci * indicates that it's clear. We also do this for tiled 267bf215546Sopenharmony_ci * resources, but only if the RS can blit them. */ 268bf215546Sopenharmony_ci if (usage & PIPE_MAP_DIRECTLY) { 269bf215546Sopenharmony_ci slab_free(&ctx->transfer_pool, trans); 270bf215546Sopenharmony_ci BUG("unsupported map flags %#x with tile status/tiled layout", usage); 271bf215546Sopenharmony_ci return NULL; 272bf215546Sopenharmony_ci } 273bf215546Sopenharmony_ci 274bf215546Sopenharmony_ci if (prsc->depth0 > 1 && rsc->ts_bo) { 275bf215546Sopenharmony_ci slab_free(&ctx->transfer_pool, trans); 276bf215546Sopenharmony_ci BUG("resource has depth >1 with tile status"); 277bf215546Sopenharmony_ci return NULL; 278bf215546Sopenharmony_ci } 279bf215546Sopenharmony_ci 280bf215546Sopenharmony_ci struct pipe_resource templ = *prsc; 281bf215546Sopenharmony_ci templ.nr_samples = 0; 282bf215546Sopenharmony_ci templ.bind = PIPE_BIND_RENDER_TARGET; 283bf215546Sopenharmony_ci 284bf215546Sopenharmony_ci trans->rsc = etna_resource_alloc(pctx->screen, ETNA_LAYOUT_LINEAR, 285bf215546Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, &templ); 286bf215546Sopenharmony_ci if (!trans->rsc) { 287bf215546Sopenharmony_ci slab_free(&ctx->transfer_pool, trans); 288bf215546Sopenharmony_ci return NULL; 289bf215546Sopenharmony_ci } 290bf215546Sopenharmony_ci 291bf215546Sopenharmony_ci if (!screen->specs.use_blt) { 292bf215546Sopenharmony_ci /* Need to align the transfer region to satisfy RS restrictions, as we 293bf215546Sopenharmony_ci * really want to hit the RS blit path here. 294bf215546Sopenharmony_ci */ 295bf215546Sopenharmony_ci unsigned w_align, h_align; 296bf215546Sopenharmony_ci 297bf215546Sopenharmony_ci if (rsc->layout & ETNA_LAYOUT_BIT_SUPER) { 298bf215546Sopenharmony_ci w_align = 64; 299bf215546Sopenharmony_ci h_align = 64 * ctx->screen->specs.pixel_pipes; 300bf215546Sopenharmony_ci } else { 301bf215546Sopenharmony_ci w_align = ETNA_RS_WIDTH_MASK + 1; 302bf215546Sopenharmony_ci h_align = ETNA_RS_HEIGHT_MASK + 1; 303bf215546Sopenharmony_ci } 304bf215546Sopenharmony_ci 305bf215546Sopenharmony_ci ptrans->box.width += ptrans->box.x & (w_align - 1); 306bf215546Sopenharmony_ci ptrans->box.x = ptrans->box.x & ~(w_align - 1); 307bf215546Sopenharmony_ci ptrans->box.width = align(ptrans->box.width, (ETNA_RS_WIDTH_MASK + 1)); 308bf215546Sopenharmony_ci ptrans->box.height += ptrans->box.y & (h_align - 1); 309bf215546Sopenharmony_ci ptrans->box.y = ptrans->box.y & ~(h_align - 1); 310bf215546Sopenharmony_ci ptrans->box.height = align(ptrans->box.height, ETNA_RS_HEIGHT_MASK + 1); 311bf215546Sopenharmony_ci } 312bf215546Sopenharmony_ci 313bf215546Sopenharmony_ci if (!(usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE)) 314bf215546Sopenharmony_ci etna_copy_resource_box(pctx, trans->rsc, &rsc->base, level, &ptrans->box); 315bf215546Sopenharmony_ci 316bf215546Sopenharmony_ci /* Switch to using the temporary resource instead */ 317bf215546Sopenharmony_ci rsc = etna_resource(trans->rsc); 318bf215546Sopenharmony_ci } 319bf215546Sopenharmony_ci 320bf215546Sopenharmony_ci struct etna_resource_level *res_level = &rsc->levels[level]; 321bf215546Sopenharmony_ci 322bf215546Sopenharmony_ci /* XXX we don't handle PIPE_MAP_FLUSH_EXPLICIT; this flag can be ignored 323bf215546Sopenharmony_ci * when mapping in-place, 324bf215546Sopenharmony_ci * but when not in place we need to fire off the copy operation in 325bf215546Sopenharmony_ci * transfer_flush_region (currently 326bf215546Sopenharmony_ci * a no-op) instead of unmap. Need to handle this to support 327bf215546Sopenharmony_ci * ARB_map_buffer_range extension at least. 328bf215546Sopenharmony_ci */ 329bf215546Sopenharmony_ci /* XXX we don't take care of current operations on the resource; which can 330bf215546Sopenharmony_ci be, at some point in the pipeline 331bf215546Sopenharmony_ci which is not yet executed: 332bf215546Sopenharmony_ci 333bf215546Sopenharmony_ci - bound as surface 334bf215546Sopenharmony_ci - bound through vertex buffer 335bf215546Sopenharmony_ci - bound through index buffer 336bf215546Sopenharmony_ci - bound in sampler view 337bf215546Sopenharmony_ci - used in clear_render_target / clear_depth_stencil operation 338bf215546Sopenharmony_ci - used in blit 339bf215546Sopenharmony_ci - used in resource_copy_region 340bf215546Sopenharmony_ci 341bf215546Sopenharmony_ci How do other drivers record this information over course of the rendering 342bf215546Sopenharmony_ci pipeline? 343bf215546Sopenharmony_ci Is it necessary at all? Only in case we want to provide a fast path and 344bf215546Sopenharmony_ci map the resource directly 345bf215546Sopenharmony_ci (and for PIPE_MAP_DIRECTLY) and we don't want to force a sync. 346bf215546Sopenharmony_ci We also need to know whether the resource is in use to determine if a sync 347bf215546Sopenharmony_ci is needed (or just do it 348bf215546Sopenharmony_ci always, but that comes at the expense of performance). 349bf215546Sopenharmony_ci 350bf215546Sopenharmony_ci A conservative approximation without too much overhead would be to mark 351bf215546Sopenharmony_ci all resources that have 352bf215546Sopenharmony_ci been bound at some point as busy. A drawback would be that accessing 353bf215546Sopenharmony_ci resources that have 354bf215546Sopenharmony_ci been bound but are no longer in use for a while still carry a performance 355bf215546Sopenharmony_ci penalty. On the other hand, 356bf215546Sopenharmony_ci the program could be using PIPE_MAP_DISCARD_WHOLE_RESOURCE or 357bf215546Sopenharmony_ci PIPE_MAP_UNSYNCHRONIZED to 358bf215546Sopenharmony_ci avoid this in the first place... 359bf215546Sopenharmony_ci 360bf215546Sopenharmony_ci A) We use an in-pipe copy engine, and queue the copy operation after unmap 361bf215546Sopenharmony_ci so that the copy 362bf215546Sopenharmony_ci will be performed when all current commands have been executed. 363bf215546Sopenharmony_ci Using the RS is possible, not sure if always efficient. This can also 364bf215546Sopenharmony_ci do any kind of tiling for us. 365bf215546Sopenharmony_ci Only possible when PIPE_MAP_DISCARD_RANGE is set. 366bf215546Sopenharmony_ci B) We discard the entire resource (or at least, the mipmap level) and 367bf215546Sopenharmony_ci allocate new memory for it. 368bf215546Sopenharmony_ci Only possible when mapping the entire resource or 369bf215546Sopenharmony_ci PIPE_MAP_DISCARD_WHOLE_RESOURCE is set. 370bf215546Sopenharmony_ci */ 371bf215546Sopenharmony_ci 372bf215546Sopenharmony_ci /* 373bf215546Sopenharmony_ci * Pull resources into the CPU domain. Only skipped for unsynchronized 374bf215546Sopenharmony_ci * transfers without a temporary resource. 375bf215546Sopenharmony_ci */ 376bf215546Sopenharmony_ci if (trans->rsc || !(usage & PIPE_MAP_UNSYNCHRONIZED)) { 377bf215546Sopenharmony_ci enum etna_resource_status status = etna_resource_status(ctx, rsc); 378bf215546Sopenharmony_ci uint32_t prep_flags = 0; 379bf215546Sopenharmony_ci 380bf215546Sopenharmony_ci /* 381bf215546Sopenharmony_ci * Always flush if we have the temporary resource and have a copy to this 382bf215546Sopenharmony_ci * outstanding. Otherwise infer flush requirement from resource access and 383bf215546Sopenharmony_ci * current GPU usage (reads must wait for GPU writes, writes must have 384bf215546Sopenharmony_ci * exclusive access to the buffer). 385bf215546Sopenharmony_ci */ 386bf215546Sopenharmony_ci if ((trans->rsc && (status & ETNA_PENDING_WRITE)) || 387bf215546Sopenharmony_ci (!trans->rsc && 388bf215546Sopenharmony_ci (((usage & PIPE_MAP_READ) && (status & ETNA_PENDING_WRITE)) || 389bf215546Sopenharmony_ci ((usage & PIPE_MAP_WRITE) && status)))) { 390bf215546Sopenharmony_ci pctx->flush(pctx, NULL, 0); 391bf215546Sopenharmony_ci } 392bf215546Sopenharmony_ci 393bf215546Sopenharmony_ci if (usage & PIPE_MAP_READ) 394bf215546Sopenharmony_ci prep_flags |= DRM_ETNA_PREP_READ; 395bf215546Sopenharmony_ci if (usage & PIPE_MAP_WRITE) 396bf215546Sopenharmony_ci prep_flags |= DRM_ETNA_PREP_WRITE; 397bf215546Sopenharmony_ci 398bf215546Sopenharmony_ci /* 399bf215546Sopenharmony_ci * The ETC2 patching operates in-place on the resource, so the resource will 400bf215546Sopenharmony_ci * get written even on read-only transfers. This blocks the GPU to sample 401bf215546Sopenharmony_ci * from this resource. 402bf215546Sopenharmony_ci */ 403bf215546Sopenharmony_ci if ((usage & PIPE_MAP_READ) && etna_etc2_needs_patching(prsc)) 404bf215546Sopenharmony_ci prep_flags |= DRM_ETNA_PREP_WRITE; 405bf215546Sopenharmony_ci 406bf215546Sopenharmony_ci if (etna_bo_cpu_prep(rsc->bo, prep_flags)) 407bf215546Sopenharmony_ci goto fail_prep; 408bf215546Sopenharmony_ci } 409bf215546Sopenharmony_ci 410bf215546Sopenharmony_ci /* map buffer object */ 411bf215546Sopenharmony_ci trans->mapped = etna_bo_map(rsc->bo); 412bf215546Sopenharmony_ci if (!trans->mapped) 413bf215546Sopenharmony_ci goto fail; 414bf215546Sopenharmony_ci 415bf215546Sopenharmony_ci *out_transfer = ptrans; 416bf215546Sopenharmony_ci 417bf215546Sopenharmony_ci if (rsc->layout == ETNA_LAYOUT_LINEAR) { 418bf215546Sopenharmony_ci ptrans->stride = res_level->stride; 419bf215546Sopenharmony_ci ptrans->layer_stride = res_level->layer_stride; 420bf215546Sopenharmony_ci 421bf215546Sopenharmony_ci trans->mapped += res_level->offset + 422bf215546Sopenharmony_ci etna_compute_offset(prsc->format, box, res_level->stride, 423bf215546Sopenharmony_ci res_level->layer_stride); 424bf215546Sopenharmony_ci 425bf215546Sopenharmony_ci /* We need to have the unpatched data ready for the gfx stack. */ 426bf215546Sopenharmony_ci if (usage & PIPE_MAP_READ) 427bf215546Sopenharmony_ci etna_unpatch_data(trans->mapped, ptrans); 428bf215546Sopenharmony_ci 429bf215546Sopenharmony_ci return trans->mapped; 430bf215546Sopenharmony_ci } else { 431bf215546Sopenharmony_ci unsigned divSizeX = util_format_get_blockwidth(format); 432bf215546Sopenharmony_ci unsigned divSizeY = util_format_get_blockheight(format); 433bf215546Sopenharmony_ci 434bf215546Sopenharmony_ci /* No direct mappings of tiled, since we need to manually 435bf215546Sopenharmony_ci * tile/untile. 436bf215546Sopenharmony_ci */ 437bf215546Sopenharmony_ci if (usage & PIPE_MAP_DIRECTLY) 438bf215546Sopenharmony_ci goto fail; 439bf215546Sopenharmony_ci 440bf215546Sopenharmony_ci trans->mapped += res_level->offset; 441bf215546Sopenharmony_ci ptrans->stride = align(box->width, divSizeX) * util_format_get_blocksize(format); /* row stride in bytes */ 442bf215546Sopenharmony_ci ptrans->layer_stride = align(box->height, divSizeY) * ptrans->stride; 443bf215546Sopenharmony_ci size_t size = ptrans->layer_stride * box->depth; 444bf215546Sopenharmony_ci 445bf215546Sopenharmony_ci trans->staging = MALLOC(size); 446bf215546Sopenharmony_ci if (!trans->staging) 447bf215546Sopenharmony_ci goto fail; 448bf215546Sopenharmony_ci 449bf215546Sopenharmony_ci if (usage & PIPE_MAP_READ) { 450bf215546Sopenharmony_ci if (rsc->layout == ETNA_LAYOUT_TILED) { 451bf215546Sopenharmony_ci for (unsigned z = 0; z < ptrans->box.depth; z++) { 452bf215546Sopenharmony_ci etna_texture_untile(trans->staging + z * ptrans->layer_stride, 453bf215546Sopenharmony_ci trans->mapped + (ptrans->box.z + z) * res_level->layer_stride, 454bf215546Sopenharmony_ci ptrans->box.x, ptrans->box.y, res_level->stride, 455bf215546Sopenharmony_ci ptrans->box.width, ptrans->box.height, ptrans->stride, 456bf215546Sopenharmony_ci util_format_get_blocksize(rsc->base.format)); 457bf215546Sopenharmony_ci } 458bf215546Sopenharmony_ci } else if (rsc->layout == ETNA_LAYOUT_LINEAR) { 459bf215546Sopenharmony_ci util_copy_box(trans->staging, rsc->base.format, ptrans->stride, 460bf215546Sopenharmony_ci ptrans->layer_stride, 0, 0, 0, /* dst x,y,z */ 461bf215546Sopenharmony_ci ptrans->box.width, ptrans->box.height, 462bf215546Sopenharmony_ci ptrans->box.depth, trans->mapped, res_level->stride, 463bf215546Sopenharmony_ci res_level->layer_stride, ptrans->box.x, 464bf215546Sopenharmony_ci ptrans->box.y, ptrans->box.z); 465bf215546Sopenharmony_ci } else { 466bf215546Sopenharmony_ci /* TODO supertiling */ 467bf215546Sopenharmony_ci BUG("unsupported tiling %i for reading", rsc->layout); 468bf215546Sopenharmony_ci } 469bf215546Sopenharmony_ci } 470bf215546Sopenharmony_ci 471bf215546Sopenharmony_ci return trans->staging; 472bf215546Sopenharmony_ci } 473bf215546Sopenharmony_ci 474bf215546Sopenharmony_cifail: 475bf215546Sopenharmony_ci etna_bo_cpu_fini(rsc->bo); 476bf215546Sopenharmony_cifail_prep: 477bf215546Sopenharmony_ci etna_transfer_unmap(pctx, ptrans); 478bf215546Sopenharmony_ci return NULL; 479bf215546Sopenharmony_ci} 480bf215546Sopenharmony_ci 481bf215546Sopenharmony_cistatic void 482bf215546Sopenharmony_cietna_transfer_flush_region(struct pipe_context *pctx, 483bf215546Sopenharmony_ci struct pipe_transfer *ptrans, 484bf215546Sopenharmony_ci const struct pipe_box *box) 485bf215546Sopenharmony_ci{ 486bf215546Sopenharmony_ci struct etna_resource *rsc = etna_resource(ptrans->resource); 487bf215546Sopenharmony_ci 488bf215546Sopenharmony_ci if (ptrans->resource->target == PIPE_BUFFER) 489bf215546Sopenharmony_ci util_range_add(&rsc->base, 490bf215546Sopenharmony_ci &rsc->valid_buffer_range, 491bf215546Sopenharmony_ci ptrans->box.x + box->x, 492bf215546Sopenharmony_ci ptrans->box.x + box->x + box->width); 493bf215546Sopenharmony_ci} 494bf215546Sopenharmony_ci 495bf215546Sopenharmony_civoid 496bf215546Sopenharmony_cietna_transfer_init(struct pipe_context *pctx) 497bf215546Sopenharmony_ci{ 498bf215546Sopenharmony_ci pctx->buffer_map = etna_transfer_map; 499bf215546Sopenharmony_ci pctx->texture_map = etna_transfer_map; 500bf215546Sopenharmony_ci pctx->transfer_flush_region = etna_transfer_flush_region; 501bf215546Sopenharmony_ci pctx->buffer_unmap = etna_transfer_unmap; 502bf215546Sopenharmony_ci pctx->texture_unmap = etna_transfer_unmap; 503bf215546Sopenharmony_ci pctx->buffer_subdata = u_default_buffer_subdata; 504bf215546Sopenharmony_ci pctx->texture_subdata = u_default_texture_subdata; 505bf215546Sopenharmony_ci} 506