Path: blob/21.2-virgl/src/gallium/drivers/svga/svga_resource_buffer.c
4570 views
/**********************************************************1* Copyright 2008-2009 VMware, Inc. All rights reserved.2*3* Permission is hereby granted, free of charge, to any person4* obtaining a copy of this software and associated documentation5* files (the "Software"), to deal in the Software without6* restriction, including without limitation the rights to use, copy,7* modify, merge, publish, distribute, sublicense, and/or sell copies8* of the Software, and to permit persons to whom the Software is9* furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice shall be12* included in all copies or substantial portions of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS18* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN19* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*23**********************************************************/2425#include "svga_cmd.h"2627#include "pipe/p_state.h"28#include "pipe/p_defines.h"29#include "util/u_inlines.h"30#include "os/os_thread.h"31#include "util/u_math.h"32#include "util/u_memory.h"33#include "util/u_resource.h"3435#include "svga_context.h"36#include "svga_screen.h"37#include "svga_resource_buffer.h"38#include "svga_resource_buffer_upload.h"39#include "svga_resource_texture.h"40#include "svga_sampler_view.h"41#include "svga_winsys.h"42#include "svga_debug.h"434445/**46* Determine what buffers eventually need hardware backing.47*48* Vertex- and index buffers need hardware backing. Constant buffers49* do on vgpu10. Staging texture-upload buffers do when they are50* supported.51*/52static inline boolean53svga_buffer_needs_hw_storage(const struct svga_screen *ss,54const struct pipe_resource *template)55{56unsigned bind_mask = (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_INDEX_BUFFER |57PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_STREAM_OUTPUT |58PIPE_BIND_SHADER_BUFFER | PIPE_BIND_COMMAND_ARGS_BUFFER);5960if (ss->sws->have_vgpu10) {61/*62* Driver-created upload const0- and staging texture upload buffers63* tagged with PIPE_BIND_CUSTOM64*/65bind_mask |= PIPE_BIND_CUSTOM;66/**67* Uniform buffer objects.68* Don't create hardware storage for state-tracker constant buffers,69* because we frequently map them for reading and writing, and70* the length of those buffers are always small, so it is better71* to just use system memory.72*/73}7475if (template->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT)76return TRUE;7778return !!(template->bind & bind_mask);79}8081/**82* Create a buffer transfer.83*84* Unlike texture DMAs (which are written immediately to the command buffer and85* therefore inherently serialized with other context operations), for buffers86* we try to coalesce multiple range mappings (i.e, multiple calls to this87* function) into a single DMA command, for better efficiency in command88* processing. This means we need to exercise extra care here to ensure that89* the end result is exactly the same as if one DMA was used for every mapped90* range.91*/92void *93svga_buffer_transfer_map(struct pipe_context *pipe,94struct pipe_resource *resource,95unsigned level,96unsigned usage,97const struct pipe_box *box,98struct pipe_transfer **ptransfer)99{100struct svga_context *svga = svga_context(pipe);101struct svga_screen *ss = svga_screen(pipe->screen);102struct svga_buffer *sbuf = svga_buffer(resource);103struct pipe_transfer *transfer;104uint8_t *map = NULL;105int64_t begin = svga_get_time(svga);106107SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_BUFFERTRANSFERMAP);108109assert(box->y == 0);110assert(box->z == 0);111assert(box->height == 1);112assert(box->depth == 1);113114transfer = MALLOC_STRUCT(pipe_transfer);115if (!transfer) {116goto done;117}118119transfer->resource = resource;120transfer->level = level;121transfer->usage = usage;122transfer->box = *box;123transfer->stride = 0;124transfer->layer_stride = 0;125126if (usage & PIPE_MAP_WRITE) {127/* If we write to the buffer for any reason, free any saved translated128* vertices.129*/130pipe_resource_reference(&sbuf->translated_indices.buffer, NULL);131}132133if ((usage & PIPE_MAP_READ) && sbuf->dirty &&134!sbuf->key.coherent && !svga->swc->force_coherent) {135136/* Host-side buffers can only be dirtied with vgpu10 features137* (streamout and buffer copy).138*/139assert(svga_have_vgpu10(svga));140141if (!sbuf->user) {142(void) svga_buffer_handle(svga, resource, sbuf->bind_flags);143}144145if (sbuf->dma.pending) {146svga_buffer_upload_flush(svga, sbuf);147svga_context_finish(svga);148}149150assert(sbuf->handle);151152SVGA_RETRY(svga, SVGA3D_vgpu10_ReadbackSubResource(svga->swc,153sbuf->handle, 0));154svga->hud.num_readbacks++;155156svga_context_finish(svga);157158sbuf->dirty = FALSE;159}160161if (usage & PIPE_MAP_WRITE) {162if ((usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE) &&163!(resource->flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT)) {164/*165* Flush any pending primitives, finish writing any pending DMA166* commands, and tell the host to discard the buffer contents on167* the next DMA operation.168*/169170svga_hwtnl_flush_buffer(svga, resource);171172if (sbuf->dma.pending) {173svga_buffer_upload_flush(svga, sbuf);174175/*176* Instead of flushing the context command buffer, simply discard177* the current hwbuf, and start a new one.178* With GB objects, the map operation takes care of this179* if passed the PIPE_MAP_DISCARD_WHOLE_RESOURCE flag,180* and the old backing store is busy.181*/182183if (!svga_have_gb_objects(svga))184svga_buffer_destroy_hw_storage(ss, sbuf);185}186187sbuf->map.num_ranges = 0;188sbuf->dma.flags.discard = TRUE;189}190191if (usage & PIPE_MAP_UNSYNCHRONIZED) {192if (!sbuf->map.num_ranges) {193/*194* No pending ranges to upload so far, so we can tell the host to195* not synchronize on the next DMA command.196*/197198sbuf->dma.flags.unsynchronized = TRUE;199}200} else {201/*202* Synchronizing, so flush any pending primitives, finish writing any203* pending DMA command, and ensure the next DMA will be done in order.204*/205206svga_hwtnl_flush_buffer(svga, resource);207208if (sbuf->dma.pending) {209svga_buffer_upload_flush(svga, sbuf);210211if (svga_buffer_has_hw_storage(sbuf)) {212/*213* We have a pending DMA upload from a hardware buffer, therefore214* we need to ensure that the host finishes processing that DMA215* command before the gallium frontend can start overwriting the216* hardware buffer.217*218* XXX: This could be avoided by tying the hardware buffer to219* the transfer (just as done with textures), which would allow220* overlapping DMAs commands to be queued on the same context221* buffer. However, due to the likelihood of software vertex222* processing, it is more convenient to hold on to the hardware223* buffer, allowing to quickly access the contents from the CPU224* without having to do a DMA download from the host.225*/226227if (usage & PIPE_MAP_DONTBLOCK) {228/*229* Flushing the command buffer here will most likely cause230* the map of the hwbuf below to block, so preemptively231* return NULL here if DONTBLOCK is set to prevent unnecessary232* command buffer flushes.233*/234235FREE(transfer);236goto done;237}238239svga_context_flush(svga, NULL);240}241}242243sbuf->dma.flags.unsynchronized = FALSE;244}245}246247if (!sbuf->swbuf && !svga_buffer_has_hw_storage(sbuf)) {248if (svga_buffer_create_hw_storage(ss, sbuf, sbuf->bind_flags) != PIPE_OK) {249/*250* We can't create a hardware buffer big enough, so create a malloc251* buffer instead.252*/253if (0) {254debug_printf("%s: failed to allocate %u KB of DMA, "255"splitting DMA transfers\n",256__FUNCTION__,257(sbuf->b.width0 + 1023)/1024);258}259260sbuf->swbuf = align_malloc(sbuf->b.width0, 16);261if (!sbuf->swbuf) {262FREE(transfer);263goto done;264}265}266}267268if (sbuf->swbuf) {269/* User/malloc buffer */270map = sbuf->swbuf;271}272else if (svga_buffer_has_hw_storage(sbuf)) {273boolean retry;274275map = SVGA_TRY_MAP(svga_buffer_hw_storage_map276(svga, sbuf, transfer->usage, &retry), retry);277if (map == NULL && retry) {278/*279* At this point, svga_buffer_get_transfer() has already280* hit the DISCARD_WHOLE_RESOURCE path and flushed HWTNL281* for this buffer.282*/283svga_retry_enter(svga);284svga_context_flush(svga, NULL);285map = svga_buffer_hw_storage_map(svga, sbuf, transfer->usage, &retry);286svga_retry_exit(svga);287}288}289else {290map = NULL;291}292293if (map) {294++sbuf->map.count;295map += transfer->box.x;296*ptransfer = transfer;297} else {298FREE(transfer);299}300301svga->hud.map_buffer_time += (svga_get_time(svga) - begin);302303done:304SVGA_STATS_TIME_POP(svga_sws(svga));305return map;306}307308309void310svga_buffer_transfer_flush_region(struct pipe_context *pipe,311struct pipe_transfer *transfer,312const struct pipe_box *box)313{314struct svga_screen *ss = svga_screen(pipe->screen);315struct svga_buffer *sbuf = svga_buffer(transfer->resource);316struct svga_context *svga = svga_context(pipe);317unsigned offset = transfer->box.x + box->x;318unsigned length = box->width;319320assert(transfer->usage & PIPE_MAP_WRITE);321assert(transfer->usage & PIPE_MAP_FLUSH_EXPLICIT);322323if (!(svga->swc->force_coherent || sbuf->key.coherent) || sbuf->swbuf) {324mtx_lock(&ss->swc_mutex);325svga_buffer_add_range(sbuf, offset, offset + length);326mtx_unlock(&ss->swc_mutex);327}328}329330331void332svga_buffer_transfer_unmap(struct pipe_context *pipe,333struct pipe_transfer *transfer)334{335struct svga_screen *ss = svga_screen(pipe->screen);336struct svga_context *svga = svga_context(pipe);337struct svga_buffer *sbuf = svga_buffer(transfer->resource);338339SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_BUFFERTRANSFERUNMAP);340341mtx_lock(&ss->swc_mutex);342343assert(sbuf->map.count);344if (sbuf->map.count) {345--sbuf->map.count;346}347348if (svga_buffer_has_hw_storage(sbuf)) {349350/* Note: we may wind up flushing here and unmapping other buffers351* which leads to recursively locking ss->swc_mutex.352*/353svga_buffer_hw_storage_unmap(svga, sbuf);354}355356if (transfer->usage & PIPE_MAP_WRITE) {357if (!(transfer->usage & PIPE_MAP_FLUSH_EXPLICIT)) {358/*359* Mapped range not flushed explicitly, so flush the whole buffer,360* and tell the host to discard the contents when processing the DMA361* command.362*/363364SVGA_DBG(DEBUG_DMA, "flushing the whole buffer\n");365366sbuf->dma.flags.discard = TRUE;367368if (!(svga->swc->force_coherent || sbuf->key.coherent) || sbuf->swbuf)369svga_buffer_add_range(sbuf, 0, sbuf->b.width0);370}371372if (sbuf->swbuf &&373(!sbuf->bind_flags || (sbuf->bind_flags & PIPE_BIND_CONSTANT_BUFFER))) {374/*375* Since the constant buffer is in system buffer, we need376* to set the constant buffer dirty bits, so that the context377* can update the changes in the device.378* According to the GL spec, buffer bound to other contexts will379* have to be explicitly rebound by the user to have the changes take380* into effect.381*/382svga->dirty |= SVGA_NEW_CONST_BUFFER;383}384}385386mtx_unlock(&ss->swc_mutex);387FREE(transfer);388SVGA_STATS_TIME_POP(svga_sws(svga));389}390391392void393svga_resource_destroy(struct pipe_screen *screen,394struct pipe_resource *buf)395{396if (buf->target == PIPE_BUFFER) {397struct svga_screen *ss = svga_screen(screen);398struct svga_buffer *sbuf = svga_buffer(buf);399400assert(!p_atomic_read(&buf->reference.count));401402assert(!sbuf->dma.pending);403404if (sbuf->handle)405svga_buffer_destroy_host_surface(ss, sbuf);406407if (sbuf->uploaded.buffer)408pipe_resource_reference(&sbuf->uploaded.buffer, NULL);409410if (sbuf->hwbuf)411svga_buffer_destroy_hw_storage(ss, sbuf);412413if (sbuf->swbuf && !sbuf->user)414align_free(sbuf->swbuf);415416pipe_resource_reference(&sbuf->translated_indices.buffer, NULL);417418ss->hud.total_resource_bytes -= sbuf->size;419assert(ss->hud.num_resources > 0);420if (ss->hud.num_resources > 0)421ss->hud.num_resources--;422423FREE(sbuf);424} else {425struct svga_screen *ss = svga_screen(screen);426struct svga_texture *tex = svga_texture(buf);427428ss->texture_timestamp++;429430svga_sampler_view_reference(&tex->cached_view, NULL);431432/*433DBG("%s deleting %p\n", __FUNCTION__, (void *) tex);434*/435SVGA_DBG(DEBUG_DMA, "unref sid %p (texture)\n", tex->handle);436svga_screen_surface_destroy(ss, &tex->key, &tex->handle);437438/* Destroy the backed surface handle if exists */439if (tex->backed_handle)440svga_screen_surface_destroy(ss, &tex->backed_key, &tex->backed_handle);441442ss->hud.total_resource_bytes -= tex->size;443444FREE(tex->defined);445FREE(tex->rendered_to);446FREE(tex->dirty);447FREE(tex);448449assert(ss->hud.num_resources > 0);450if (ss->hud.num_resources > 0)451ss->hud.num_resources--;452}453}454455struct pipe_resource *456svga_buffer_create(struct pipe_screen *screen,457const struct pipe_resource *template)458{459struct svga_screen *ss = svga_screen(screen);460struct svga_buffer *sbuf;461unsigned bind_flags;462463SVGA_STATS_TIME_PUSH(ss->sws, SVGA_STATS_TIME_CREATEBUFFER);464465sbuf = CALLOC_STRUCT(svga_buffer);466if (!sbuf)467goto error1;468469sbuf->b = *template;470pipe_reference_init(&sbuf->b.reference, 1);471sbuf->b.screen = screen;472bind_flags = template->bind & ~PIPE_BIND_CUSTOM;473474list_inithead(&sbuf->surfaces);475476if (bind_flags & PIPE_BIND_CONSTANT_BUFFER) {477/* Constant buffers can only have the PIPE_BIND_CONSTANT_BUFFER478* flag set.479*/480if (ss->sws->have_vgpu10) {481bind_flags = PIPE_BIND_CONSTANT_BUFFER;482}483}484485/* Although svga device only requires constant buffer size to be486* in multiples of 16, in order to allow bind_flags promotion,487* we are mandating all buffer size to be in multiples of 16.488*/489sbuf->b.width0 = align(sbuf->b.width0, 16);490491if (svga_buffer_needs_hw_storage(ss, template)) {492493/* If the buffer is not used for constant buffer, set494* the vertex/index bind flags as well so that the buffer will be495* accepted for those uses.496* Note that the PIPE_BIND_ flags we get from the gallium frontend are497* just a hint about how the buffer may be used. And OpenGL buffer498* object may be used for many different things.499* Also note that we do not unconditionally set the streamout500* bind flag since streamout buffer is an output buffer and501* might have performance implication.502*/503if (!(template->bind & PIPE_BIND_CONSTANT_BUFFER) &&504!(template->bind & PIPE_BIND_CUSTOM)) {505/* Not a constant- or staging buffer.506* The buffer may be used for vertex data or indexes.507*/508bind_flags |= (PIPE_BIND_VERTEX_BUFFER |509PIPE_BIND_INDEX_BUFFER);510511/* It may be used for shader resource as well. */512bind_flags |= PIPE_BIND_SAMPLER_VIEW;513}514515if (svga_buffer_create_host_surface(ss, sbuf, bind_flags) != PIPE_OK)516goto error2;517}518else {519sbuf->swbuf = align_malloc(sbuf->b.width0, 64);520if (!sbuf->swbuf)521goto error2;522523/* Since constant buffer is usually small, it is much cheaper to524* use system memory for the data just as it is being done for525* the default constant buffer.526*/527if ((bind_flags & PIPE_BIND_CONSTANT_BUFFER) || !bind_flags)528sbuf->use_swbuf = TRUE;529}530531debug_reference(&sbuf->b.reference,532(debug_reference_descriptor)debug_describe_resource, 0);533534sbuf->bind_flags = bind_flags;535sbuf->size = util_resource_size(&sbuf->b);536ss->hud.total_resource_bytes += sbuf->size;537538ss->hud.num_resources++;539SVGA_STATS_TIME_POP(ss->sws);540541return &sbuf->b;542543error2:544FREE(sbuf);545error1:546SVGA_STATS_TIME_POP(ss->sws);547return NULL;548}549550551struct pipe_resource *552svga_user_buffer_create(struct pipe_screen *screen,553void *ptr,554unsigned bytes,555unsigned bind)556{557struct svga_buffer *sbuf;558struct svga_screen *ss = svga_screen(screen);559560sbuf = CALLOC_STRUCT(svga_buffer);561if (!sbuf)562goto no_sbuf;563564pipe_reference_init(&sbuf->b.reference, 1);565sbuf->b.screen = screen;566sbuf->b.format = PIPE_FORMAT_R8_UNORM; /* ?? */567sbuf->b.usage = PIPE_USAGE_IMMUTABLE;568sbuf->b.bind = bind;569sbuf->b.width0 = bytes;570sbuf->b.height0 = 1;571sbuf->b.depth0 = 1;572sbuf->b.array_size = 1;573574sbuf->bind_flags = bind;575sbuf->swbuf = ptr;576sbuf->user = TRUE;577578debug_reference(&sbuf->b.reference,579(debug_reference_descriptor)debug_describe_resource, 0);580581ss->hud.num_resources++;582583return &sbuf->b;584585no_sbuf:586return NULL;587}588589590