virgl: track valid buffer range for transfer sync
[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 "virgl_context.h"
27 #include "virgl_resource.h"
28 #include "virgl_screen.h"
29
30 /* We need to flush to properly sync the transfer with the current cmdbuf.
31 * But there are cases where the flushing can be skipped:
32 *
33 * - synchronization is disabled
34 * - the resource is not referenced by the current cmdbuf
35 * - the current cmdbuf has no draw/compute command that accesses the
36 * resource (XXX there are also clear or blit commands)
37 */
38 static bool virgl_res_needs_flush(struct virgl_context *vctx,
39 struct virgl_transfer *trans)
40 {
41 struct virgl_winsys *vws = virgl_screen(vctx->base.screen)->vws;
42 struct virgl_resource *res = virgl_resource(trans->base.resource);
43
44 if (trans->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)
45 return false;
46
47 if (!vws->res_is_referenced(vws, vctx->cbuf, res->hw_res))
48 return false;
49
50 if (res->clean_mask & (1 << trans->base.level)) {
51 /* XXX Consider
52 *
53 * glCopyBufferSubData(src, dst, ...);
54 * glBufferSubData(src, ...);
55 *
56 * at the beginning of a cmdbuf. glBufferSubData will be incorrectly
57 * reordered before glCopyBufferSubData.
58 */
59 if (vctx->num_draws == 0 && vctx->num_compute == 0)
60 return false;
61 }
62
63 return true;
64 }
65
66 /* We need to read back from the host storage to make sure the guest storage
67 * is up-to-date. But there are cases where the readback can be skipped:
68 *
69 * - the content can be discarded
70 * - the host storage is read-only
71 *
72 * Note that PIPE_TRANSFER_WRITE without discard bits requires readback.
73 * PIPE_TRANSFER_READ becomes irrelevant. PIPE_TRANSFER_UNSYNCHRONIZED and
74 * PIPE_TRANSFER_FLUSH_EXPLICIT are also irrelevant.
75 */
76 static bool virgl_res_needs_readback(struct virgl_context *vctx,
77 struct virgl_resource *res,
78 unsigned usage, unsigned level)
79 {
80 if (usage & (PIPE_TRANSFER_DISCARD_RANGE |
81 PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE))
82 return false;
83
84 if (res->clean_mask & (1 << level))
85 return false;
86
87 return true;
88 }
89
90 enum virgl_transfer_map_type
91 virgl_resource_transfer_prepare(struct virgl_context *vctx,
92 struct virgl_transfer *xfer)
93 {
94 struct virgl_winsys *vws = virgl_screen(vctx->base.screen)->vws;
95 struct virgl_resource *res = virgl_resource(xfer->base.resource);
96 enum virgl_transfer_map_type map_type = VIRGL_TRANSFER_MAP_HW_RES;
97 bool flush;
98 bool readback;
99 bool wait;
100
101 /* there is no way to map the host storage currently */
102 if (xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY)
103 return VIRGL_TRANSFER_MAP_ERROR;
104
105 flush = virgl_res_needs_flush(vctx, xfer);
106 readback = virgl_res_needs_readback(vctx, res, xfer->base.usage,
107 xfer->base.level);
108
109 /* We need to wait for all cmdbufs, current or previous, that access the
110 * resource to finish, unless synchronization is disabled. Readback, which
111 * is yet another command and is transparent to the state trackers, should
112 * also be waited for.
113 */
114 wait = !(xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED) || readback;
115
116 /* When the transfer range consists of only uninitialized data, we can
117 * assume the GPU is not accessing the range and readback is unnecessary.
118 * We can proceed as if PIPE_TRANSFER_UNSYNCHRONIZED and
119 * PIPE_TRANSFER_DISCARD_RANGE are set.
120 */
121 if (res->u.b.target == PIPE_BUFFER &&
122 !util_ranges_intersect(&res->valid_buffer_range, xfer->base.box.x,
123 xfer->base.box.x + xfer->base.box.width)) {
124 flush = false;
125 readback = false;
126 wait = false;
127 }
128
129 /* XXX This is incorrect. Consider
130 *
131 * glTexImage2D(..., data1);
132 * glDrawArrays();
133 * glFlush();
134 * glTexImage2D(..., data2);
135 *
136 * readback and flush are both false in the second glTexImage2D call. The
137 * draw call might end up seeing data2. Same applies to buffers with
138 * glBufferSubData.
139 */
140 wait = flush || readback;
141
142 if (flush)
143 vctx->base.flush(&vctx->base, NULL, 0);
144
145 if (readback) {
146 vws->transfer_get(vws, res->hw_res, &xfer->base.box, xfer->base.stride,
147 xfer->l_stride, xfer->offset, xfer->base.level);
148 }
149
150 if (wait) {
151 /* fail the mapping after flush and readback so that it will succeed in
152 * the future
153 */
154 if ((xfer->base.usage & PIPE_TRANSFER_DONTBLOCK) &&
155 vws->resource_is_busy(vws, res->hw_res))
156 return VIRGL_TRANSFER_MAP_ERROR;
157
158 vws->resource_wait(vws, res->hw_res);
159 }
160
161 return map_type;
162 }
163
164 static struct pipe_resource *virgl_resource_create(struct pipe_screen *screen,
165 const struct pipe_resource *templ)
166 {
167 unsigned vbind;
168 struct virgl_screen *vs = virgl_screen(screen);
169 struct virgl_resource *res = CALLOC_STRUCT(virgl_resource);
170
171 res->u.b = *templ;
172 res->u.b.screen = &vs->base;
173 pipe_reference_init(&res->u.b.reference, 1);
174 vbind = pipe_to_virgl_bind(vs, templ->bind);
175 virgl_resource_layout(&res->u.b, &res->metadata);
176 res->hw_res = vs->vws->resource_create(vs->vws, templ->target,
177 templ->format, vbind,
178 templ->width0,
179 templ->height0,
180 templ->depth0,
181 templ->array_size,
182 templ->last_level,
183 templ->nr_samples,
184 res->metadata.total_size);
185 if (!res->hw_res) {
186 FREE(res);
187 return NULL;
188 }
189
190 res->clean_mask = (1 << VR_MAX_TEXTURE_2D_LEVELS) - 1;
191
192 if (templ->target == PIPE_BUFFER) {
193 util_range_init(&res->valid_buffer_range);
194 virgl_buffer_init(res);
195 } else {
196 virgl_texture_init(res);
197 }
198
199 return &res->u.b;
200
201 }
202
203 static struct pipe_resource *virgl_resource_from_handle(struct pipe_screen *screen,
204 const struct pipe_resource *templ,
205 struct winsys_handle *whandle,
206 unsigned usage)
207 {
208 struct virgl_screen *vs = virgl_screen(screen);
209 if (templ->target == PIPE_BUFFER)
210 return NULL;
211
212 struct virgl_resource *res = CALLOC_STRUCT(virgl_resource);
213 res->u.b = *templ;
214 res->u.b.screen = &vs->base;
215 pipe_reference_init(&res->u.b.reference, 1);
216
217 res->hw_res = vs->vws->resource_create_from_handle(vs->vws, whandle);
218 if (!res->hw_res) {
219 FREE(res);
220 return NULL;
221 }
222
223 virgl_texture_init(res);
224
225 return &res->u.b;
226 }
227
228 void virgl_init_screen_resource_functions(struct pipe_screen *screen)
229 {
230 screen->resource_create = virgl_resource_create;
231 screen->resource_from_handle = virgl_resource_from_handle;
232 screen->resource_get_handle = u_resource_get_handle_vtbl;
233 screen->resource_destroy = u_resource_destroy_vtbl;
234 }
235
236 static bool virgl_buffer_transfer_extend(struct pipe_context *ctx,
237 struct pipe_resource *resource,
238 unsigned usage,
239 const struct pipe_box *box,
240 const void *data)
241 {
242 struct virgl_context *vctx = virgl_context(ctx);
243 struct virgl_resource *vbuf = virgl_resource(resource);
244 struct virgl_transfer dummy_trans = { 0 };
245 bool flush;
246 struct virgl_transfer *queued;
247
248 /*
249 * Attempts to short circuit the entire process of mapping and unmapping
250 * a resource if there is an existing transfer that can be extended.
251 * Pessimestically falls back if a flush is required.
252 */
253 dummy_trans.base.resource = resource;
254 dummy_trans.base.usage = usage;
255 dummy_trans.base.box = *box;
256 dummy_trans.base.stride = vbuf->metadata.stride[0];
257 dummy_trans.base.layer_stride = vbuf->metadata.layer_stride[0];
258 dummy_trans.offset = box->x;
259
260 flush = virgl_res_needs_flush(vctx, &dummy_trans);
261 if (flush && util_ranges_intersect(&vbuf->valid_buffer_range,
262 box->x, box->x + box->width))
263 return false;
264
265 queued = virgl_transfer_queue_extend(&vctx->queue, &dummy_trans);
266 if (!queued || !queued->hw_res_map)
267 return false;
268
269 memcpy(queued->hw_res_map + dummy_trans.offset, data, box->width);
270 util_range_add(&vbuf->valid_buffer_range, box->x, box->x + box->width);
271
272 return true;
273 }
274
275 static void virgl_buffer_subdata(struct pipe_context *pipe,
276 struct pipe_resource *resource,
277 unsigned usage, unsigned offset,
278 unsigned size, const void *data)
279 {
280 struct pipe_transfer *transfer;
281 uint8_t *map;
282 struct pipe_box box;
283
284 assert(!(usage & PIPE_TRANSFER_READ));
285
286 /* the write flag is implicit by the nature of buffer_subdata */
287 usage |= PIPE_TRANSFER_WRITE;
288
289 if (offset == 0 && size == resource->width0)
290 usage |= PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE;
291 else
292 usage |= PIPE_TRANSFER_DISCARD_RANGE;
293
294 u_box_1d(offset, size, &box);
295
296 if (usage & PIPE_TRANSFER_DISCARD_RANGE &&
297 virgl_buffer_transfer_extend(pipe, resource, usage, &box, data))
298 return;
299
300 map = pipe->transfer_map(pipe, resource, 0, usage, &box, &transfer);
301 if (map) {
302 memcpy(map, data, size);
303 pipe_transfer_unmap(pipe, transfer);
304 }
305 }
306
307 void virgl_init_context_resource_functions(struct pipe_context *ctx)
308 {
309 ctx->transfer_map = u_transfer_map_vtbl;
310 ctx->transfer_flush_region = u_transfer_flush_region_vtbl;
311 ctx->transfer_unmap = u_transfer_unmap_vtbl;
312 ctx->buffer_subdata = virgl_buffer_subdata;
313 ctx->texture_subdata = u_default_texture_subdata;
314 }
315
316 void virgl_resource_layout(struct pipe_resource *pt,
317 struct virgl_resource_metadata *metadata)
318 {
319 unsigned level, nblocksy;
320 unsigned width = pt->width0;
321 unsigned height = pt->height0;
322 unsigned depth = pt->depth0;
323 unsigned buffer_size = 0;
324
325 for (level = 0; level <= pt->last_level; level++) {
326 unsigned slices;
327
328 if (pt->target == PIPE_TEXTURE_CUBE)
329 slices = 6;
330 else if (pt->target == PIPE_TEXTURE_3D)
331 slices = depth;
332 else
333 slices = pt->array_size;
334
335 nblocksy = util_format_get_nblocksy(pt->format, height);
336 metadata->stride[level] = util_format_get_stride(pt->format, width);
337 metadata->layer_stride[level] = nblocksy * metadata->stride[level];
338 metadata->level_offset[level] = buffer_size;
339
340 buffer_size += slices * metadata->layer_stride[level];
341
342 width = u_minify(width, 1);
343 height = u_minify(height, 1);
344 depth = u_minify(depth, 1);
345 }
346
347 if (pt->nr_samples <= 1)
348 metadata->total_size = buffer_size;
349 else /* don't create guest backing store for MSAA */
350 metadata->total_size = 0;
351 }
352
353 struct virgl_transfer *
354 virgl_resource_create_transfer(struct slab_child_pool *pool,
355 struct pipe_resource *pres,
356 const struct virgl_resource_metadata *metadata,
357 unsigned level, unsigned usage,
358 const struct pipe_box *box)
359 {
360 struct virgl_transfer *trans;
361 enum pipe_format format = pres->format;
362 const unsigned blocksy = box->y / util_format_get_blockheight(format);
363 const unsigned blocksx = box->x / util_format_get_blockwidth(format);
364
365 unsigned offset = metadata->level_offset[level];
366 if (pres->target == PIPE_TEXTURE_CUBE ||
367 pres->target == PIPE_TEXTURE_CUBE_ARRAY ||
368 pres->target == PIPE_TEXTURE_3D ||
369 pres->target == PIPE_TEXTURE_2D_ARRAY) {
370 offset += box->z * metadata->layer_stride[level];
371 }
372 else if (pres->target == PIPE_TEXTURE_1D_ARRAY) {
373 offset += box->z * metadata->stride[level];
374 assert(box->y == 0);
375 } else if (pres->target == PIPE_BUFFER) {
376 assert(box->y == 0 && box->z == 0);
377 } else {
378 assert(box->z == 0);
379 }
380
381 offset += blocksy * metadata->stride[level];
382 offset += blocksx * util_format_get_blocksize(format);
383
384 trans = slab_alloc(pool);
385 if (!trans)
386 return NULL;
387
388 trans->base.resource = pres;
389 trans->base.level = level;
390 trans->base.usage = usage;
391 trans->base.box = *box;
392 trans->base.stride = metadata->stride[level];
393 trans->base.layer_stride = metadata->layer_stride[level];
394 trans->offset = offset;
395 util_range_init(&trans->range);
396
397 if (trans->base.resource->target != PIPE_TEXTURE_3D &&
398 trans->base.resource->target != PIPE_TEXTURE_CUBE &&
399 trans->base.resource->target != PIPE_TEXTURE_1D_ARRAY &&
400 trans->base.resource->target != PIPE_TEXTURE_2D_ARRAY &&
401 trans->base.resource->target != PIPE_TEXTURE_CUBE_ARRAY)
402 trans->l_stride = 0;
403 else
404 trans->l_stride = trans->base.layer_stride;
405
406 return trans;
407 }
408
409 void virgl_resource_destroy_transfer(struct slab_child_pool *pool,
410 struct virgl_transfer *trans)
411 {
412 util_range_destroy(&trans->range);
413 slab_free(pool, trans);
414 }
415
416 void virgl_resource_destroy(struct pipe_screen *screen,
417 struct pipe_resource *resource)
418 {
419 struct virgl_screen *vs = virgl_screen(screen);
420 struct virgl_resource *res = virgl_resource(resource);
421
422 if (res->u.b.target == PIPE_BUFFER)
423 util_range_destroy(&res->valid_buffer_range);
424
425 vs->vws->resource_unref(vs->vws, res->hw_res);
426 FREE(res);
427 }
428
429 boolean virgl_resource_get_handle(struct pipe_screen *screen,
430 struct pipe_resource *resource,
431 struct winsys_handle *whandle)
432 {
433 struct virgl_screen *vs = virgl_screen(screen);
434 struct virgl_resource *res = virgl_resource(resource);
435
436 if (res->u.b.target == PIPE_BUFFER)
437 return FALSE;
438
439 return vs->vws->resource_get_handle(vs->vws, res->hw_res,
440 res->metadata.stride[0],
441 whandle);
442 }
443
444 void virgl_resource_dirty(struct virgl_resource *res, uint32_t level)
445 {
446 if (res) {
447 if (res->u.b.target == PIPE_BUFFER)
448 res->clean_mask &= ~1;
449 else
450 res->clean_mask &= ~(1 << level);
451 }
452 }