virgl: Use copy transfers for textures
[mesa.git] / src / gallium / drivers / virgl / virgl_texture.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
27 #include "virgl_context.h"
28 #include "virgl_encode.h"
29 #include "virgl_resource.h"
30 #include "virgl_screen.h"
31
32 static void virgl_copy_region_with_blit(struct pipe_context *pipe,
33 struct pipe_resource *dst,
34 unsigned dst_level,
35 const struct pipe_box *dst_box,
36 struct pipe_resource *src,
37 unsigned src_level,
38 const struct pipe_box *src_box)
39 {
40 struct pipe_blit_info blit;
41
42 memset(&blit, 0, sizeof(blit));
43 blit.src.resource = src;
44 blit.src.format = src->format;
45 blit.src.level = src_level;
46 blit.src.box = *src_box;
47 blit.dst.resource = dst;
48 blit.dst.format = dst->format;
49 blit.dst.level = dst_level;
50 blit.dst.box.x = dst_box->x;
51 blit.dst.box.y = dst_box->y;
52 blit.dst.box.z = dst_box->z;
53 blit.dst.box.width = dst_box->width;
54 blit.dst.box.height = dst_box->height;
55 blit.dst.box.depth = dst_box->depth;
56 blit.mask = util_format_get_mask(src->format) &
57 util_format_get_mask(dst->format);
58 blit.filter = PIPE_TEX_FILTER_NEAREST;
59
60 if (blit.mask) {
61 pipe->blit(pipe, &blit);
62 }
63 }
64
65 static unsigned temp_bind(unsigned orig)
66 {
67 unsigned warn = ~(PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL |
68 PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_DISPLAY_TARGET);
69 if (orig & warn)
70 debug_printf("VIRGL: Warning, possibly unhandled bind: %x\n",
71 orig & warn);
72
73 return orig & (PIPE_BIND_DEPTH_STENCIL | PIPE_BIND_RENDER_TARGET);
74 }
75
76 static void virgl_init_temp_resource_from_box(struct pipe_resource *res,
77 struct pipe_resource *orig,
78 const struct pipe_box *box,
79 unsigned level, unsigned flags,
80 enum pipe_format fmt)
81 {
82 memset(res, 0, sizeof(*res));
83 res->bind = temp_bind(orig->bind);
84 res->format = fmt;
85 res->width0 = box->width;
86 res->height0 = box->height;
87 res->depth0 = 1;
88 res->array_size = 1;
89 res->usage = PIPE_USAGE_STAGING;
90 res->flags = flags;
91
92 /* We must set the correct texture target and dimensions for a 3D box. */
93 if (box->depth > 1 && util_max_layer(orig, level) > 0)
94 res->target = orig->target;
95 else
96 res->target = PIPE_TEXTURE_2D;
97
98 if (res->target != PIPE_BUFFER)
99 res->bind = PIPE_BIND_RENDER_TARGET;
100
101 switch (res->target) {
102 case PIPE_TEXTURE_1D_ARRAY:
103 case PIPE_TEXTURE_2D_ARRAY:
104 case PIPE_TEXTURE_CUBE_ARRAY:
105 res->array_size = box->depth;
106 break;
107 case PIPE_TEXTURE_3D:
108 res->depth0 = box->depth;
109 break;
110 default:
111 break;
112 }
113 }
114
115 static void *texture_transfer_map_plain(struct pipe_context *ctx,
116 struct pipe_resource *resource,
117 unsigned level,
118 unsigned usage,
119 const struct pipe_box *box,
120 struct pipe_transfer **transfer)
121 {
122 struct virgl_context *vctx = virgl_context(ctx);
123 struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws;
124 struct virgl_resource *vtex = virgl_resource(resource);
125 struct virgl_transfer *trans;
126 enum virgl_transfer_map_type map_type;
127 void *map_addr;
128
129 trans = virgl_resource_create_transfer(&vctx->transfer_pool, resource,
130 &vtex->metadata, level, usage, box);
131 trans->resolve_transfer = NULL;
132
133 assert(resource->nr_samples <= 1);
134
135 map_type = virgl_resource_transfer_prepare(vctx, trans);
136 switch (map_type) {
137 case VIRGL_TRANSFER_MAP_HW_RES:
138 trans->hw_res_map = vws->resource_map(vws, vtex->hw_res);
139 if (trans->hw_res_map)
140 map_addr = trans->hw_res_map + trans->offset;
141 else
142 map_addr = NULL;
143 break;
144 case VIRGL_TRANSFER_MAP_STAGING:
145 map_addr = virgl_transfer_uploader_map(vctx, trans);
146 /* Copy transfers don't make use of hw_res_map at the moment. */
147 trans->hw_res_map = NULL;
148 break;
149 case VIRGL_TRANSFER_MAP_ERROR:
150 default:
151 trans->hw_res_map = NULL;
152 map_addr = NULL;
153 break;
154 }
155
156 if (!map_addr) {
157 virgl_resource_destroy_transfer(&vctx->transfer_pool, trans);
158 return NULL;
159 }
160
161 *transfer = &trans->base;
162 return map_addr;
163 }
164
165 static void *texture_transfer_map_resolve(struct pipe_context *ctx,
166 struct pipe_resource *resource,
167 unsigned level,
168 unsigned usage,
169 const struct pipe_box *box,
170 struct pipe_transfer **transfer)
171 {
172 struct virgl_context *vctx = virgl_context(ctx);
173 struct virgl_resource *vtex = virgl_resource(resource);
174 struct pipe_resource templ, *resolve_tmp;
175 struct virgl_transfer *trans;
176
177 trans = virgl_resource_create_transfer(&vctx->transfer_pool, resource,
178 &vtex->metadata, level, usage, box);
179 if (!trans)
180 return NULL;
181
182 enum pipe_format fmt = resource->format;
183 if (!virgl_has_readback_format(ctx->screen, fmt)) {
184 if (util_format_fits_8unorm(util_format_description(fmt)))
185 fmt = PIPE_FORMAT_R8G8B8A8_UNORM;
186 else if (util_format_is_pure_sint(fmt))
187 fmt = PIPE_FORMAT_R32G32B32A32_SINT;
188 else if (util_format_is_pure_uint(fmt))
189 fmt = PIPE_FORMAT_R32G32B32A32_UINT;
190 else
191 fmt = PIPE_FORMAT_R32G32B32A32_FLOAT;
192 assert(virgl_has_readback_format(ctx->screen, fmt));
193 }
194
195 struct pipe_box dst_box = *box;
196 dst_box.x = dst_box.y = dst_box.z = 0;
197 if (usage & PIPE_TRANSFER_READ) {
198 /* readback should scale to the block size */
199 dst_box.width = align(dst_box.width,
200 util_format_get_blockwidth(resource->format));
201 dst_box.height = align(dst_box.height,
202 util_format_get_blockheight(resource->format));
203 }
204
205 virgl_init_temp_resource_from_box(&templ, resource, &dst_box, level, 0, fmt);
206
207 resolve_tmp = ctx->screen->resource_create(ctx->screen, &templ);
208 if (!resolve_tmp)
209 return NULL;
210
211 if (usage & PIPE_TRANSFER_READ) {
212 virgl_copy_region_with_blit(ctx, resolve_tmp, 0, &dst_box, resource,
213 level, box);
214 ctx->flush(ctx, NULL, 0);
215 }
216
217 void *ptr = texture_transfer_map_plain(ctx, resolve_tmp, 0, usage, &dst_box,
218 &trans->resolve_transfer);
219 if (!ptr)
220 goto fail;
221
222 *transfer = &trans->base;
223 if (fmt == resource->format) {
224 trans->base.stride = trans->resolve_transfer->stride;
225 trans->base.layer_stride = trans->resolve_transfer->layer_stride;
226 return ptr;
227 } else {
228 if (usage & PIPE_TRANSFER_READ) {
229 struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws;
230 void *src = ptr;
231 ptr = vws->resource_map(vws, vtex->hw_res);
232 if (!ptr)
233 goto fail;
234
235 if (!util_format_translate_3d(resource->format,
236 ptr + vtex->metadata.level_offset[level],
237 trans->base.stride,
238 trans->base.layer_stride,
239 box->x, box->y, box->z,
240 fmt,
241 src,
242 trans->resolve_transfer->stride,
243 trans->resolve_transfer->layer_stride,
244 0, 0, 0,
245 dst_box.width,
246 dst_box.height,
247 dst_box.depth)) {
248 debug_printf("failed to translate format %s to %s\n",
249 util_format_short_name(fmt),
250 util_format_short_name(resource->format));
251 goto fail;
252 }
253 }
254
255 if ((usage & PIPE_TRANSFER_WRITE) == 0)
256 pipe_resource_reference(&trans->resolve_transfer->resource, NULL);
257
258 return ptr + trans->offset;
259 }
260
261 fail:
262 pipe_resource_reference(&resolve_tmp, NULL);
263 virgl_resource_destroy_transfer(&vctx->transfer_pool, trans);
264 return NULL;
265 }
266
267 static bool needs_resolve(struct pipe_screen *screen,
268 struct pipe_resource *resource, unsigned usage)
269 {
270 if (resource->nr_samples > 1)
271 return true;
272
273 if (usage & PIPE_TRANSFER_READ)
274 return !util_format_is_depth_or_stencil(resource->format) &&
275 !virgl_has_readback_format(screen, resource->format);
276
277 return false;
278 }
279
280 static void *virgl_texture_transfer_map(struct pipe_context *ctx,
281 struct pipe_resource *resource,
282 unsigned level,
283 unsigned usage,
284 const struct pipe_box *box,
285 struct pipe_transfer **transfer)
286 {
287 if (needs_resolve(ctx->screen, resource, usage))
288 return texture_transfer_map_resolve(ctx, resource, level, usage, box,
289 transfer);
290
291 return texture_transfer_map_plain(ctx, resource, level, usage, box, transfer);
292 }
293
294 static void flush_data(struct pipe_context *ctx,
295 struct virgl_transfer *trans,
296 const struct pipe_box *box)
297 {
298 struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws;
299 vws->transfer_put(vws, virgl_resource(trans->base.resource)->hw_res, box,
300 trans->base.stride, trans->l_stride, trans->offset,
301 trans->base.level);
302 }
303
304 static void virgl_texture_transfer_unmap(struct pipe_context *ctx,
305 struct pipe_transfer *transfer)
306 {
307 struct virgl_context *vctx = virgl_context(ctx);
308 struct virgl_transfer *trans = virgl_transfer(transfer);
309 struct virgl_screen *vs = virgl_screen(ctx->screen);
310 struct pipe_resource *res = transfer->resource;
311 bool queue_unmap = false;
312
313 /* We don't need to transfer the contents of staging buffers, since they
314 * don't have any host-side storage. */
315 if (pipe_to_virgl_bind(vs, res->bind, res->flags) == VIRGL_BIND_STAGING) {
316 virgl_resource_destroy_transfer(&vctx->transfer_pool, trans);
317 return;
318 }
319
320 if (transfer->usage & PIPE_TRANSFER_WRITE &&
321 (transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT) == 0) {
322
323 if (trans->resolve_transfer && (trans->base.resource->format ==
324 trans->resolve_transfer->resource->format)) {
325 flush_data(ctx, virgl_transfer(trans->resolve_transfer),
326 &trans->resolve_transfer->box);
327
328 /* FINISHME: In case the destination format isn't renderable here, the
329 * blit here will currently fail. This could for instance happen if the
330 * mapped resource is of a compressed format, and it's mapped with both
331 * read and write usage.
332 */
333
334 virgl_copy_region_with_blit(ctx,
335 trans->base.resource, trans->base.level,
336 &transfer->box,
337 trans->resolve_transfer->resource, 0,
338 &trans->resolve_transfer->box);
339 ctx->flush(ctx, NULL, 0);
340 } else
341 queue_unmap = true;
342 }
343
344 if (trans->resolve_transfer) {
345 pipe_resource_reference(&trans->resolve_transfer->resource, NULL);
346 virgl_resource_destroy_transfer(&vctx->transfer_pool,
347 virgl_transfer(trans->resolve_transfer));
348 }
349
350 if (queue_unmap) {
351 if (trans->copy_src_res) {
352 virgl_encode_copy_transfer(vctx, trans);
353 /* It's now safe for other mappings to use the transfer_uploader. */
354 vctx->transfer_uploader_in_use = false;
355 virgl_resource_destroy_transfer(&vctx->transfer_pool, trans);
356 } else {
357 virgl_transfer_queue_unmap(&vctx->queue, trans);
358 }
359 } else {
360 virgl_resource_destroy_transfer(&vctx->transfer_pool, trans);
361 }
362
363 }
364
365 static const struct u_resource_vtbl virgl_texture_vtbl =
366 {
367 virgl_resource_get_handle, /* get_handle */
368 virgl_resource_destroy, /* resource_destroy */
369 virgl_texture_transfer_map, /* transfer_map */
370 NULL, /* transfer_flush_region */
371 virgl_texture_transfer_unmap, /* transfer_unmap */
372 };
373
374 void virgl_texture_init(struct virgl_resource *res)
375 {
376 res->u.vtbl = &virgl_texture_vtbl;
377 }