virgl: Use copy transfers for textures
[mesa.git] / src / gallium / drivers / virgl / virgl_resource.c
1 /*
2 * Copyright 2014, 2015 Red Hat.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23 #include "util/u_format.h"
24 #include "util/u_inlines.h"
25 #include "util/u_memory.h"
26 #include "util/u_upload_mgr.h"
27 #include "virgl_context.h"
28 #include "virgl_resource.h"
29 #include "virgl_screen.h"
30
31 /* We need to flush to properly sync the transfer with the current cmdbuf.
32 * But there are cases where the flushing can be skipped:
33 *
34 * - synchronization is disabled
35 * - the resource is not referenced by the current cmdbuf
36 */
37 static bool virgl_res_needs_flush(struct virgl_context *vctx,
38 struct virgl_transfer *trans)
39 {
40 struct virgl_winsys *vws = virgl_screen(vctx->base.screen)->vws;
41 struct virgl_resource *res = virgl_resource(trans->base.resource);
42
43 if (trans->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)
44 return false;
45
46 if (!vws->res_is_referenced(vws, vctx->cbuf, res->hw_res))
47 return false;
48
49 return true;
50 }
51
52 /* We need to read back from the host storage to make sure the guest storage
53 * is up-to-date. But there are cases where the readback can be skipped:
54 *
55 * - the content can be discarded
56 * - the host storage is read-only
57 *
58 * Note that PIPE_TRANSFER_WRITE without discard bits requires readback.
59 * PIPE_TRANSFER_READ becomes irrelevant. PIPE_TRANSFER_UNSYNCHRONIZED and
60 * PIPE_TRANSFER_FLUSH_EXPLICIT are also irrelevant.
61 */
62 static bool virgl_res_needs_readback(struct virgl_context *vctx,
63 struct virgl_resource *res,
64 unsigned usage, unsigned level)
65 {
66 if (usage & (PIPE_TRANSFER_DISCARD_RANGE |
67 PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE))
68 return false;
69
70 if (res->clean_mask & (1 << level))
71 return false;
72
73 return true;
74 }
75
76 enum virgl_transfer_map_type
77 virgl_resource_transfer_prepare(struct virgl_context *vctx,
78 struct virgl_transfer *xfer)
79 {
80 struct virgl_screen *vs = virgl_screen(vctx->base.screen);
81 struct virgl_winsys *vws = vs->vws;
82 struct virgl_resource *res = virgl_resource(xfer->base.resource);
83 enum virgl_transfer_map_type map_type = VIRGL_TRANSFER_MAP_HW_RES;
84 bool unsynchronized = xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED;
85 bool discard = xfer->base.usage & (PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE |
86 PIPE_TRANSFER_DISCARD_RANGE);
87 bool flush;
88 bool readback;
89 bool wait;
90 bool copy_transfer;
91
92 /* there is no way to map the host storage currently */
93 if (xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY)
94 return VIRGL_TRANSFER_MAP_ERROR;
95
96 /* We break the logic down into four steps
97 *
98 * step 1: determine the required operations independently
99 * step 2: look for chances to skip the operations
100 * step 3: resolve dependencies between the operations
101 * step 4: execute the operations
102 */
103
104 flush = virgl_res_needs_flush(vctx, xfer);
105 readback = virgl_res_needs_readback(vctx, res, xfer->base.usage,
106 xfer->base.level);
107
108 /* Check if we should perform a copy transfer through the transfer_uploader. */
109 copy_transfer = discard &&
110 !readback &&
111 !unsynchronized &&
112 vctx->transfer_uploader &&
113 !vctx->transfer_uploader_in_use &&
114 (flush || vws->resource_is_busy(vws, res->hw_res));
115
116 /* We need to wait for all cmdbufs, current or previous, that access the
117 * resource to finish unless synchronization is disabled.
118 */
119 wait = !unsynchronized;
120
121 /* When the transfer range consists of only uninitialized data, we can
122 * assume the GPU is not accessing the range and readback is unnecessary.
123 * We can proceed as if PIPE_TRANSFER_UNSYNCHRONIZED and
124 * PIPE_TRANSFER_DISCARD_RANGE are set.
125 */
126 if (res->u.b.target == PIPE_BUFFER &&
127 !util_ranges_intersect(&res->valid_buffer_range, xfer->base.box.x,
128 xfer->base.box.x + xfer->base.box.width)) {
129 flush = false;
130 readback = false;
131 wait = false;
132 copy_transfer = false;
133 }
134
135 /* When performing a copy transfer there is no need to flush or wait for
136 * the target resource.
137 */
138 if (copy_transfer) {
139 flush = false;
140 wait = false;
141 }
142
143 /* readback has some implications */
144 if (readback) {
145 /* Readback is yet another command and is transparent to the state
146 * trackers. It should be waited for in all cases, including when
147 * PIPE_TRANSFER_UNSYNCHRONIZED is set.
148 */
149 wait = true;
150
151 /* When the transfer queue has pending writes to this transfer's region,
152 * we have to flush before readback.
153 */
154 if (!flush && virgl_transfer_queue_is_queued(&vctx->queue, xfer))
155 flush = true;
156 }
157
158 /* XXX This is incorrect and will be removed. Consider
159 *
160 * glTexImage2D(..., data1);
161 * glDrawArrays();
162 * glFlush();
163 * glTexImage2D(..., data2);
164 *
165 * readback and flush are both false in the second glTexImage2D call. The
166 * draw call might end up seeing data2. Same applies to buffers with
167 * glBufferSubData.
168 */
169 wait = flush || readback;
170
171 if (flush)
172 vctx->base.flush(&vctx->base, NULL, 0);
173
174 /* If we are not allowed to block, and we know that we will have to wait,
175 * either because the resource is busy, or because it will become busy due
176 * to a readback, return early to avoid performing an incomplete
177 * transfer_get. Such an incomplete transfer_get may finish at any time,
178 * during which another unsynchronized map could write to the resource
179 * contents, leaving the contents in an undefined state.
180 */
181 if ((xfer->base.usage & PIPE_TRANSFER_DONTBLOCK) &&
182 (readback || (wait && vws->resource_is_busy(vws, res->hw_res))))
183 return VIRGL_TRANSFER_MAP_ERROR;
184
185 if (readback) {
186 vws->transfer_get(vws, res->hw_res, &xfer->base.box, xfer->base.stride,
187 xfer->l_stride, xfer->offset, xfer->base.level);
188 }
189
190 if (wait)
191 vws->resource_wait(vws, res->hw_res);
192
193 if (copy_transfer)
194 map_type = VIRGL_TRANSFER_MAP_STAGING;
195
196 return map_type;
197 }
198
199 static struct pipe_resource *virgl_resource_create(struct pipe_screen *screen,
200 const struct pipe_resource *templ)
201 {
202 unsigned vbind;
203 struct virgl_screen *vs = virgl_screen(screen);
204 struct virgl_resource *res = CALLOC_STRUCT(virgl_resource);
205
206 res->u.b = *templ;
207 res->u.b.screen = &vs->base;
208 pipe_reference_init(&res->u.b.reference, 1);
209 vbind = pipe_to_virgl_bind(vs, templ->bind, templ->flags);
210 virgl_resource_layout(&res->u.b, &res->metadata);
211 res->hw_res = vs->vws->resource_create(vs->vws, templ->target,
212 templ->format, vbind,
213 templ->width0,
214 templ->height0,
215 templ->depth0,
216 templ->array_size,
217 templ->last_level,
218 templ->nr_samples,
219 res->metadata.total_size);
220 if (!res->hw_res) {
221 FREE(res);
222 return NULL;
223 }
224
225 res->clean_mask = (1 << VR_MAX_TEXTURE_2D_LEVELS) - 1;
226
227 if (templ->target == PIPE_BUFFER) {
228 util_range_init(&res->valid_buffer_range);
229 virgl_buffer_init(res);
230 } else {
231 virgl_texture_init(res);
232 }
233
234 return &res->u.b;
235
236 }
237
238 static struct pipe_resource *virgl_resource_from_handle(struct pipe_screen *screen,
239 const struct pipe_resource *templ,
240 struct winsys_handle *whandle,
241 unsigned usage)
242 {
243 struct virgl_screen *vs = virgl_screen(screen);
244 if (templ->target == PIPE_BUFFER)
245 return NULL;
246
247 struct virgl_resource *res = CALLOC_STRUCT(virgl_resource);
248 res->u.b = *templ;
249 res->u.b.screen = &vs->base;
250 pipe_reference_init(&res->u.b.reference, 1);
251
252 res->hw_res = vs->vws->resource_create_from_handle(vs->vws, whandle);
253 if (!res->hw_res) {
254 FREE(res);
255 return NULL;
256 }
257
258 virgl_texture_init(res);
259
260 return &res->u.b;
261 }
262
263 void virgl_init_screen_resource_functions(struct pipe_screen *screen)
264 {
265 screen->resource_create = virgl_resource_create;
266 screen->resource_from_handle = virgl_resource_from_handle;
267 screen->resource_get_handle = u_resource_get_handle_vtbl;
268 screen->resource_destroy = u_resource_destroy_vtbl;
269 }
270
271 static bool virgl_buffer_transfer_extend(struct pipe_context *ctx,
272 struct pipe_resource *resource,
273 unsigned usage,
274 const struct pipe_box *box,
275 const void *data)
276 {
277 struct virgl_context *vctx = virgl_context(ctx);
278 struct virgl_resource *vbuf = virgl_resource(resource);
279 struct virgl_transfer dummy_trans = { 0 };
280 bool flush;
281 struct virgl_transfer *queued;
282
283 /*
284 * Attempts to short circuit the entire process of mapping and unmapping
285 * a resource if there is an existing transfer that can be extended.
286 * Pessimestically falls back if a flush is required.
287 */
288 dummy_trans.base.resource = resource;
289 dummy_trans.base.usage = usage;
290 dummy_trans.base.box = *box;
291 dummy_trans.base.stride = vbuf->metadata.stride[0];
292 dummy_trans.base.layer_stride = vbuf->metadata.layer_stride[0];
293 dummy_trans.offset = box->x;
294
295 flush = virgl_res_needs_flush(vctx, &dummy_trans);
296 if (flush && util_ranges_intersect(&vbuf->valid_buffer_range,
297 box->x, box->x + box->width))
298 return false;
299
300 queued = virgl_transfer_queue_extend(&vctx->queue, &dummy_trans);
301 if (!queued || !queued->hw_res_map)
302 return false;
303
304 memcpy(queued->hw_res_map + dummy_trans.offset, data, box->width);
305 util_range_add(&vbuf->valid_buffer_range, box->x, box->x + box->width);
306
307 return true;
308 }
309
310 static void virgl_buffer_subdata(struct pipe_context *pipe,
311 struct pipe_resource *resource,
312 unsigned usage, unsigned offset,
313 unsigned size, const void *data)
314 {
315 struct pipe_transfer *transfer;
316 uint8_t *map;
317 struct pipe_box box;
318
319 assert(!(usage & PIPE_TRANSFER_READ));
320
321 /* the write flag is implicit by the nature of buffer_subdata */
322 usage |= PIPE_TRANSFER_WRITE;
323
324 if (offset == 0 && size == resource->width0)
325 usage |= PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE;
326 else
327 usage |= PIPE_TRANSFER_DISCARD_RANGE;
328
329 u_box_1d(offset, size, &box);
330
331 if (usage & PIPE_TRANSFER_DISCARD_RANGE &&
332 virgl_buffer_transfer_extend(pipe, resource, usage, &box, data))
333 return;
334
335 map = pipe->transfer_map(pipe, resource, 0, usage, &box, &transfer);
336 if (map) {
337 memcpy(map, data, size);
338 pipe_transfer_unmap(pipe, transfer);
339 }
340 }
341
342 void virgl_init_context_resource_functions(struct pipe_context *ctx)
343 {
344 ctx->transfer_map = u_transfer_map_vtbl;
345 ctx->transfer_flush_region = u_transfer_flush_region_vtbl;
346 ctx->transfer_unmap = u_transfer_unmap_vtbl;
347 ctx->buffer_subdata = virgl_buffer_subdata;
348 ctx->texture_subdata = u_default_texture_subdata;
349 }
350
351 void virgl_resource_layout(struct pipe_resource *pt,
352 struct virgl_resource_metadata *metadata)
353 {
354 unsigned level, nblocksy;
355 unsigned width = pt->width0;
356 unsigned height = pt->height0;
357 unsigned depth = pt->depth0;
358 unsigned buffer_size = 0;
359
360 for (level = 0; level <= pt->last_level; level++) {
361 unsigned slices;
362
363 if (pt->target == PIPE_TEXTURE_CUBE)
364 slices = 6;
365 else if (pt->target == PIPE_TEXTURE_3D)
366 slices = depth;
367 else
368 slices = pt->array_size;
369
370 nblocksy = util_format_get_nblocksy(pt->format, height);
371 metadata->stride[level] = util_format_get_stride(pt->format, width);
372 metadata->layer_stride[level] = nblocksy * metadata->stride[level];
373 metadata->level_offset[level] = buffer_size;
374
375 buffer_size += slices * metadata->layer_stride[level];
376
377 width = u_minify(width, 1);
378 height = u_minify(height, 1);
379 depth = u_minify(depth, 1);
380 }
381
382 if (pt->nr_samples <= 1)
383 metadata->total_size = buffer_size;
384 else /* don't create guest backing store for MSAA */
385 metadata->total_size = 0;
386 }
387
388 struct virgl_transfer *
389 virgl_resource_create_transfer(struct slab_child_pool *pool,
390 struct pipe_resource *pres,
391 const struct virgl_resource_metadata *metadata,
392 unsigned level, unsigned usage,
393 const struct pipe_box *box)
394 {
395 struct virgl_transfer *trans;
396 enum pipe_format format = pres->format;
397 const unsigned blocksy = box->y / util_format_get_blockheight(format);
398 const unsigned blocksx = box->x / util_format_get_blockwidth(format);
399
400 unsigned offset = metadata->level_offset[level];
401 if (pres->target == PIPE_TEXTURE_CUBE ||
402 pres->target == PIPE_TEXTURE_CUBE_ARRAY ||
403 pres->target == PIPE_TEXTURE_3D ||
404 pres->target == PIPE_TEXTURE_2D_ARRAY) {
405 offset += box->z * metadata->layer_stride[level];
406 }
407 else if (pres->target == PIPE_TEXTURE_1D_ARRAY) {
408 offset += box->z * metadata->stride[level];
409 assert(box->y == 0);
410 } else if (pres->target == PIPE_BUFFER) {
411 assert(box->y == 0 && box->z == 0);
412 } else {
413 assert(box->z == 0);
414 }
415
416 offset += blocksy * metadata->stride[level];
417 offset += blocksx * util_format_get_blocksize(format);
418
419 trans = slab_alloc(pool);
420 if (!trans)
421 return NULL;
422
423 trans->base.resource = pres;
424 trans->base.level = level;
425 trans->base.usage = usage;
426 trans->base.box = *box;
427 trans->base.stride = metadata->stride[level];
428 trans->base.layer_stride = metadata->layer_stride[level];
429 trans->offset = offset;
430 util_range_init(&trans->range);
431 trans->copy_src_res = NULL;
432 trans->copy_src_offset = 0;
433
434 if (trans->base.resource->target != PIPE_TEXTURE_3D &&
435 trans->base.resource->target != PIPE_TEXTURE_CUBE &&
436 trans->base.resource->target != PIPE_TEXTURE_1D_ARRAY &&
437 trans->base.resource->target != PIPE_TEXTURE_2D_ARRAY &&
438 trans->base.resource->target != PIPE_TEXTURE_CUBE_ARRAY)
439 trans->l_stride = 0;
440 else
441 trans->l_stride = trans->base.layer_stride;
442
443 return trans;
444 }
445
446 void virgl_resource_destroy_transfer(struct slab_child_pool *pool,
447 struct virgl_transfer *trans)
448 {
449 pipe_resource_reference(&trans->copy_src_res, NULL);
450 util_range_destroy(&trans->range);
451 slab_free(pool, trans);
452 }
453
454 void virgl_resource_destroy(struct pipe_screen *screen,
455 struct pipe_resource *resource)
456 {
457 struct virgl_screen *vs = virgl_screen(screen);
458 struct virgl_resource *res = virgl_resource(resource);
459
460 if (res->u.b.target == PIPE_BUFFER)
461 util_range_destroy(&res->valid_buffer_range);
462
463 vs->vws->resource_unref(vs->vws, res->hw_res);
464 FREE(res);
465 }
466
467 boolean virgl_resource_get_handle(struct pipe_screen *screen,
468 struct pipe_resource *resource,
469 struct winsys_handle *whandle)
470 {
471 struct virgl_screen *vs = virgl_screen(screen);
472 struct virgl_resource *res = virgl_resource(resource);
473
474 if (res->u.b.target == PIPE_BUFFER)
475 return FALSE;
476
477 return vs->vws->resource_get_handle(vs->vws, res->hw_res,
478 res->metadata.stride[0],
479 whandle);
480 }
481
482 void virgl_resource_dirty(struct virgl_resource *res, uint32_t level)
483 {
484 if (res) {
485 if (res->u.b.target == PIPE_BUFFER)
486 res->clean_mask &= ~1;
487 else
488 res->clean_mask &= ~(1 << level);
489 }
490 }
491
492 /* Calculate the minimum size of the memory required to service a resource
493 * transfer map. Also return the stride and layer_stride for the corresponding
494 * layout.
495 */
496 static unsigned virgl_transfer_map_size(struct virgl_transfer *vtransfer,
497 unsigned *out_stride,
498 unsigned *out_layer_stride)
499 {
500 struct pipe_resource *pres = vtransfer->base.resource;
501 struct pipe_box *box = &vtransfer->base.box;
502 unsigned stride;
503 unsigned layer_stride;
504 unsigned size;
505
506 assert(out_stride);
507 assert(out_layer_stride);
508
509 stride = util_format_get_stride(pres->format, box->width);
510 layer_stride = util_format_get_2d_size(pres->format, stride, box->height);
511
512 if (pres->target == PIPE_TEXTURE_CUBE ||
513 pres->target == PIPE_TEXTURE_CUBE_ARRAY ||
514 pres->target == PIPE_TEXTURE_3D ||
515 pres->target == PIPE_TEXTURE_2D_ARRAY) {
516 size = box->depth * layer_stride;
517 } else if (pres->target == PIPE_TEXTURE_1D_ARRAY) {
518 size = box->depth * stride;
519 } else {
520 size = layer_stride;
521 }
522
523 *out_stride = stride;
524 *out_layer_stride = layer_stride;
525
526 return size;
527 }
528
529 /* Maps a region from the transfer uploader to service the transfer. */
530 void *virgl_transfer_uploader_map(struct virgl_context *vctx,
531 struct virgl_transfer *vtransfer)
532 {
533 struct virgl_resource *vres = virgl_resource(vtransfer->base.resource);
534 unsigned size;
535 unsigned align_offset;
536 unsigned stride;
537 unsigned layer_stride;
538 void *map_addr;
539
540 assert(vctx->transfer_uploader);
541 assert(!vctx->transfer_uploader_in_use);
542
543 size = virgl_transfer_map_size(vtransfer, &stride, &layer_stride);
544
545 /* For buffers we need to ensure that the start of the buffer would be
546 * aligned to VIRGL_MAP_BUFFER_ALIGNMENT, even if our transfer doesn't
547 * actually include it. To achieve this we may need to allocate a slightly
548 * larger range from the upload buffer, and later update the uploader
549 * resource offset and map address to point to the requested x coordinate
550 * within that range.
551 *
552 * 0 A 2A 3A
553 * |-------|---bbbb|bbbbb--|
554 * |--------| ==> size
555 * |---| ==> align_offset
556 * |------------| ==> allocation of size + align_offset
557 */
558 align_offset = vres->u.b.target == PIPE_BUFFER ?
559 vtransfer->base.box.x % VIRGL_MAP_BUFFER_ALIGNMENT :
560 0;
561
562 u_upload_alloc(vctx->transfer_uploader, 0, size + align_offset,
563 VIRGL_MAP_BUFFER_ALIGNMENT,
564 &vtransfer->copy_src_offset,
565 &vtransfer->copy_src_res, &map_addr);
566 if (map_addr) {
567 /* Update source offset and address to point to the requested x coordinate
568 * if we have an align_offset (see above for more information). */
569 vtransfer->copy_src_offset += align_offset;
570 map_addr += align_offset;
571
572 /* Mark as dirty, since we are updating the host side resource
573 * without going through the corresponding guest side resource, and
574 * hence the two will diverge.
575 */
576 virgl_resource_dirty(vres, vtransfer->base.level);
577
578 /* The pointer returned by u_upload_alloc already has +offset
579 * applied. */
580 vctx->transfer_uploader_in_use = true;
581
582 /* We are using the minimum required size to hold the contents,
583 * possibly using a layout different from the layout of the resource,
584 * so update the transfer strides accordingly.
585 */
586 vtransfer->base.stride = stride;
587 vtransfer->base.layer_stride = layer_stride;
588 }
589
590 return map_addr;
591 }