/* * Copyright 2014, 2015 Red Hat. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "util/u_format.h" #include "util/u_inlines.h" #include "util/u_memory.h" #include "virgl_context.h" #include "virgl_resource.h" #include "virgl_screen.h" bool virgl_res_needs_flush(struct virgl_context *vctx, struct virgl_transfer *trans) { struct virgl_screen *vs = virgl_screen(vctx->base.screen); struct virgl_resource *res = virgl_resource(trans->base.resource); if (trans->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED) return false; if (!vs->vws->res_is_referenced(vs->vws, vctx->cbuf, res->hw_res)) return false; if (res->clean_mask & (1 << trans->base.level)) { if (vctx->num_draws == 0 && vctx->num_compute == 0) return false; if (!virgl_transfer_queue_is_queued(&vctx->queue, trans)) return false; } return true; } bool virgl_res_needs_readback(struct virgl_context *vctx, struct virgl_resource *res, unsigned usage, unsigned level) { bool readback = true; if (res->clean_mask & (1 << level)) readback = false; else if (usage & PIPE_TRANSFER_DISCARD_RANGE) readback = false; else if ((usage & (PIPE_TRANSFER_WRITE | PIPE_TRANSFER_FLUSH_EXPLICIT)) == (PIPE_TRANSFER_WRITE | PIPE_TRANSFER_FLUSH_EXPLICIT)) readback = false; return readback; } static struct pipe_resource *virgl_resource_create(struct pipe_screen *screen, const struct pipe_resource *templ) { unsigned vbind; struct virgl_screen *vs = virgl_screen(screen); struct virgl_resource *res = CALLOC_STRUCT(virgl_resource); res->u.b = *templ; res->u.b.screen = &vs->base; pipe_reference_init(&res->u.b.reference, 1); vbind = pipe_to_virgl_bind(vs, templ->bind); virgl_resource_layout(&res->u.b, &res->metadata); res->hw_res = vs->vws->resource_create(vs->vws, templ->target, templ->format, vbind, templ->width0, templ->height0, templ->depth0, templ->array_size, templ->last_level, templ->nr_samples, res->metadata.total_size); if (!res->hw_res) { FREE(res); return NULL; } res->clean_mask = (1 << VR_MAX_TEXTURE_2D_LEVELS) - 1; if (templ->target == PIPE_BUFFER) virgl_buffer_init(res); else virgl_texture_init(res); return &res->u.b; } static struct pipe_resource *virgl_resource_from_handle(struct pipe_screen *screen, const struct pipe_resource *templ, struct winsys_handle *whandle, unsigned usage) { struct virgl_screen *vs = virgl_screen(screen); if (templ->target == PIPE_BUFFER) return NULL; struct virgl_resource *res = CALLOC_STRUCT(virgl_resource); res->u.b = *templ; res->u.b.screen = &vs->base; pipe_reference_init(&res->u.b.reference, 1); virgl_resource_layout(&res->u.b, &res->metadata); res->hw_res = vs->vws->resource_create_from_handle(vs->vws, whandle); if (!res->hw_res) { FREE(res); return NULL; } virgl_texture_init(res); return &res->u.b; } void virgl_init_screen_resource_functions(struct pipe_screen *screen) { screen->resource_create = virgl_resource_create; screen->resource_from_handle = virgl_resource_from_handle; screen->resource_get_handle = u_resource_get_handle_vtbl; screen->resource_destroy = u_resource_destroy_vtbl; } static bool virgl_buffer_transfer_extend(struct pipe_context *ctx, struct pipe_resource *resource, unsigned usage, const struct pipe_box *box, const void *data) { struct virgl_context *vctx = virgl_context(ctx); struct virgl_resource *vbuf = virgl_resource(resource); struct virgl_transfer dummy_trans = { 0 }; bool flush; struct virgl_transfer *queued; /* * Attempts to short circuit the entire process of mapping and unmapping * a resource if there is an existing transfer that can be extended. * Pessimestically falls back if a flush is required. */ dummy_trans.base.resource = resource; dummy_trans.base.usage = usage; dummy_trans.base.box = *box; dummy_trans.base.stride = vbuf->metadata.stride[0]; dummy_trans.base.layer_stride = vbuf->metadata.layer_stride[0]; dummy_trans.offset = box->x; flush = virgl_res_needs_flush(vctx, &dummy_trans); if (flush) return false; queued = virgl_transfer_queue_extend(&vctx->queue, &dummy_trans); if (!queued || !queued->hw_res_map) return false; memcpy(queued->hw_res_map + dummy_trans.offset, data, box->width); return true; } static void virgl_buffer_subdata(struct pipe_context *pipe, struct pipe_resource *resource, unsigned usage, unsigned offset, unsigned size, const void *data) { struct pipe_box box; assert(!(usage & PIPE_TRANSFER_READ)); /* the write flag is implicit by the nature of buffer_subdata */ usage |= PIPE_TRANSFER_WRITE; if (offset == 0 && size == resource->width0) usage |= PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE; else usage |= PIPE_TRANSFER_DISCARD_RANGE; u_box_1d(offset, size, &box); if (usage & PIPE_TRANSFER_DISCARD_RANGE && virgl_buffer_transfer_extend(pipe, resource, usage, &box, data)) return; if (resource->width0 >= getpagesize()) u_default_buffer_subdata(pipe, resource, usage, offset, size, data); else virgl_transfer_inline_write(pipe, resource, 0, usage, &box, data, 0, 0); } void virgl_init_context_resource_functions(struct pipe_context *ctx) { ctx->transfer_map = u_transfer_map_vtbl; ctx->transfer_flush_region = u_transfer_flush_region_vtbl; ctx->transfer_unmap = u_transfer_unmap_vtbl; ctx->buffer_subdata = virgl_buffer_subdata; ctx->texture_subdata = u_default_texture_subdata; } void virgl_resource_layout(struct pipe_resource *pt, struct virgl_resource_metadata *metadata) { unsigned level, nblocksy; unsigned width = pt->width0; unsigned height = pt->height0; unsigned depth = pt->depth0; unsigned buffer_size = 0; for (level = 0; level <= pt->last_level; level++) { unsigned slices; if (pt->target == PIPE_TEXTURE_CUBE) slices = 6; else if (pt->target == PIPE_TEXTURE_3D) slices = depth; else slices = pt->array_size; nblocksy = util_format_get_nblocksy(pt->format, height); metadata->stride[level] = util_format_get_stride(pt->format, width); metadata->layer_stride[level] = nblocksy * metadata->stride[level]; metadata->level_offset[level] = buffer_size; buffer_size += slices * metadata->layer_stride[level]; width = u_minify(width, 1); height = u_minify(height, 1); depth = u_minify(depth, 1); } if (pt->nr_samples <= 1) metadata->total_size = buffer_size; else /* don't create guest backing store for MSAA */ metadata->total_size = 0; } struct virgl_transfer * virgl_resource_create_transfer(struct slab_child_pool *pool, struct pipe_resource *pres, const struct virgl_resource_metadata *metadata, unsigned level, unsigned usage, const struct pipe_box *box) { struct virgl_transfer *trans; enum pipe_format format = pres->format; const unsigned blocksy = box->y / util_format_get_blockheight(format); const unsigned blocksx = box->x / util_format_get_blockwidth(format); unsigned offset = metadata->level_offset[level]; if (pres->target == PIPE_TEXTURE_CUBE || pres->target == PIPE_TEXTURE_CUBE_ARRAY || pres->target == PIPE_TEXTURE_3D || pres->target == PIPE_TEXTURE_2D_ARRAY) { offset += box->z * metadata->layer_stride[level]; } else if (pres->target == PIPE_TEXTURE_1D_ARRAY) { offset += box->z * metadata->stride[level]; assert(box->y == 0); } else if (pres->target == PIPE_BUFFER) { assert(box->y == 0 && box->z == 0); } else { assert(box->z == 0); } offset += blocksy * metadata->stride[level]; offset += blocksx * util_format_get_blocksize(format); trans = slab_alloc(pool); if (!trans) return NULL; trans->base.resource = pres; trans->base.level = level; trans->base.usage = usage; trans->base.box = *box; trans->base.stride = metadata->stride[level]; trans->base.layer_stride = metadata->layer_stride[level]; trans->offset = offset; util_range_init(&trans->range); if (trans->base.resource->target != PIPE_TEXTURE_3D && trans->base.resource->target != PIPE_TEXTURE_CUBE && trans->base.resource->target != PIPE_TEXTURE_1D_ARRAY && trans->base.resource->target != PIPE_TEXTURE_2D_ARRAY && trans->base.resource->target != PIPE_TEXTURE_CUBE_ARRAY) trans->l_stride = 0; else trans->l_stride = trans->base.layer_stride; return trans; } void virgl_resource_destroy_transfer(struct slab_child_pool *pool, struct virgl_transfer *trans) { util_range_destroy(&trans->range); slab_free(pool, trans); } void virgl_resource_destroy(struct pipe_screen *screen, struct pipe_resource *resource) { struct virgl_screen *vs = virgl_screen(screen); struct virgl_resource *res = virgl_resource(resource); vs->vws->resource_unref(vs->vws, res->hw_res); FREE(res); } boolean virgl_resource_get_handle(struct pipe_screen *screen, struct pipe_resource *resource, struct winsys_handle *whandle) { struct virgl_screen *vs = virgl_screen(screen); struct virgl_resource *res = virgl_resource(resource); if (res->u.b.target == PIPE_BUFFER) return FALSE; return vs->vws->resource_get_handle(vs->vws, res->hw_res, res->metadata.stride[0], whandle); } void virgl_resource_dirty(struct virgl_resource *res, uint32_t level) { if (res) { if (res->u.b.target == PIPE_BUFFER) res->clean_mask &= ~1; else res->clean_mask &= ~(1 << level); } }