1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright 2014, 2015 Red Hat. 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 * on the rights to use, copy, modify, merge, publish, distribute, sub 8bf215546Sopenharmony_ci * license, and/or sell copies of the Software, and to permit persons to whom 9bf215546Sopenharmony_ci * the 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 next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * 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 AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 19bf215546Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20bf215546Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21bf215546Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci#include "util/format/u_format.h" 24bf215546Sopenharmony_ci#include "util/u_inlines.h" 25bf215546Sopenharmony_ci#include "util/u_memory.h" 26bf215546Sopenharmony_ci#include "util/u_upload_mgr.h" 27bf215546Sopenharmony_ci#include "virgl_context.h" 28bf215546Sopenharmony_ci#include "virgl_resource.h" 29bf215546Sopenharmony_ci#include "virgl_screen.h" 30bf215546Sopenharmony_ci#include "virgl_staging_mgr.h" 31bf215546Sopenharmony_ci#include "virgl_encode.h" // for declaration of virgl_encode_copy_transfer 32bf215546Sopenharmony_ci 33bf215546Sopenharmony_ci/* A (soft) limit for the amount of memory we want to allow for queued staging 34bf215546Sopenharmony_ci * resources. This is used to decide when we should force a flush, in order to 35bf215546Sopenharmony_ci * avoid exhausting virtio-gpu memory. 36bf215546Sopenharmony_ci */ 37bf215546Sopenharmony_ci#define VIRGL_QUEUED_STAGING_RES_SIZE_LIMIT (128 * 1024 * 1024) 38bf215546Sopenharmony_ci 39bf215546Sopenharmony_cienum virgl_transfer_map_type { 40bf215546Sopenharmony_ci VIRGL_TRANSFER_MAP_ERROR = -1, 41bf215546Sopenharmony_ci VIRGL_TRANSFER_MAP_HW_RES, 42bf215546Sopenharmony_ci 43bf215546Sopenharmony_ci /* Map a range of a staging buffer. The updated contents should be transferred 44bf215546Sopenharmony_ci * with a copy transfer. 45bf215546Sopenharmony_ci */ 46bf215546Sopenharmony_ci VIRGL_TRANSFER_MAP_WRITE_TO_STAGING, 47bf215546Sopenharmony_ci 48bf215546Sopenharmony_ci /* Reallocate the underlying virgl_hw_res. */ 49bf215546Sopenharmony_ci VIRGL_TRANSFER_MAP_REALLOC, 50bf215546Sopenharmony_ci 51bf215546Sopenharmony_ci /* Map type for read of texture data from host to guest 52bf215546Sopenharmony_ci * using staging buffer. */ 53bf215546Sopenharmony_ci VIRGL_TRANSFER_MAP_READ_FROM_STAGING, 54bf215546Sopenharmony_ci /* Map type for write of texture data to host using staging 55bf215546Sopenharmony_ci * buffer that needs a readback first. */ 56bf215546Sopenharmony_ci VIRGL_TRANSFER_MAP_WRITE_TO_STAGING_WITH_READBACK, 57bf215546Sopenharmony_ci}; 58bf215546Sopenharmony_ci 59bf215546Sopenharmony_ci/* Check if copy transfer from host can be used: 60bf215546Sopenharmony_ci * 1. if resource is a texture, 61bf215546Sopenharmony_ci * 2. if renderer supports copy transfer from host, 62bf215546Sopenharmony_ci * 3. the host is not GLES (no fake FP64) 63bf215546Sopenharmony_ci * 4. the format can be rendered to and the format is a readback format 64bf215546Sopenharmony_ci * or the format is a scanout format and we can read back from scanout 65bf215546Sopenharmony_ci */ 66bf215546Sopenharmony_cistatic bool virgl_can_readback_from_rendertarget(struct virgl_screen *vs, 67bf215546Sopenharmony_ci struct virgl_resource *res) 68bf215546Sopenharmony_ci{ 69bf215546Sopenharmony_ci return res->b.nr_samples < 2 && 70bf215546Sopenharmony_ci vs->base.is_format_supported(&vs->base, res->b.format, res->b.target, 71bf215546Sopenharmony_ci res->b.nr_samples, res->b.nr_samples, 72bf215546Sopenharmony_ci PIPE_BIND_RENDER_TARGET); 73bf215546Sopenharmony_ci} 74bf215546Sopenharmony_ci 75bf215546Sopenharmony_cistatic bool virgl_can_readback_from_scanout(struct virgl_screen *vs, 76bf215546Sopenharmony_ci struct virgl_resource *res, 77bf215546Sopenharmony_ci int bind) 78bf215546Sopenharmony_ci{ 79bf215546Sopenharmony_ci return (vs->caps.caps.v2.capability_bits_v2 & VIRGL_CAP_V2_SCANOUT_USES_GBM) && 80bf215546Sopenharmony_ci (bind & VIRGL_BIND_SCANOUT) && 81bf215546Sopenharmony_ci virgl_has_scanout_format(vs, res->b.format, true); 82bf215546Sopenharmony_ci} 83bf215546Sopenharmony_ci 84bf215546Sopenharmony_cistatic bool virgl_can_use_staging(struct virgl_screen *vs, 85bf215546Sopenharmony_ci struct virgl_resource *res) 86bf215546Sopenharmony_ci{ 87bf215546Sopenharmony_ci return (vs->caps.caps.v2.capability_bits_v2 & VIRGL_CAP_V2_COPY_TRANSFER_BOTH_DIRECTIONS) && 88bf215546Sopenharmony_ci (res->b.target != PIPE_BUFFER); 89bf215546Sopenharmony_ci} 90bf215546Sopenharmony_ci 91bf215546Sopenharmony_cistatic bool is_stencil_array(struct virgl_resource *res) 92bf215546Sopenharmony_ci{ 93bf215546Sopenharmony_ci const struct util_format_description *descr = util_format_description(res->b.format); 94bf215546Sopenharmony_ci return (res->b.array_size > 1 || res->b.depth0 > 1) && util_format_has_stencil(descr); 95bf215546Sopenharmony_ci} 96bf215546Sopenharmony_ci 97bf215546Sopenharmony_cistatic bool virgl_can_copy_transfer_from_host(struct virgl_screen *vs, 98bf215546Sopenharmony_ci struct virgl_resource *res, 99bf215546Sopenharmony_ci int bind) 100bf215546Sopenharmony_ci{ 101bf215546Sopenharmony_ci return virgl_can_use_staging(vs, res) && 102bf215546Sopenharmony_ci !is_stencil_array(res) && 103bf215546Sopenharmony_ci virgl_has_readback_format(&vs->base, pipe_to_virgl_format(res->b.format), false) && 104bf215546Sopenharmony_ci ((!(vs->caps.caps.v2.capability_bits & VIRGL_CAP_FAKE_FP64)) || 105bf215546Sopenharmony_ci virgl_can_readback_from_rendertarget(vs, res) || 106bf215546Sopenharmony_ci virgl_can_readback_from_scanout(vs, res, bind)); 107bf215546Sopenharmony_ci} 108bf215546Sopenharmony_ci 109bf215546Sopenharmony_ci/* We need to flush to properly sync the transfer with the current cmdbuf. 110bf215546Sopenharmony_ci * But there are cases where the flushing can be skipped: 111bf215546Sopenharmony_ci * 112bf215546Sopenharmony_ci * - synchronization is disabled 113bf215546Sopenharmony_ci * - the resource is not referenced by the current cmdbuf 114bf215546Sopenharmony_ci */ 115bf215546Sopenharmony_cistatic bool virgl_res_needs_flush(struct virgl_context *vctx, 116bf215546Sopenharmony_ci struct virgl_transfer *trans) 117bf215546Sopenharmony_ci{ 118bf215546Sopenharmony_ci struct virgl_winsys *vws = virgl_screen(vctx->base.screen)->vws; 119bf215546Sopenharmony_ci struct virgl_resource *res = virgl_resource(trans->base.resource); 120bf215546Sopenharmony_ci 121bf215546Sopenharmony_ci if (trans->base.usage & PIPE_MAP_UNSYNCHRONIZED) 122bf215546Sopenharmony_ci return false; 123bf215546Sopenharmony_ci 124bf215546Sopenharmony_ci if (!vws->res_is_referenced(vws, vctx->cbuf, res->hw_res)) 125bf215546Sopenharmony_ci return false; 126bf215546Sopenharmony_ci 127bf215546Sopenharmony_ci return true; 128bf215546Sopenharmony_ci} 129bf215546Sopenharmony_ci 130bf215546Sopenharmony_ci/* We need to read back from the host storage to make sure the guest storage 131bf215546Sopenharmony_ci * is up-to-date. But there are cases where the readback can be skipped: 132bf215546Sopenharmony_ci * 133bf215546Sopenharmony_ci * - the content can be discarded 134bf215546Sopenharmony_ci * - the host storage is read-only 135bf215546Sopenharmony_ci * 136bf215546Sopenharmony_ci * Note that PIPE_MAP_WRITE without discard bits requires readback. 137bf215546Sopenharmony_ci * PIPE_MAP_READ becomes irrelevant. PIPE_MAP_UNSYNCHRONIZED and 138bf215546Sopenharmony_ci * PIPE_MAP_FLUSH_EXPLICIT are also irrelevant. 139bf215546Sopenharmony_ci */ 140bf215546Sopenharmony_cistatic bool virgl_res_needs_readback(struct virgl_context *vctx, 141bf215546Sopenharmony_ci struct virgl_resource *res, 142bf215546Sopenharmony_ci unsigned usage, unsigned level) 143bf215546Sopenharmony_ci{ 144bf215546Sopenharmony_ci if (usage & (PIPE_MAP_DISCARD_RANGE | 145bf215546Sopenharmony_ci PIPE_MAP_DISCARD_WHOLE_RESOURCE)) 146bf215546Sopenharmony_ci return false; 147bf215546Sopenharmony_ci 148bf215546Sopenharmony_ci if (res->clean_mask & (1 << level)) 149bf215546Sopenharmony_ci return false; 150bf215546Sopenharmony_ci 151bf215546Sopenharmony_ci return true; 152bf215546Sopenharmony_ci} 153bf215546Sopenharmony_ci 154bf215546Sopenharmony_cistatic enum virgl_transfer_map_type 155bf215546Sopenharmony_civirgl_resource_transfer_prepare(struct virgl_context *vctx, 156bf215546Sopenharmony_ci struct virgl_transfer *xfer) 157bf215546Sopenharmony_ci{ 158bf215546Sopenharmony_ci struct virgl_screen *vs = virgl_screen(vctx->base.screen); 159bf215546Sopenharmony_ci struct virgl_winsys *vws = vs->vws; 160bf215546Sopenharmony_ci struct virgl_resource *res = virgl_resource(xfer->base.resource); 161bf215546Sopenharmony_ci enum virgl_transfer_map_type map_type = VIRGL_TRANSFER_MAP_HW_RES; 162bf215546Sopenharmony_ci bool flush; 163bf215546Sopenharmony_ci bool readback; 164bf215546Sopenharmony_ci bool wait; 165bf215546Sopenharmony_ci 166bf215546Sopenharmony_ci /* there is no way to map the host storage currently */ 167bf215546Sopenharmony_ci if (xfer->base.usage & PIPE_MAP_DIRECTLY) 168bf215546Sopenharmony_ci return VIRGL_TRANSFER_MAP_ERROR; 169bf215546Sopenharmony_ci 170bf215546Sopenharmony_ci /* We break the logic down into four steps 171bf215546Sopenharmony_ci * 172bf215546Sopenharmony_ci * step 1: determine the required operations independently 173bf215546Sopenharmony_ci * step 2: look for chances to skip the operations 174bf215546Sopenharmony_ci * step 3: resolve dependencies between the operations 175bf215546Sopenharmony_ci * step 4: execute the operations 176bf215546Sopenharmony_ci */ 177bf215546Sopenharmony_ci 178bf215546Sopenharmony_ci flush = virgl_res_needs_flush(vctx, xfer); 179bf215546Sopenharmony_ci readback = virgl_res_needs_readback(vctx, res, xfer->base.usage, 180bf215546Sopenharmony_ci xfer->base.level); 181bf215546Sopenharmony_ci /* We need to wait for all cmdbufs, current or previous, that access the 182bf215546Sopenharmony_ci * resource to finish unless synchronization is disabled. 183bf215546Sopenharmony_ci */ 184bf215546Sopenharmony_ci wait = !(xfer->base.usage & PIPE_MAP_UNSYNCHRONIZED); 185bf215546Sopenharmony_ci 186bf215546Sopenharmony_ci /* When the transfer range consists of only uninitialized data, we can 187bf215546Sopenharmony_ci * assume the GPU is not accessing the range and readback is unnecessary. 188bf215546Sopenharmony_ci * We can proceed as if PIPE_MAP_UNSYNCHRONIZED and 189bf215546Sopenharmony_ci * PIPE_MAP_DISCARD_RANGE are set. 190bf215546Sopenharmony_ci */ 191bf215546Sopenharmony_ci if (res->b.target == PIPE_BUFFER && 192bf215546Sopenharmony_ci !util_ranges_intersect(&res->valid_buffer_range, xfer->base.box.x, 193bf215546Sopenharmony_ci xfer->base.box.x + xfer->base.box.width) && 194bf215546Sopenharmony_ci likely(!(virgl_debug & VIRGL_DEBUG_XFER))) { 195bf215546Sopenharmony_ci flush = false; 196bf215546Sopenharmony_ci readback = false; 197bf215546Sopenharmony_ci wait = false; 198bf215546Sopenharmony_ci } 199bf215546Sopenharmony_ci 200bf215546Sopenharmony_ci /* When the resource is busy but its content can be discarded, we can 201bf215546Sopenharmony_ci * replace its HW resource or use a staging buffer to avoid waiting. 202bf215546Sopenharmony_ci */ 203bf215546Sopenharmony_ci if (wait && 204bf215546Sopenharmony_ci (xfer->base.usage & (PIPE_MAP_DISCARD_RANGE | 205bf215546Sopenharmony_ci PIPE_MAP_DISCARD_WHOLE_RESOURCE)) && 206bf215546Sopenharmony_ci likely(!(virgl_debug & VIRGL_DEBUG_XFER))) { 207bf215546Sopenharmony_ci bool can_realloc = false; 208bf215546Sopenharmony_ci 209bf215546Sopenharmony_ci /* A PIPE_MAP_DISCARD_WHOLE_RESOURCE transfer may be followed by 210bf215546Sopenharmony_ci * PIPE_MAP_UNSYNCHRONIZED transfers to non-overlapping regions. 211bf215546Sopenharmony_ci * It cannot be treated as a PIPE_MAP_DISCARD_RANGE transfer, 212bf215546Sopenharmony_ci * otherwise those following unsynchronized transfers may overwrite 213bf215546Sopenharmony_ci * valid data. 214bf215546Sopenharmony_ci */ 215bf215546Sopenharmony_ci if (xfer->base.usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) { 216bf215546Sopenharmony_ci can_realloc = virgl_can_rebind_resource(vctx, &res->b); 217bf215546Sopenharmony_ci } 218bf215546Sopenharmony_ci 219bf215546Sopenharmony_ci /* discard implies no readback */ 220bf215546Sopenharmony_ci assert(!readback); 221bf215546Sopenharmony_ci 222bf215546Sopenharmony_ci if (can_realloc || vctx->supports_staging) { 223bf215546Sopenharmony_ci /* Both map types have some costs. Do them only when the resource is 224bf215546Sopenharmony_ci * (or will be) busy for real. Otherwise, set wait to false. 225bf215546Sopenharmony_ci */ 226bf215546Sopenharmony_ci wait = (flush || vws->resource_is_busy(vws, res->hw_res)); 227bf215546Sopenharmony_ci if (wait) { 228bf215546Sopenharmony_ci map_type = (can_realloc) ? 229bf215546Sopenharmony_ci VIRGL_TRANSFER_MAP_REALLOC : 230bf215546Sopenharmony_ci VIRGL_TRANSFER_MAP_WRITE_TO_STAGING; 231bf215546Sopenharmony_ci 232bf215546Sopenharmony_ci wait = false; 233bf215546Sopenharmony_ci 234bf215546Sopenharmony_ci /* There is normally no need to flush either, unless the amount of 235bf215546Sopenharmony_ci * memory we are using for staging resources starts growing, in 236bf215546Sopenharmony_ci * which case we want to flush to keep our memory consumption in 237bf215546Sopenharmony_ci * check. 238bf215546Sopenharmony_ci */ 239bf215546Sopenharmony_ci flush = (vctx->queued_staging_res_size > 240bf215546Sopenharmony_ci VIRGL_QUEUED_STAGING_RES_SIZE_LIMIT); 241bf215546Sopenharmony_ci } 242bf215546Sopenharmony_ci } 243bf215546Sopenharmony_ci } 244bf215546Sopenharmony_ci 245bf215546Sopenharmony_ci /* readback has some implications */ 246bf215546Sopenharmony_ci if (readback) { 247bf215546Sopenharmony_ci /* If we are performing readback for textures and renderer supports 248bf215546Sopenharmony_ci * copy_transfer_from_host, then we can return here with proper map. 249bf215546Sopenharmony_ci */ 250bf215546Sopenharmony_ci if (res->use_staging) { 251bf215546Sopenharmony_ci if (xfer->base.usage & PIPE_MAP_READ) 252bf215546Sopenharmony_ci return VIRGL_TRANSFER_MAP_READ_FROM_STAGING; 253bf215546Sopenharmony_ci else 254bf215546Sopenharmony_ci return VIRGL_TRANSFER_MAP_WRITE_TO_STAGING_WITH_READBACK; 255bf215546Sopenharmony_ci } 256bf215546Sopenharmony_ci 257bf215546Sopenharmony_ci /* When the transfer queue has pending writes to this transfer's region, 258bf215546Sopenharmony_ci * we have to flush before readback. 259bf215546Sopenharmony_ci */ 260bf215546Sopenharmony_ci if (!flush && virgl_transfer_queue_is_queued(&vctx->queue, xfer)) 261bf215546Sopenharmony_ci flush = true; 262bf215546Sopenharmony_ci } 263bf215546Sopenharmony_ci 264bf215546Sopenharmony_ci if (flush) 265bf215546Sopenharmony_ci vctx->base.flush(&vctx->base, NULL, 0); 266bf215546Sopenharmony_ci 267bf215546Sopenharmony_ci /* If we are not allowed to block, and we know that we will have to wait, 268bf215546Sopenharmony_ci * either because the resource is busy, or because it will become busy due 269bf215546Sopenharmony_ci * to a readback, return early to avoid performing an incomplete 270bf215546Sopenharmony_ci * transfer_get. Such an incomplete transfer_get may finish at any time, 271bf215546Sopenharmony_ci * during which another unsynchronized map could write to the resource 272bf215546Sopenharmony_ci * contents, leaving the contents in an undefined state. 273bf215546Sopenharmony_ci */ 274bf215546Sopenharmony_ci if ((xfer->base.usage & PIPE_MAP_DONTBLOCK) && 275bf215546Sopenharmony_ci (readback || (wait && vws->resource_is_busy(vws, res->hw_res)))) 276bf215546Sopenharmony_ci return VIRGL_TRANSFER_MAP_ERROR; 277bf215546Sopenharmony_ci 278bf215546Sopenharmony_ci if (readback) { 279bf215546Sopenharmony_ci /* Readback is yet another command and is transparent to the state 280bf215546Sopenharmony_ci * trackers. It should be waited for in all cases, including when 281bf215546Sopenharmony_ci * PIPE_MAP_UNSYNCHRONIZED is set. 282bf215546Sopenharmony_ci */ 283bf215546Sopenharmony_ci vws->resource_wait(vws, res->hw_res); 284bf215546Sopenharmony_ci vws->transfer_get(vws, res->hw_res, &xfer->base.box, xfer->base.stride, 285bf215546Sopenharmony_ci xfer->l_stride, xfer->offset, xfer->base.level); 286bf215546Sopenharmony_ci /* transfer_get puts the resource into a maybe_busy state, so we will have 287bf215546Sopenharmony_ci * to wait another time if we want to use that resource. */ 288bf215546Sopenharmony_ci wait = true; 289bf215546Sopenharmony_ci } 290bf215546Sopenharmony_ci 291bf215546Sopenharmony_ci if (wait) 292bf215546Sopenharmony_ci vws->resource_wait(vws, res->hw_res); 293bf215546Sopenharmony_ci 294bf215546Sopenharmony_ci if (res->use_staging) { 295bf215546Sopenharmony_ci map_type = VIRGL_TRANSFER_MAP_WRITE_TO_STAGING; 296bf215546Sopenharmony_ci } 297bf215546Sopenharmony_ci 298bf215546Sopenharmony_ci return map_type; 299bf215546Sopenharmony_ci} 300bf215546Sopenharmony_ci 301bf215546Sopenharmony_ci/* Calculate the minimum size of the memory required to service a resource 302bf215546Sopenharmony_ci * transfer map. Also return the stride and layer_stride for the corresponding 303bf215546Sopenharmony_ci * layout. 304bf215546Sopenharmony_ci */ 305bf215546Sopenharmony_cistatic unsigned 306bf215546Sopenharmony_civirgl_transfer_map_size(struct virgl_transfer *vtransfer, 307bf215546Sopenharmony_ci unsigned *out_stride, 308bf215546Sopenharmony_ci unsigned *out_layer_stride) 309bf215546Sopenharmony_ci{ 310bf215546Sopenharmony_ci struct pipe_resource *pres = vtransfer->base.resource; 311bf215546Sopenharmony_ci struct pipe_box *box = &vtransfer->base.box; 312bf215546Sopenharmony_ci unsigned stride; 313bf215546Sopenharmony_ci unsigned layer_stride; 314bf215546Sopenharmony_ci unsigned size; 315bf215546Sopenharmony_ci 316bf215546Sopenharmony_ci assert(out_stride); 317bf215546Sopenharmony_ci assert(out_layer_stride); 318bf215546Sopenharmony_ci 319bf215546Sopenharmony_ci stride = util_format_get_stride(pres->format, box->width); 320bf215546Sopenharmony_ci layer_stride = util_format_get_2d_size(pres->format, stride, box->height); 321bf215546Sopenharmony_ci 322bf215546Sopenharmony_ci if (pres->target == PIPE_TEXTURE_CUBE || 323bf215546Sopenharmony_ci pres->target == PIPE_TEXTURE_CUBE_ARRAY || 324bf215546Sopenharmony_ci pres->target == PIPE_TEXTURE_3D || 325bf215546Sopenharmony_ci pres->target == PIPE_TEXTURE_2D_ARRAY) { 326bf215546Sopenharmony_ci size = box->depth * layer_stride; 327bf215546Sopenharmony_ci } else if (pres->target == PIPE_TEXTURE_1D_ARRAY) { 328bf215546Sopenharmony_ci size = box->depth * stride; 329bf215546Sopenharmony_ci } else { 330bf215546Sopenharmony_ci size = layer_stride; 331bf215546Sopenharmony_ci } 332bf215546Sopenharmony_ci 333bf215546Sopenharmony_ci *out_stride = stride; 334bf215546Sopenharmony_ci *out_layer_stride = layer_stride; 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci return size; 337bf215546Sopenharmony_ci} 338bf215546Sopenharmony_ci 339bf215546Sopenharmony_ci/* Maps a region from staging to service the transfer. */ 340bf215546Sopenharmony_cistatic void * 341bf215546Sopenharmony_civirgl_staging_map(struct virgl_context *vctx, 342bf215546Sopenharmony_ci struct virgl_transfer *vtransfer) 343bf215546Sopenharmony_ci{ 344bf215546Sopenharmony_ci struct virgl_resource *vres = virgl_resource(vtransfer->base.resource); 345bf215546Sopenharmony_ci unsigned size; 346bf215546Sopenharmony_ci unsigned align_offset; 347bf215546Sopenharmony_ci unsigned stride; 348bf215546Sopenharmony_ci unsigned layer_stride; 349bf215546Sopenharmony_ci void *map_addr; 350bf215546Sopenharmony_ci bool alloc_succeeded; 351bf215546Sopenharmony_ci 352bf215546Sopenharmony_ci assert(vctx->supports_staging); 353bf215546Sopenharmony_ci 354bf215546Sopenharmony_ci size = virgl_transfer_map_size(vtransfer, &stride, &layer_stride); 355bf215546Sopenharmony_ci 356bf215546Sopenharmony_ci /* For buffers we need to ensure that the start of the buffer would be 357bf215546Sopenharmony_ci * aligned to VIRGL_MAP_BUFFER_ALIGNMENT, even if our transfer doesn't 358bf215546Sopenharmony_ci * actually include it. To achieve this we may need to allocate a slightly 359bf215546Sopenharmony_ci * larger range from the upload buffer, and later update the uploader 360bf215546Sopenharmony_ci * resource offset and map address to point to the requested x coordinate 361bf215546Sopenharmony_ci * within that range. 362bf215546Sopenharmony_ci * 363bf215546Sopenharmony_ci * 0 A 2A 3A 364bf215546Sopenharmony_ci * |-------|---bbbb|bbbbb--| 365bf215546Sopenharmony_ci * |--------| ==> size 366bf215546Sopenharmony_ci * |---| ==> align_offset 367bf215546Sopenharmony_ci * |------------| ==> allocation of size + align_offset 368bf215546Sopenharmony_ci */ 369bf215546Sopenharmony_ci align_offset = vres->b.target == PIPE_BUFFER ? 370bf215546Sopenharmony_ci vtransfer->base.box.x % VIRGL_MAP_BUFFER_ALIGNMENT : 371bf215546Sopenharmony_ci 0; 372bf215546Sopenharmony_ci 373bf215546Sopenharmony_ci alloc_succeeded = 374bf215546Sopenharmony_ci virgl_staging_alloc(&vctx->staging, size + align_offset, 375bf215546Sopenharmony_ci VIRGL_MAP_BUFFER_ALIGNMENT, 376bf215546Sopenharmony_ci &vtransfer->copy_src_offset, 377bf215546Sopenharmony_ci &vtransfer->copy_src_hw_res, 378bf215546Sopenharmony_ci &map_addr); 379bf215546Sopenharmony_ci if (alloc_succeeded) { 380bf215546Sopenharmony_ci /* Update source offset and address to point to the requested x coordinate 381bf215546Sopenharmony_ci * if we have an align_offset (see above for more information). */ 382bf215546Sopenharmony_ci vtransfer->copy_src_offset += align_offset; 383bf215546Sopenharmony_ci map_addr += align_offset; 384bf215546Sopenharmony_ci 385bf215546Sopenharmony_ci /* Mark as dirty, since we are updating the host side resource 386bf215546Sopenharmony_ci * without going through the corresponding guest side resource, and 387bf215546Sopenharmony_ci * hence the two will diverge. 388bf215546Sopenharmony_ci */ 389bf215546Sopenharmony_ci virgl_resource_dirty(vres, vtransfer->base.level); 390bf215546Sopenharmony_ci 391bf215546Sopenharmony_ci /* We are using the minimum required size to hold the contents, 392bf215546Sopenharmony_ci * possibly using a layout different from the layout of the resource, 393bf215546Sopenharmony_ci * so update the transfer strides accordingly. 394bf215546Sopenharmony_ci */ 395bf215546Sopenharmony_ci vtransfer->base.stride = stride; 396bf215546Sopenharmony_ci vtransfer->base.layer_stride = layer_stride; 397bf215546Sopenharmony_ci 398bf215546Sopenharmony_ci /* Track the total size of active staging resources. */ 399bf215546Sopenharmony_ci vctx->queued_staging_res_size += size + align_offset; 400bf215546Sopenharmony_ci } 401bf215546Sopenharmony_ci 402bf215546Sopenharmony_ci return map_addr; 403bf215546Sopenharmony_ci} 404bf215546Sopenharmony_ci 405bf215546Sopenharmony_ci/* Maps a region from staging to service the transfer from host. 406bf215546Sopenharmony_ci * This function should be called only for texture readbacks 407bf215546Sopenharmony_ci * from host. */ 408bf215546Sopenharmony_cistatic void * 409bf215546Sopenharmony_civirgl_staging_read_map(struct virgl_context *vctx, 410bf215546Sopenharmony_ci struct virgl_transfer *vtransfer) 411bf215546Sopenharmony_ci{ 412bf215546Sopenharmony_ci struct virgl_screen *vscreen = virgl_screen(vctx->base.screen); 413bf215546Sopenharmony_ci struct virgl_winsys *vws = vscreen->vws; 414bf215546Sopenharmony_ci assert(vtransfer->base.resource->target != PIPE_BUFFER); 415bf215546Sopenharmony_ci void *map_addr; 416bf215546Sopenharmony_ci 417bf215546Sopenharmony_ci /* There are two possibilities to perform readback via: 418bf215546Sopenharmony_ci * a) calling transfer_get(); 419bf215546Sopenharmony_ci * b) calling submit_cmd() with encoded transfer inside cmd. 420bf215546Sopenharmony_ci * 421bf215546Sopenharmony_ci * For b) we need: 422bf215546Sopenharmony_ci * 1. select offset from staging buffer 423bf215546Sopenharmony_ci * 2. encode this transfer in wire 424bf215546Sopenharmony_ci * 3. flush the execbuffer to the host 425bf215546Sopenharmony_ci * 4. wait till copy on the host is done 426bf215546Sopenharmony_ci */ 427bf215546Sopenharmony_ci map_addr = virgl_staging_map(vctx, vtransfer); 428bf215546Sopenharmony_ci vtransfer->direction = VIRGL_TRANSFER_FROM_HOST; 429bf215546Sopenharmony_ci virgl_encode_copy_transfer(vctx, vtransfer); 430bf215546Sopenharmony_ci vctx->base.flush(&vctx->base, NULL, 0); 431bf215546Sopenharmony_ci vws->resource_wait(vws, vtransfer->copy_src_hw_res); 432bf215546Sopenharmony_ci return map_addr; 433bf215546Sopenharmony_ci} 434bf215546Sopenharmony_ci 435bf215546Sopenharmony_cistatic bool 436bf215546Sopenharmony_civirgl_resource_realloc(struct virgl_context *vctx, struct virgl_resource *res) 437bf215546Sopenharmony_ci{ 438bf215546Sopenharmony_ci struct virgl_screen *vs = virgl_screen(vctx->base.screen); 439bf215546Sopenharmony_ci const struct pipe_resource *templ = &res->b; 440bf215546Sopenharmony_ci unsigned vbind, vflags; 441bf215546Sopenharmony_ci struct virgl_hw_res *hw_res; 442bf215546Sopenharmony_ci 443bf215546Sopenharmony_ci vbind = pipe_to_virgl_bind(vs, templ->bind); 444bf215546Sopenharmony_ci vflags = pipe_to_virgl_flags(vs, templ->flags); 445bf215546Sopenharmony_ci 446bf215546Sopenharmony_ci int alloc_size = res->use_staging ? 1 : res->metadata.total_size; 447bf215546Sopenharmony_ci 448bf215546Sopenharmony_ci hw_res = vs->vws->resource_create(vs->vws, 449bf215546Sopenharmony_ci templ->target, 450bf215546Sopenharmony_ci NULL, 451bf215546Sopenharmony_ci templ->format, 452bf215546Sopenharmony_ci vbind, 453bf215546Sopenharmony_ci templ->width0, 454bf215546Sopenharmony_ci templ->height0, 455bf215546Sopenharmony_ci templ->depth0, 456bf215546Sopenharmony_ci templ->array_size, 457bf215546Sopenharmony_ci templ->last_level, 458bf215546Sopenharmony_ci templ->nr_samples, 459bf215546Sopenharmony_ci vflags, 460bf215546Sopenharmony_ci alloc_size); 461bf215546Sopenharmony_ci if (!hw_res) 462bf215546Sopenharmony_ci return false; 463bf215546Sopenharmony_ci 464bf215546Sopenharmony_ci vs->vws->resource_reference(vs->vws, &res->hw_res, NULL); 465bf215546Sopenharmony_ci res->hw_res = hw_res; 466bf215546Sopenharmony_ci 467bf215546Sopenharmony_ci /* We can safely clear the range here, since it will be repopulated in the 468bf215546Sopenharmony_ci * following rebind operation, according to the active buffer binds. 469bf215546Sopenharmony_ci */ 470bf215546Sopenharmony_ci util_range_set_empty(&res->valid_buffer_range); 471bf215546Sopenharmony_ci 472bf215546Sopenharmony_ci /* count toward the staging resource size limit */ 473bf215546Sopenharmony_ci vctx->queued_staging_res_size += res->metadata.total_size; 474bf215546Sopenharmony_ci 475bf215546Sopenharmony_ci virgl_rebind_resource(vctx, &res->b); 476bf215546Sopenharmony_ci 477bf215546Sopenharmony_ci return true; 478bf215546Sopenharmony_ci} 479bf215546Sopenharmony_ci 480bf215546Sopenharmony_civoid * 481bf215546Sopenharmony_civirgl_resource_transfer_map(struct pipe_context *ctx, 482bf215546Sopenharmony_ci struct pipe_resource *resource, 483bf215546Sopenharmony_ci unsigned level, 484bf215546Sopenharmony_ci unsigned usage, 485bf215546Sopenharmony_ci const struct pipe_box *box, 486bf215546Sopenharmony_ci struct pipe_transfer **transfer) 487bf215546Sopenharmony_ci{ 488bf215546Sopenharmony_ci struct virgl_context *vctx = virgl_context(ctx); 489bf215546Sopenharmony_ci struct virgl_screen *vscreen = virgl_screen(ctx->screen); 490bf215546Sopenharmony_ci struct virgl_winsys *vws = vscreen->vws; 491bf215546Sopenharmony_ci struct virgl_resource *vres = virgl_resource(resource); 492bf215546Sopenharmony_ci struct virgl_transfer *trans; 493bf215546Sopenharmony_ci enum virgl_transfer_map_type map_type; 494bf215546Sopenharmony_ci void *map_addr; 495bf215546Sopenharmony_ci 496bf215546Sopenharmony_ci /* Multisampled resources require resolve before mapping. */ 497bf215546Sopenharmony_ci assert(resource->nr_samples <= 1); 498bf215546Sopenharmony_ci 499bf215546Sopenharmony_ci /* If virgl resource was created using persistence and coherency flags, 500bf215546Sopenharmony_ci * then its memory mapping can be only made in accordance to these 501bf215546Sopenharmony_ci * flags. We record the "usage" flags in struct virgl_transfer and 502bf215546Sopenharmony_ci * then virgl_buffer_transfer_unmap() uses them to differentiate 503bf215546Sopenharmony_ci * unmapping of a host blob resource from guest. 504bf215546Sopenharmony_ci */ 505bf215546Sopenharmony_ci if (resource->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT) 506bf215546Sopenharmony_ci usage |= PIPE_MAP_PERSISTENT; 507bf215546Sopenharmony_ci 508bf215546Sopenharmony_ci if (resource->flags & PIPE_RESOURCE_FLAG_MAP_COHERENT) 509bf215546Sopenharmony_ci usage |= PIPE_MAP_COHERENT; 510bf215546Sopenharmony_ci 511bf215546Sopenharmony_ci trans = virgl_resource_create_transfer(vctx, resource, 512bf215546Sopenharmony_ci &vres->metadata, level, usage, box); 513bf215546Sopenharmony_ci 514bf215546Sopenharmony_ci map_type = virgl_resource_transfer_prepare(vctx, trans); 515bf215546Sopenharmony_ci switch (map_type) { 516bf215546Sopenharmony_ci case VIRGL_TRANSFER_MAP_REALLOC: 517bf215546Sopenharmony_ci if (!virgl_resource_realloc(vctx, vres)) { 518bf215546Sopenharmony_ci map_addr = NULL; 519bf215546Sopenharmony_ci break; 520bf215546Sopenharmony_ci } 521bf215546Sopenharmony_ci vws->resource_reference(vws, &trans->hw_res, vres->hw_res); 522bf215546Sopenharmony_ci FALLTHROUGH; 523bf215546Sopenharmony_ci case VIRGL_TRANSFER_MAP_HW_RES: 524bf215546Sopenharmony_ci trans->hw_res_map = vws->resource_map(vws, vres->hw_res); 525bf215546Sopenharmony_ci if (trans->hw_res_map) 526bf215546Sopenharmony_ci map_addr = trans->hw_res_map + trans->offset; 527bf215546Sopenharmony_ci else 528bf215546Sopenharmony_ci map_addr = NULL; 529bf215546Sopenharmony_ci break; 530bf215546Sopenharmony_ci case VIRGL_TRANSFER_MAP_WRITE_TO_STAGING: 531bf215546Sopenharmony_ci map_addr = virgl_staging_map(vctx, trans); 532bf215546Sopenharmony_ci /* Copy transfers don't make use of hw_res_map at the moment. */ 533bf215546Sopenharmony_ci trans->hw_res_map = NULL; 534bf215546Sopenharmony_ci trans->direction = VIRGL_TRANSFER_TO_HOST; 535bf215546Sopenharmony_ci break; 536bf215546Sopenharmony_ci case VIRGL_TRANSFER_MAP_READ_FROM_STAGING: 537bf215546Sopenharmony_ci map_addr = virgl_staging_read_map(vctx, trans); 538bf215546Sopenharmony_ci /* Copy transfers don't make use of hw_res_map at the moment. */ 539bf215546Sopenharmony_ci trans->hw_res_map = NULL; 540bf215546Sopenharmony_ci break; 541bf215546Sopenharmony_ci case VIRGL_TRANSFER_MAP_WRITE_TO_STAGING_WITH_READBACK: 542bf215546Sopenharmony_ci map_addr = virgl_staging_read_map(vctx, trans); 543bf215546Sopenharmony_ci /* Copy transfers don't make use of hw_res_map at the moment. */ 544bf215546Sopenharmony_ci trans->hw_res_map = NULL; 545bf215546Sopenharmony_ci trans->direction = VIRGL_TRANSFER_TO_HOST; 546bf215546Sopenharmony_ci break; 547bf215546Sopenharmony_ci case VIRGL_TRANSFER_MAP_ERROR: 548bf215546Sopenharmony_ci default: 549bf215546Sopenharmony_ci trans->hw_res_map = NULL; 550bf215546Sopenharmony_ci map_addr = NULL; 551bf215546Sopenharmony_ci break; 552bf215546Sopenharmony_ci } 553bf215546Sopenharmony_ci 554bf215546Sopenharmony_ci if (!map_addr) { 555bf215546Sopenharmony_ci virgl_resource_destroy_transfer(vctx, trans); 556bf215546Sopenharmony_ci return NULL; 557bf215546Sopenharmony_ci } 558bf215546Sopenharmony_ci 559bf215546Sopenharmony_ci if (vres->b.target == PIPE_BUFFER) { 560bf215546Sopenharmony_ci /* For the checks below to be able to use 'usage', we assume that 561bf215546Sopenharmony_ci * transfer preparation doesn't affect the usage. 562bf215546Sopenharmony_ci */ 563bf215546Sopenharmony_ci assert(usage == trans->base.usage); 564bf215546Sopenharmony_ci 565bf215546Sopenharmony_ci /* If we are doing a whole resource discard with a hw_res map, the buffer 566bf215546Sopenharmony_ci * storage can now be considered unused and we don't care about previous 567bf215546Sopenharmony_ci * contents. We can thus mark the storage as uninitialized, but only if 568bf215546Sopenharmony_ci * the buffer is not host writable (in which case we can't clear the 569bf215546Sopenharmony_ci * valid range, since that would result in missed readbacks in future 570bf215546Sopenharmony_ci * transfers). We only do this for VIRGL_TRANSFER_MAP_HW_RES, since for 571bf215546Sopenharmony_ci * VIRGL_TRANSFER_MAP_REALLOC we already take care of the buffer range 572bf215546Sopenharmony_ci * when reallocating and rebinding, and VIRGL_TRANSFER_MAP_STAGING is not 573bf215546Sopenharmony_ci * currently used for whole resource discards. 574bf215546Sopenharmony_ci */ 575bf215546Sopenharmony_ci if (map_type == VIRGL_TRANSFER_MAP_HW_RES && 576bf215546Sopenharmony_ci (usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) && 577bf215546Sopenharmony_ci (vres->clean_mask & 1)) { 578bf215546Sopenharmony_ci util_range_set_empty(&vres->valid_buffer_range); 579bf215546Sopenharmony_ci } 580bf215546Sopenharmony_ci 581bf215546Sopenharmony_ci if (usage & PIPE_MAP_WRITE) 582bf215546Sopenharmony_ci util_range_add(&vres->b, &vres->valid_buffer_range, box->x, box->x + box->width); 583bf215546Sopenharmony_ci } 584bf215546Sopenharmony_ci 585bf215546Sopenharmony_ci *transfer = &trans->base; 586bf215546Sopenharmony_ci return map_addr; 587bf215546Sopenharmony_ci} 588bf215546Sopenharmony_ci 589bf215546Sopenharmony_cistatic void virgl_resource_layout(struct pipe_resource *pt, 590bf215546Sopenharmony_ci struct virgl_resource_metadata *metadata, 591bf215546Sopenharmony_ci uint32_t plane, 592bf215546Sopenharmony_ci uint32_t winsys_stride, 593bf215546Sopenharmony_ci uint32_t plane_offset, 594bf215546Sopenharmony_ci uint64_t modifier) 595bf215546Sopenharmony_ci{ 596bf215546Sopenharmony_ci unsigned level, nblocksy; 597bf215546Sopenharmony_ci unsigned width = pt->width0; 598bf215546Sopenharmony_ci unsigned height = pt->height0; 599bf215546Sopenharmony_ci unsigned depth = pt->depth0; 600bf215546Sopenharmony_ci unsigned buffer_size = 0; 601bf215546Sopenharmony_ci 602bf215546Sopenharmony_ci for (level = 0; level <= pt->last_level; level++) { 603bf215546Sopenharmony_ci unsigned slices; 604bf215546Sopenharmony_ci 605bf215546Sopenharmony_ci if (pt->target == PIPE_TEXTURE_CUBE) 606bf215546Sopenharmony_ci slices = 6; 607bf215546Sopenharmony_ci else if (pt->target == PIPE_TEXTURE_3D) 608bf215546Sopenharmony_ci slices = depth; 609bf215546Sopenharmony_ci else 610bf215546Sopenharmony_ci slices = pt->array_size; 611bf215546Sopenharmony_ci 612bf215546Sopenharmony_ci nblocksy = util_format_get_nblocksy(pt->format, height); 613bf215546Sopenharmony_ci metadata->stride[level] = winsys_stride ? winsys_stride : 614bf215546Sopenharmony_ci util_format_get_stride(pt->format, width); 615bf215546Sopenharmony_ci metadata->layer_stride[level] = nblocksy * metadata->stride[level]; 616bf215546Sopenharmony_ci metadata->level_offset[level] = buffer_size; 617bf215546Sopenharmony_ci 618bf215546Sopenharmony_ci buffer_size += slices * metadata->layer_stride[level]; 619bf215546Sopenharmony_ci 620bf215546Sopenharmony_ci width = u_minify(width, 1); 621bf215546Sopenharmony_ci height = u_minify(height, 1); 622bf215546Sopenharmony_ci depth = u_minify(depth, 1); 623bf215546Sopenharmony_ci } 624bf215546Sopenharmony_ci 625bf215546Sopenharmony_ci metadata->plane = plane; 626bf215546Sopenharmony_ci metadata->plane_offset = plane_offset; 627bf215546Sopenharmony_ci metadata->modifier = modifier; 628bf215546Sopenharmony_ci if (pt->nr_samples <= 1) 629bf215546Sopenharmony_ci metadata->total_size = buffer_size; 630bf215546Sopenharmony_ci else /* don't create guest backing store for MSAA */ 631bf215546Sopenharmony_ci metadata->total_size = 0; 632bf215546Sopenharmony_ci} 633bf215546Sopenharmony_ci 634bf215546Sopenharmony_cistatic struct pipe_resource *virgl_resource_create_front(struct pipe_screen *screen, 635bf215546Sopenharmony_ci const struct pipe_resource *templ, 636bf215546Sopenharmony_ci const void *map_front_private) 637bf215546Sopenharmony_ci{ 638bf215546Sopenharmony_ci unsigned vbind, vflags; 639bf215546Sopenharmony_ci struct virgl_screen *vs = virgl_screen(screen); 640bf215546Sopenharmony_ci struct virgl_resource *res = CALLOC_STRUCT(virgl_resource); 641bf215546Sopenharmony_ci uint32_t alloc_size; 642bf215546Sopenharmony_ci 643bf215546Sopenharmony_ci res->b = *templ; 644bf215546Sopenharmony_ci res->b.screen = &vs->base; 645bf215546Sopenharmony_ci pipe_reference_init(&res->b.reference, 1); 646bf215546Sopenharmony_ci vbind = pipe_to_virgl_bind(vs, templ->bind); 647bf215546Sopenharmony_ci vflags = pipe_to_virgl_flags(vs, templ->flags); 648bf215546Sopenharmony_ci virgl_resource_layout(&res->b, &res->metadata, 0, 0, 0, 0); 649bf215546Sopenharmony_ci 650bf215546Sopenharmony_ci if ((vs->caps.caps.v2.capability_bits & VIRGL_CAP_APP_TWEAK_SUPPORT) && 651bf215546Sopenharmony_ci vs->tweak_gles_emulate_bgra && 652bf215546Sopenharmony_ci (templ->format == PIPE_FORMAT_B8G8R8A8_SRGB || 653bf215546Sopenharmony_ci templ->format == PIPE_FORMAT_B8G8R8A8_UNORM || 654bf215546Sopenharmony_ci templ->format == PIPE_FORMAT_B8G8R8X8_SRGB || 655bf215546Sopenharmony_ci templ->format == PIPE_FORMAT_B8G8R8X8_UNORM)) { 656bf215546Sopenharmony_ci vbind |= VIRGL_BIND_PREFER_EMULATED_BGRA; 657bf215546Sopenharmony_ci } 658bf215546Sopenharmony_ci 659bf215546Sopenharmony_ci // If renderer supports copy transfer from host, and we either have support 660bf215546Sopenharmony_ci // for then for textures alloc minimum size of bo 661bf215546Sopenharmony_ci // This size is not passed to the host 662bf215546Sopenharmony_ci res->use_staging = virgl_can_copy_transfer_from_host(vs, res, vbind); 663bf215546Sopenharmony_ci 664bf215546Sopenharmony_ci if (res->use_staging) 665bf215546Sopenharmony_ci alloc_size = 1; 666bf215546Sopenharmony_ci else 667bf215546Sopenharmony_ci alloc_size = res->metadata.total_size; 668bf215546Sopenharmony_ci 669bf215546Sopenharmony_ci res->hw_res = vs->vws->resource_create(vs->vws, templ->target, 670bf215546Sopenharmony_ci map_front_private, 671bf215546Sopenharmony_ci templ->format, vbind, 672bf215546Sopenharmony_ci templ->width0, 673bf215546Sopenharmony_ci templ->height0, 674bf215546Sopenharmony_ci templ->depth0, 675bf215546Sopenharmony_ci templ->array_size, 676bf215546Sopenharmony_ci templ->last_level, 677bf215546Sopenharmony_ci templ->nr_samples, 678bf215546Sopenharmony_ci vflags, 679bf215546Sopenharmony_ci alloc_size); 680bf215546Sopenharmony_ci if (!res->hw_res) { 681bf215546Sopenharmony_ci FREE(res); 682bf215546Sopenharmony_ci return NULL; 683bf215546Sopenharmony_ci } 684bf215546Sopenharmony_ci 685bf215546Sopenharmony_ci res->clean_mask = (1 << VR_MAX_TEXTURE_2D_LEVELS) - 1; 686bf215546Sopenharmony_ci 687bf215546Sopenharmony_ci if (templ->target == PIPE_BUFFER) { 688bf215546Sopenharmony_ci util_range_init(&res->valid_buffer_range); 689bf215546Sopenharmony_ci virgl_buffer_init(res); 690bf215546Sopenharmony_ci } else { 691bf215546Sopenharmony_ci virgl_texture_init(res); 692bf215546Sopenharmony_ci } 693bf215546Sopenharmony_ci 694bf215546Sopenharmony_ci return &res->b; 695bf215546Sopenharmony_ci 696bf215546Sopenharmony_ci} 697bf215546Sopenharmony_ci 698bf215546Sopenharmony_cistatic struct pipe_resource *virgl_resource_create(struct pipe_screen *screen, 699bf215546Sopenharmony_ci const struct pipe_resource *templ) 700bf215546Sopenharmony_ci{ 701bf215546Sopenharmony_ci return virgl_resource_create_front(screen, templ, NULL); 702bf215546Sopenharmony_ci} 703bf215546Sopenharmony_ci 704bf215546Sopenharmony_cistatic struct pipe_resource *virgl_resource_from_handle(struct pipe_screen *screen, 705bf215546Sopenharmony_ci const struct pipe_resource *templ, 706bf215546Sopenharmony_ci struct winsys_handle *whandle, 707bf215546Sopenharmony_ci unsigned usage) 708bf215546Sopenharmony_ci{ 709bf215546Sopenharmony_ci uint32_t winsys_stride, plane_offset, plane; 710bf215546Sopenharmony_ci uint64_t modifier; 711bf215546Sopenharmony_ci struct virgl_screen *vs = virgl_screen(screen); 712bf215546Sopenharmony_ci if (templ->target == PIPE_BUFFER) 713bf215546Sopenharmony_ci return NULL; 714bf215546Sopenharmony_ci 715bf215546Sopenharmony_ci struct virgl_resource *res = CALLOC_STRUCT(virgl_resource); 716bf215546Sopenharmony_ci res->b = *templ; 717bf215546Sopenharmony_ci res->b.screen = &vs->base; 718bf215546Sopenharmony_ci pipe_reference_init(&res->b.reference, 1); 719bf215546Sopenharmony_ci 720bf215546Sopenharmony_ci plane = winsys_stride = plane_offset = modifier = 0; 721bf215546Sopenharmony_ci res->hw_res = vs->vws->resource_create_from_handle(vs->vws, whandle, 722bf215546Sopenharmony_ci &plane, 723bf215546Sopenharmony_ci &winsys_stride, 724bf215546Sopenharmony_ci &plane_offset, 725bf215546Sopenharmony_ci &modifier, 726bf215546Sopenharmony_ci &res->blob_mem); 727bf215546Sopenharmony_ci 728bf215546Sopenharmony_ci /* do not use winsys returns for guest storage info of classic resource */ 729bf215546Sopenharmony_ci if (!res->blob_mem) { 730bf215546Sopenharmony_ci winsys_stride = 0; 731bf215546Sopenharmony_ci plane_offset = 0; 732bf215546Sopenharmony_ci modifier = 0; 733bf215546Sopenharmony_ci } 734bf215546Sopenharmony_ci 735bf215546Sopenharmony_ci virgl_resource_layout(&res->b, &res->metadata, plane, winsys_stride, 736bf215546Sopenharmony_ci plane_offset, modifier); 737bf215546Sopenharmony_ci if (!res->hw_res) { 738bf215546Sopenharmony_ci FREE(res); 739bf215546Sopenharmony_ci return NULL; 740bf215546Sopenharmony_ci } 741bf215546Sopenharmony_ci 742bf215546Sopenharmony_ci /* assign blob resource a type in case it was created untyped */ 743bf215546Sopenharmony_ci if (res->blob_mem && plane == 0 && 744bf215546Sopenharmony_ci (vs->caps.caps.v2.capability_bits_v2 & VIRGL_CAP_V2_UNTYPED_RESOURCE)) { 745bf215546Sopenharmony_ci uint32_t plane_strides[VIRGL_MAX_PLANE_COUNT]; 746bf215546Sopenharmony_ci uint32_t plane_offsets[VIRGL_MAX_PLANE_COUNT]; 747bf215546Sopenharmony_ci uint32_t plane_count = 0; 748bf215546Sopenharmony_ci struct pipe_resource *iter = &res->b; 749bf215546Sopenharmony_ci 750bf215546Sopenharmony_ci do { 751bf215546Sopenharmony_ci struct virgl_resource *plane = virgl_resource(iter); 752bf215546Sopenharmony_ci 753bf215546Sopenharmony_ci /* must be a plain 2D texture sharing the same hw_res */ 754bf215546Sopenharmony_ci if (plane->b.target != PIPE_TEXTURE_2D || 755bf215546Sopenharmony_ci plane->b.depth0 != 1 || 756bf215546Sopenharmony_ci plane->b.array_size != 1 || 757bf215546Sopenharmony_ci plane->b.last_level != 0 || 758bf215546Sopenharmony_ci plane->b.nr_samples > 1 || 759bf215546Sopenharmony_ci plane->hw_res != res->hw_res || 760bf215546Sopenharmony_ci plane_count >= VIRGL_MAX_PLANE_COUNT) { 761bf215546Sopenharmony_ci vs->vws->resource_reference(vs->vws, &res->hw_res, NULL); 762bf215546Sopenharmony_ci FREE(res); 763bf215546Sopenharmony_ci return NULL; 764bf215546Sopenharmony_ci } 765bf215546Sopenharmony_ci 766bf215546Sopenharmony_ci plane_strides[plane_count] = plane->metadata.stride[0]; 767bf215546Sopenharmony_ci plane_offsets[plane_count] = plane->metadata.plane_offset; 768bf215546Sopenharmony_ci plane_count++; 769bf215546Sopenharmony_ci iter = iter->next; 770bf215546Sopenharmony_ci } while (iter); 771bf215546Sopenharmony_ci 772bf215546Sopenharmony_ci vs->vws->resource_set_type(vs->vws, 773bf215546Sopenharmony_ci res->hw_res, 774bf215546Sopenharmony_ci pipe_to_virgl_format(res->b.format), 775bf215546Sopenharmony_ci pipe_to_virgl_bind(vs, res->b.bind), 776bf215546Sopenharmony_ci res->b.width0, 777bf215546Sopenharmony_ci res->b.height0, 778bf215546Sopenharmony_ci usage, 779bf215546Sopenharmony_ci res->metadata.modifier, 780bf215546Sopenharmony_ci plane_count, 781bf215546Sopenharmony_ci plane_strides, 782bf215546Sopenharmony_ci plane_offsets); 783bf215546Sopenharmony_ci } 784bf215546Sopenharmony_ci 785bf215546Sopenharmony_ci virgl_texture_init(res); 786bf215546Sopenharmony_ci 787bf215546Sopenharmony_ci return &res->b; 788bf215546Sopenharmony_ci} 789bf215546Sopenharmony_ci 790bf215546Sopenharmony_civoid virgl_init_screen_resource_functions(struct pipe_screen *screen) 791bf215546Sopenharmony_ci{ 792bf215546Sopenharmony_ci screen->resource_create_front = virgl_resource_create_front; 793bf215546Sopenharmony_ci screen->resource_create = virgl_resource_create; 794bf215546Sopenharmony_ci screen->resource_from_handle = virgl_resource_from_handle; 795bf215546Sopenharmony_ci screen->resource_get_handle = virgl_resource_get_handle; 796bf215546Sopenharmony_ci screen->resource_destroy = virgl_resource_destroy; 797bf215546Sopenharmony_ci} 798bf215546Sopenharmony_ci 799bf215546Sopenharmony_cistatic void virgl_buffer_subdata(struct pipe_context *pipe, 800bf215546Sopenharmony_ci struct pipe_resource *resource, 801bf215546Sopenharmony_ci unsigned usage, unsigned offset, 802bf215546Sopenharmony_ci unsigned size, const void *data) 803bf215546Sopenharmony_ci{ 804bf215546Sopenharmony_ci struct virgl_context *vctx = virgl_context(pipe); 805bf215546Sopenharmony_ci struct virgl_resource *vbuf = virgl_resource(resource); 806bf215546Sopenharmony_ci 807bf215546Sopenharmony_ci /* We can try virgl_transfer_queue_extend_buffer when there is no 808bf215546Sopenharmony_ci * flush/readback/wait required. Based on virgl_resource_transfer_prepare, 809bf215546Sopenharmony_ci * the simplest way to make sure that is the case is to check the valid 810bf215546Sopenharmony_ci * buffer range. 811bf215546Sopenharmony_ci */ 812bf215546Sopenharmony_ci if (!util_ranges_intersect(&vbuf->valid_buffer_range, 813bf215546Sopenharmony_ci offset, offset + size) && 814bf215546Sopenharmony_ci likely(!(virgl_debug & VIRGL_DEBUG_XFER)) && 815bf215546Sopenharmony_ci virgl_transfer_queue_extend_buffer(&vctx->queue, 816bf215546Sopenharmony_ci vbuf->hw_res, offset, size, data)) { 817bf215546Sopenharmony_ci util_range_add(&vbuf->b, &vbuf->valid_buffer_range, offset, offset + size); 818bf215546Sopenharmony_ci return; 819bf215546Sopenharmony_ci } 820bf215546Sopenharmony_ci 821bf215546Sopenharmony_ci u_default_buffer_subdata(pipe, resource, usage, offset, size, data); 822bf215546Sopenharmony_ci} 823bf215546Sopenharmony_ci 824bf215546Sopenharmony_civoid virgl_init_context_resource_functions(struct pipe_context *ctx) 825bf215546Sopenharmony_ci{ 826bf215546Sopenharmony_ci ctx->buffer_map = virgl_resource_transfer_map; 827bf215546Sopenharmony_ci ctx->texture_map = virgl_texture_transfer_map; 828bf215546Sopenharmony_ci ctx->transfer_flush_region = virgl_buffer_transfer_flush_region; 829bf215546Sopenharmony_ci ctx->buffer_unmap = virgl_buffer_transfer_unmap; 830bf215546Sopenharmony_ci ctx->texture_unmap = virgl_texture_transfer_unmap; 831bf215546Sopenharmony_ci ctx->buffer_subdata = virgl_buffer_subdata; 832bf215546Sopenharmony_ci ctx->texture_subdata = u_default_texture_subdata; 833bf215546Sopenharmony_ci} 834bf215546Sopenharmony_ci 835bf215546Sopenharmony_ci 836bf215546Sopenharmony_cistruct virgl_transfer * 837bf215546Sopenharmony_civirgl_resource_create_transfer(struct virgl_context *vctx, 838bf215546Sopenharmony_ci struct pipe_resource *pres, 839bf215546Sopenharmony_ci const struct virgl_resource_metadata *metadata, 840bf215546Sopenharmony_ci unsigned level, unsigned usage, 841bf215546Sopenharmony_ci const struct pipe_box *box) 842bf215546Sopenharmony_ci{ 843bf215546Sopenharmony_ci struct virgl_winsys *vws = virgl_screen(vctx->base.screen)->vws; 844bf215546Sopenharmony_ci struct virgl_transfer *trans; 845bf215546Sopenharmony_ci enum pipe_format format = pres->format; 846bf215546Sopenharmony_ci const unsigned blocksy = box->y / util_format_get_blockheight(format); 847bf215546Sopenharmony_ci const unsigned blocksx = box->x / util_format_get_blockwidth(format); 848bf215546Sopenharmony_ci 849bf215546Sopenharmony_ci unsigned offset = metadata->plane_offset + metadata->level_offset[level]; 850bf215546Sopenharmony_ci if (pres->target == PIPE_TEXTURE_CUBE || 851bf215546Sopenharmony_ci pres->target == PIPE_TEXTURE_CUBE_ARRAY || 852bf215546Sopenharmony_ci pres->target == PIPE_TEXTURE_3D || 853bf215546Sopenharmony_ci pres->target == PIPE_TEXTURE_2D_ARRAY) { 854bf215546Sopenharmony_ci offset += box->z * metadata->layer_stride[level]; 855bf215546Sopenharmony_ci } 856bf215546Sopenharmony_ci else if (pres->target == PIPE_TEXTURE_1D_ARRAY) { 857bf215546Sopenharmony_ci offset += box->z * metadata->stride[level]; 858bf215546Sopenharmony_ci assert(box->y == 0); 859bf215546Sopenharmony_ci } else if (pres->target == PIPE_BUFFER) { 860bf215546Sopenharmony_ci assert(box->y == 0 && box->z == 0); 861bf215546Sopenharmony_ci } else { 862bf215546Sopenharmony_ci assert(box->z == 0); 863bf215546Sopenharmony_ci } 864bf215546Sopenharmony_ci 865bf215546Sopenharmony_ci offset += blocksy * metadata->stride[level]; 866bf215546Sopenharmony_ci offset += blocksx * util_format_get_blocksize(format); 867bf215546Sopenharmony_ci 868bf215546Sopenharmony_ci trans = slab_zalloc(&vctx->transfer_pool); 869bf215546Sopenharmony_ci if (!trans) 870bf215546Sopenharmony_ci return NULL; 871bf215546Sopenharmony_ci 872bf215546Sopenharmony_ci pipe_resource_reference(&trans->base.resource, pres); 873bf215546Sopenharmony_ci vws->resource_reference(vws, &trans->hw_res, virgl_resource(pres)->hw_res); 874bf215546Sopenharmony_ci 875bf215546Sopenharmony_ci trans->base.level = level; 876bf215546Sopenharmony_ci trans->base.usage = usage; 877bf215546Sopenharmony_ci trans->base.box = *box; 878bf215546Sopenharmony_ci trans->base.stride = metadata->stride[level]; 879bf215546Sopenharmony_ci trans->base.layer_stride = metadata->layer_stride[level]; 880bf215546Sopenharmony_ci trans->offset = offset; 881bf215546Sopenharmony_ci util_range_init(&trans->range); 882bf215546Sopenharmony_ci 883bf215546Sopenharmony_ci if (trans->base.resource->target != PIPE_TEXTURE_3D && 884bf215546Sopenharmony_ci trans->base.resource->target != PIPE_TEXTURE_CUBE && 885bf215546Sopenharmony_ci trans->base.resource->target != PIPE_TEXTURE_1D_ARRAY && 886bf215546Sopenharmony_ci trans->base.resource->target != PIPE_TEXTURE_2D_ARRAY && 887bf215546Sopenharmony_ci trans->base.resource->target != PIPE_TEXTURE_CUBE_ARRAY) 888bf215546Sopenharmony_ci trans->l_stride = 0; 889bf215546Sopenharmony_ci else 890bf215546Sopenharmony_ci trans->l_stride = trans->base.layer_stride; 891bf215546Sopenharmony_ci 892bf215546Sopenharmony_ci return trans; 893bf215546Sopenharmony_ci} 894bf215546Sopenharmony_ci 895bf215546Sopenharmony_civoid virgl_resource_destroy_transfer(struct virgl_context *vctx, 896bf215546Sopenharmony_ci struct virgl_transfer *trans) 897bf215546Sopenharmony_ci{ 898bf215546Sopenharmony_ci struct virgl_winsys *vws = virgl_screen(vctx->base.screen)->vws; 899bf215546Sopenharmony_ci 900bf215546Sopenharmony_ci vws->resource_reference(vws, &trans->copy_src_hw_res, NULL); 901bf215546Sopenharmony_ci 902bf215546Sopenharmony_ci util_range_destroy(&trans->range); 903bf215546Sopenharmony_ci vws->resource_reference(vws, &trans->hw_res, NULL); 904bf215546Sopenharmony_ci pipe_resource_reference(&trans->base.resource, NULL); 905bf215546Sopenharmony_ci slab_free(&vctx->transfer_pool, trans); 906bf215546Sopenharmony_ci} 907bf215546Sopenharmony_ci 908bf215546Sopenharmony_civoid virgl_resource_destroy(struct pipe_screen *screen, 909bf215546Sopenharmony_ci struct pipe_resource *resource) 910bf215546Sopenharmony_ci{ 911bf215546Sopenharmony_ci struct virgl_screen *vs = virgl_screen(screen); 912bf215546Sopenharmony_ci struct virgl_resource *res = virgl_resource(resource); 913bf215546Sopenharmony_ci 914bf215546Sopenharmony_ci if (res->b.target == PIPE_BUFFER) 915bf215546Sopenharmony_ci util_range_destroy(&res->valid_buffer_range); 916bf215546Sopenharmony_ci 917bf215546Sopenharmony_ci vs->vws->resource_reference(vs->vws, &res->hw_res, NULL); 918bf215546Sopenharmony_ci FREE(res); 919bf215546Sopenharmony_ci} 920bf215546Sopenharmony_ci 921bf215546Sopenharmony_cibool virgl_resource_get_handle(struct pipe_screen *screen, 922bf215546Sopenharmony_ci struct pipe_context *context, 923bf215546Sopenharmony_ci struct pipe_resource *resource, 924bf215546Sopenharmony_ci struct winsys_handle *whandle, 925bf215546Sopenharmony_ci unsigned usage) 926bf215546Sopenharmony_ci{ 927bf215546Sopenharmony_ci struct virgl_screen *vs = virgl_screen(screen); 928bf215546Sopenharmony_ci struct virgl_resource *res = virgl_resource(resource); 929bf215546Sopenharmony_ci 930bf215546Sopenharmony_ci if (res->b.target == PIPE_BUFFER) 931bf215546Sopenharmony_ci return false; 932bf215546Sopenharmony_ci 933bf215546Sopenharmony_ci return vs->vws->resource_get_handle(vs->vws, res->hw_res, 934bf215546Sopenharmony_ci res->metadata.stride[0], 935bf215546Sopenharmony_ci whandle); 936bf215546Sopenharmony_ci} 937bf215546Sopenharmony_ci 938bf215546Sopenharmony_civoid virgl_resource_dirty(struct virgl_resource *res, uint32_t level) 939bf215546Sopenharmony_ci{ 940bf215546Sopenharmony_ci if (res) { 941bf215546Sopenharmony_ci if (res->b.target == PIPE_BUFFER) 942bf215546Sopenharmony_ci res->clean_mask &= ~1; 943bf215546Sopenharmony_ci else 944bf215546Sopenharmony_ci res->clean_mask &= ~(1 << level); 945bf215546Sopenharmony_ci } 946bf215546Sopenharmony_ci} 947