• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_resource.h"
29 #include "virgl_screen.h"
30 
virgl_copy_region_with_blit(struct pipe_context * pipe,struct pipe_resource * dst,unsigned dst_level,unsigned dstx,unsigned dsty,unsigned dstz,struct pipe_resource * src,unsigned src_level,const struct pipe_box * src_box)31 static void virgl_copy_region_with_blit(struct pipe_context *pipe,
32                                         struct pipe_resource *dst,
33                                         unsigned dst_level,
34                                         unsigned dstx, unsigned dsty, unsigned dstz,
35                                         struct pipe_resource *src,
36                                         unsigned src_level,
37                                         const struct pipe_box *src_box)
38 {
39    struct pipe_blit_info blit;
40 
41    memset(&blit, 0, sizeof(blit));
42    blit.src.resource = src;
43    blit.src.format = src->format;
44    blit.src.level = src_level;
45    blit.src.box = *src_box;
46    blit.dst.resource = dst;
47    blit.dst.format = dst->format;
48    blit.dst.level = dst_level;
49    blit.dst.box.x = dstx;
50    blit.dst.box.y = dsty;
51    blit.dst.box.z = dstz;
52    blit.dst.box.width = src_box->width;
53    blit.dst.box.height = src_box->height;
54    blit.dst.box.depth = src_box->depth;
55    blit.mask = util_format_get_mask(src->format) &
56       util_format_get_mask(dst->format);
57    blit.filter = PIPE_TEX_FILTER_NEAREST;
58 
59    if (blit.mask) {
60       pipe->blit(pipe, &blit);
61    }
62 }
virgl_init_temp_resource_from_box(struct pipe_resource * res,struct pipe_resource * orig,const struct pipe_box * box,unsigned level,unsigned flags)63 static void virgl_init_temp_resource_from_box(struct pipe_resource *res,
64                                               struct pipe_resource *orig,
65                                               const struct pipe_box *box,
66                                               unsigned level, unsigned flags)
67 {
68    memset(res, 0, sizeof(*res));
69    res->format = orig->format;
70    res->width0 = box->width;
71    res->height0 = box->height;
72    res->depth0 = 1;
73    res->array_size = 1;
74    res->usage = PIPE_USAGE_STAGING;
75    res->flags = flags;
76 
77    /* We must set the correct texture target and dimensions for a 3D box. */
78    if (box->depth > 1 && util_max_layer(orig, level) > 0)
79       res->target = orig->target;
80    else
81       res->target = PIPE_TEXTURE_2D;
82 
83    switch (res->target) {
84    case PIPE_TEXTURE_1D_ARRAY:
85    case PIPE_TEXTURE_2D_ARRAY:
86    case PIPE_TEXTURE_CUBE_ARRAY:
87       res->array_size = box->depth;
88       break;
89    case PIPE_TEXTURE_3D:
90       res->depth0 = box->depth;
91       break;
92    default:
93       break;
94    }
95 }
96 
97 static unsigned
vrend_get_tex_image_offset(const struct virgl_texture * res,unsigned level,unsigned layer)98 vrend_get_tex_image_offset(const struct virgl_texture *res,
99                            unsigned level, unsigned layer)
100 {
101    const struct pipe_resource *pres = &res->base.u.b;
102    const unsigned hgt = u_minify(pres->height0, level);
103    const unsigned nblocksy = util_format_get_nblocksy(pres->format, hgt);
104    unsigned offset = res->level_offset[level];
105 
106    if (pres->target == PIPE_TEXTURE_CUBE ||
107        pres->target == PIPE_TEXTURE_CUBE_ARRAY ||
108        pres->target == PIPE_TEXTURE_3D ||
109        pres->target == PIPE_TEXTURE_2D_ARRAY) {
110       offset += layer * nblocksy * res->stride[level];
111    }
112    else if (pres->target == PIPE_TEXTURE_1D_ARRAY) {
113       offset += layer * res->stride[level];
114    }
115    else {
116       assert(layer == 0);
117    }
118 
119    return offset;
120 }
121 
virgl_texture_transfer_map(struct pipe_context * ctx,struct pipe_resource * resource,unsigned level,unsigned usage,const struct pipe_box * box,struct pipe_transfer ** transfer)122 static void *virgl_texture_transfer_map(struct pipe_context *ctx,
123                                         struct pipe_resource *resource,
124                                         unsigned level,
125                                         unsigned usage,
126                                         const struct pipe_box *box,
127                                         struct pipe_transfer **transfer)
128 {
129    struct virgl_context *vctx = virgl_context(ctx);
130    struct virgl_screen *vs = virgl_screen(ctx->screen);
131    struct virgl_texture *vtex = virgl_texture(resource);
132    enum pipe_format format = resource->format;
133    struct virgl_transfer *trans;
134    void *ptr;
135    boolean readback = TRUE;
136    uint32_t offset;
137    struct virgl_hw_res *hw_res;
138    const unsigned h = u_minify(vtex->base.u.b.height0, level);
139    const unsigned nblocksy = util_format_get_nblocksy(format, h);
140    bool is_depth = util_format_has_depth(util_format_description(resource->format));
141    uint32_t l_stride;
142    bool doflushwait;
143 
144    doflushwait = virgl_res_needs_flush_wait(vctx, &vtex->base, usage);
145    if (doflushwait)
146       ctx->flush(ctx, NULL, 0);
147 
148    trans = slab_alloc(&vctx->texture_transfer_pool);
149    if (!trans)
150       return NULL;
151 
152    trans->base.resource = resource;
153    trans->base.level = level;
154    trans->base.usage = usage;
155    trans->base.box = *box;
156    trans->base.stride = vtex->stride[level];
157    trans->base.layer_stride = trans->base.stride * nblocksy;
158 
159    if (resource->target != PIPE_TEXTURE_3D &&
160        resource->target != PIPE_TEXTURE_CUBE &&
161        resource->target != PIPE_TEXTURE_1D_ARRAY &&
162        resource->target != PIPE_TEXTURE_2D_ARRAY &&
163        resource->target != PIPE_TEXTURE_CUBE_ARRAY)
164       l_stride = 0;
165    else
166       l_stride = trans->base.layer_stride;
167 
168    if (is_depth && resource->nr_samples > 1) {
169       struct pipe_resource tmp_resource;
170       virgl_init_temp_resource_from_box(&tmp_resource, resource, box,
171                                         level, 0);
172 
173       trans->resolve_tmp = (struct virgl_resource *)ctx->screen->resource_create(ctx->screen, &tmp_resource);
174 
175       virgl_copy_region_with_blit(ctx, &trans->resolve_tmp->u.b, 0, 0, 0, 0, resource, level, box);
176       ctx->flush(ctx, NULL, 0);
177       /* we want to do a resolve blit into the temporary */
178       hw_res = trans->resolve_tmp->hw_res;
179       offset = 0;
180    } else {
181       offset = vrend_get_tex_image_offset(vtex, level, box->z);
182 
183       offset += box->y / util_format_get_blockheight(format) * trans->base.stride +
184       box->x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
185       hw_res = vtex->base.hw_res;
186       trans->resolve_tmp = NULL;
187    }
188 
189    readback = virgl_res_needs_readback(vctx, &vtex->base, usage);
190    if (readback)
191       vs->vws->transfer_get(vs->vws, hw_res, box, trans->base.stride, l_stride, offset, level);
192 
193    if (doflushwait || readback)
194       vs->vws->resource_wait(vs->vws, vtex->base.hw_res);
195 
196    ptr = vs->vws->resource_map(vs->vws, hw_res);
197    if (!ptr) {
198       return NULL;
199    }
200 
201    trans->offset = offset;
202    *transfer = &trans->base;
203 
204    return ptr + trans->offset;
205 }
206 
virgl_texture_transfer_unmap(struct pipe_context * ctx,struct pipe_transfer * transfer)207 static void virgl_texture_transfer_unmap(struct pipe_context *ctx,
208                                          struct pipe_transfer *transfer)
209 {
210    struct virgl_context *vctx = virgl_context(ctx);
211    struct virgl_transfer *trans = virgl_transfer(transfer);
212    struct virgl_texture *vtex = virgl_texture(transfer->resource);
213    uint32_t l_stride;
214 
215    if (transfer->resource->target != PIPE_TEXTURE_3D &&
216        transfer->resource->target != PIPE_TEXTURE_CUBE &&
217        transfer->resource->target != PIPE_TEXTURE_1D_ARRAY &&
218        transfer->resource->target != PIPE_TEXTURE_2D_ARRAY &&
219        transfer->resource->target != PIPE_TEXTURE_CUBE_ARRAY)
220       l_stride = 0;
221    else
222       l_stride = trans->base.layer_stride;
223 
224    if (trans->base.usage & PIPE_TRANSFER_WRITE) {
225       if (!(transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) {
226          struct virgl_screen *vs = virgl_screen(ctx->screen);
227          vtex->base.clean = FALSE;
228          vctx->num_transfers++;
229          vs->vws->transfer_put(vs->vws, vtex->base.hw_res,
230                                &transfer->box, trans->base.stride, l_stride, trans->offset, transfer->level);
231 
232       }
233    }
234 
235    if (trans->resolve_tmp)
236       pipe_resource_reference((struct pipe_resource **)&trans->resolve_tmp, NULL);
237 
238    slab_free(&vctx->texture_transfer_pool, trans);
239 }
240 
241 
242 static boolean
vrend_resource_layout(struct virgl_texture * res,uint32_t * total_size)243 vrend_resource_layout(struct virgl_texture *res,
244                       uint32_t *total_size)
245 {
246    struct pipe_resource *pt = &res->base.u.b;
247    unsigned level;
248    unsigned width = pt->width0;
249    unsigned height = pt->height0;
250    unsigned depth = pt->depth0;
251    unsigned buffer_size = 0;
252 
253    for (level = 0; level <= pt->last_level; level++) {
254       unsigned slices;
255 
256       if (pt->target == PIPE_TEXTURE_CUBE)
257          slices = 6;
258       else if (pt->target == PIPE_TEXTURE_3D)
259          slices = depth;
260       else
261          slices = pt->array_size;
262 
263       res->stride[level] = util_format_get_stride(pt->format, width);
264       res->level_offset[level] = buffer_size;
265 
266       buffer_size += (util_format_get_nblocksy(pt->format, height) *
267                       slices * res->stride[level]);
268 
269       width = u_minify(width, 1);
270       height = u_minify(height, 1);
271       depth = u_minify(depth, 1);
272    }
273 
274    if (pt->nr_samples <= 1)
275       *total_size = buffer_size;
276    else /* don't create guest backing store for MSAA */
277       *total_size = 0;
278    return TRUE;
279 }
280 
virgl_texture_get_handle(struct pipe_screen * screen,struct pipe_resource * ptex,struct winsys_handle * whandle)281 static boolean virgl_texture_get_handle(struct pipe_screen *screen,
282                                          struct pipe_resource *ptex,
283                                          struct winsys_handle *whandle)
284 {
285    struct virgl_screen *vs = virgl_screen(screen);
286    struct virgl_texture *vtex = virgl_texture(ptex);
287 
288    return vs->vws->resource_get_handle(vs->vws, vtex->base.hw_res, vtex->stride[0], whandle);
289 }
290 
virgl_texture_destroy(struct pipe_screen * screen,struct pipe_resource * res)291 static void virgl_texture_destroy(struct pipe_screen *screen,
292                                   struct pipe_resource *res)
293 {
294    struct virgl_screen *vs = virgl_screen(screen);
295    struct virgl_texture *vtex = virgl_texture(res);
296    vs->vws->resource_unref(vs->vws, vtex->base.hw_res);
297    FREE(vtex);
298 }
299 
300 static const struct u_resource_vtbl virgl_texture_vtbl =
301 {
302    virgl_texture_get_handle,            /* get_handle */
303    virgl_texture_destroy,               /* resource_destroy */
304    virgl_texture_transfer_map,          /* transfer_map */
305    NULL,                                /* transfer_flush_region */
306    virgl_texture_transfer_unmap,        /* transfer_unmap */
307 };
308 
309 struct pipe_resource *
virgl_texture_from_handle(struct virgl_screen * vs,const struct pipe_resource * template,struct winsys_handle * whandle)310 virgl_texture_from_handle(struct virgl_screen *vs,
311                           const struct pipe_resource *template,
312                           struct winsys_handle *whandle)
313 {
314    struct virgl_texture *tex;
315    uint32_t size;
316 
317    tex = CALLOC_STRUCT(virgl_texture);
318    tex->base.u.b = *template;
319    tex->base.u.b.screen = &vs->base;
320    pipe_reference_init(&tex->base.u.b.reference, 1);
321    tex->base.u.vtbl = &virgl_texture_vtbl;
322    vrend_resource_layout(tex, &size);
323 
324    tex->base.hw_res = vs->vws->resource_create_from_handle(vs->vws, whandle);
325    return &tex->base.u.b;
326 }
327 
virgl_texture_create(struct virgl_screen * vs,const struct pipe_resource * template)328 struct pipe_resource *virgl_texture_create(struct virgl_screen *vs,
329                                            const struct pipe_resource *template)
330 {
331    struct virgl_texture *tex;
332    uint32_t size;
333    unsigned vbind;
334 
335    tex = CALLOC_STRUCT(virgl_texture);
336    tex->base.clean = TRUE;
337    tex->base.u.b = *template;
338    tex->base.u.b.screen = &vs->base;
339    pipe_reference_init(&tex->base.u.b.reference, 1);
340    tex->base.u.vtbl = &virgl_texture_vtbl;
341    vrend_resource_layout(tex, &size);
342 
343    vbind = pipe_to_virgl_bind(template->bind);
344    tex->base.hw_res = vs->vws->resource_create(vs->vws, template->target, template->format, vbind, template->width0, template->height0, template->depth0, template->array_size, template->last_level, template->nr_samples, size);
345    if (!tex->base.hw_res) {
346       FREE(tex);
347       return NULL;
348    }
349    return &tex->base.u.b;
350 }
351