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