/********************************************************** * Copyright 2008-2009 VMware, Inc. All rights reserved. * * 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 the rights to use, copy, * modify, merge, publish, distribute, sublicense, 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 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 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * 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 "svga_cmd.h" #include "pipe/p_state.h" #include "pipe/p_defines.h" #include "util/u_inlines.h" #include "os/os_thread.h" #include "util/u_math.h" #include "util/u_memory.h" #include "svga_context.h" #include "svga_screen.h" #include "svga_resource_buffer.h" #include "svga_resource_buffer_upload.h" #include "svga_winsys.h" #include "svga_debug.h" /** * Vertex and index buffers need hardware backing. Constant buffers * do not. No other types of buffers currently supported. */ static INLINE boolean svga_buffer_needs_hw_storage(unsigned usage) { return usage & (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_INDEX_BUFFER); } /** * Create a buffer transfer. * * Unlike texture DMAs (which are written immediately to the command buffer and * therefore inherently serialized with other context operations), for buffers * we try to coalesce multiple range mappings (i.e, multiple calls to this * function) into a single DMA command, for better efficiency in command * processing. This means we need to exercise extra care here to ensure that * the end result is exactly the same as if one DMA was used for every mapped * range. */ static struct pipe_transfer * svga_buffer_get_transfer(struct pipe_context *pipe, struct pipe_resource *resource, unsigned level, unsigned usage, const struct pipe_box *box) { struct svga_context *svga = svga_context(pipe); struct svga_screen *ss = svga_screen(pipe->screen); struct svga_buffer *sbuf = svga_buffer(resource); struct pipe_transfer *transfer; if (usage & PIPE_TRANSFER_MAP_PERMANENTLY) { return NULL; } transfer = CALLOC_STRUCT(pipe_transfer); if (transfer == NULL) { return NULL; } transfer->resource = resource; transfer->level = level; transfer->usage = usage; transfer->box = *box; if (usage & PIPE_TRANSFER_WRITE) { if (usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) { /* * Flush any pending primitives, finish writing any pending DMA * commands, and tell the host to discard the buffer contents on * the next DMA operation. */ svga_hwtnl_flush_buffer(svga, resource); if (sbuf->dma.pending) { svga_buffer_upload_flush(svga, sbuf); /* * Instead of flushing the context command buffer, simply discard * the current hwbuf, and start a new one. */ svga_buffer_destroy_hw_storage(ss, sbuf); } sbuf->map.num_ranges = 0; sbuf->dma.flags.discard = TRUE; } if (usage & PIPE_TRANSFER_UNSYNCHRONIZED) { if (!sbuf->map.num_ranges) { /* * No pending ranges to upload so far, so we can tell the host to * not synchronize on the next DMA command. */ sbuf->dma.flags.unsynchronized = TRUE; } } else { /* * Synchronizing, so flush any pending primitives, finish writing any * pending DMA command, and ensure the next DMA will be done in order. */ svga_hwtnl_flush_buffer(svga, resource); if (sbuf->dma.pending) { svga_buffer_upload_flush(svga, sbuf); if (sbuf->hwbuf) { /* * We have a pending DMA upload from a hardware buffer, therefore * we need to ensure that the host finishes processing that DMA * command before the state tracker can start overwriting the * hardware buffer. * * XXX: This could be avoided by tying the hardware buffer to * the transfer (just as done with textures), which would allow * overlapping DMAs commands to be queued on the same context * buffer. However, due to the likelihood of software vertex * processing, it is more convenient to hold on to the hardware * buffer, allowing to quickly access the contents from the CPU * without having to do a DMA download from the host. */ if (usage & PIPE_TRANSFER_DONTBLOCK) { /* * Flushing the command buffer here will most likely cause * the map of the hwbuf below to block, so preemptively * return NULL here if DONTBLOCK is set to prevent unnecessary * command buffer flushes. */ FREE(transfer); return NULL; } svga_context_flush(svga, NULL); } } sbuf->dma.flags.unsynchronized = FALSE; } } if (!sbuf->swbuf && !sbuf->hwbuf) { if (svga_buffer_create_hw_storage(ss, sbuf) != PIPE_OK) { /* * We can't create a hardware buffer big enough, so create a malloc * buffer instead. */ if (0) { debug_printf("%s: failed to allocate %u KB of DMA, " "splitting DMA transfers\n", __FUNCTION__, (sbuf->b.b.width0 + 1023)/1024); } sbuf->swbuf = align_malloc(sbuf->b.b.width0, 16); if (!sbuf->swbuf) { FREE(transfer); return NULL; } } } return transfer; } /** * Map a range of a buffer. */ static void * svga_buffer_transfer_map( struct pipe_context *pipe, struct pipe_transfer *transfer ) { struct svga_buffer *sbuf = svga_buffer(transfer->resource); uint8_t *map; if (sbuf->swbuf) { /* User/malloc buffer */ map = sbuf->swbuf; } else if (sbuf->hwbuf) { struct svga_screen *ss = svga_screen(pipe->screen); struct svga_winsys_screen *sws = ss->sws; map = sws->buffer_map(sws, sbuf->hwbuf, transfer->usage); } else { map = NULL; } if (map) { ++sbuf->map.count; map += transfer->box.x; } return map; } static void svga_buffer_transfer_flush_region( struct pipe_context *pipe, struct pipe_transfer *transfer, const struct pipe_box *box) { struct svga_screen *ss = svga_screen(pipe->screen); struct svga_buffer *sbuf = svga_buffer(transfer->resource); unsigned offset = transfer->box.x + box->x; unsigned length = box->width; assert(transfer->usage & PIPE_TRANSFER_WRITE); assert(transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT); pipe_mutex_lock(ss->swc_mutex); svga_buffer_add_range(sbuf, offset, offset + length); pipe_mutex_unlock(ss->swc_mutex); } static void svga_buffer_transfer_unmap( struct pipe_context *pipe, struct pipe_transfer *transfer ) { struct svga_screen *ss = svga_screen(pipe->screen); struct svga_winsys_screen *sws = ss->sws; struct svga_buffer *sbuf = svga_buffer(transfer->resource); pipe_mutex_lock(ss->swc_mutex); assert(sbuf->map.count); if (sbuf->map.count) { --sbuf->map.count; } if (sbuf->hwbuf) { sws->buffer_unmap(sws, sbuf->hwbuf); } if (transfer->usage & PIPE_TRANSFER_WRITE) { if (!(transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) { /* * Mapped range not flushed explicitly, so flush the whole buffer, * and tell the host to discard the contents when processing the DMA * command. */ SVGA_DBG(DEBUG_DMA, "flushing the whole buffer\n"); sbuf->dma.flags.discard = TRUE; svga_buffer_add_range(sbuf, 0, sbuf->b.b.width0); } } pipe_mutex_unlock(ss->swc_mutex); } /** * Destroy transfer */ static void svga_buffer_transfer_destroy(struct pipe_context *pipe, struct pipe_transfer *transfer) { FREE(transfer); } static void svga_buffer_destroy( struct pipe_screen *screen, struct pipe_resource *buf ) { struct svga_screen *ss = svga_screen(screen); struct svga_buffer *sbuf = svga_buffer( buf ); assert(!p_atomic_read(&buf->reference.count)); assert(!sbuf->dma.pending); if(sbuf->handle) svga_buffer_destroy_host_surface(ss, sbuf); if(sbuf->uploaded.buffer) pipe_resource_reference(&sbuf->uploaded.buffer, NULL); if(sbuf->hwbuf) svga_buffer_destroy_hw_storage(ss, sbuf); if(sbuf->swbuf && !sbuf->user) align_free(sbuf->swbuf); FREE(sbuf); } struct u_resource_vtbl svga_buffer_vtbl = { u_default_resource_get_handle, /* get_handle */ svga_buffer_destroy, /* resource_destroy */ svga_buffer_get_transfer, /* get_transfer */ svga_buffer_transfer_destroy, /* transfer_destroy */ svga_buffer_transfer_map, /* transfer_map */ svga_buffer_transfer_flush_region, /* transfer_flush_region */ svga_buffer_transfer_unmap, /* transfer_unmap */ u_default_transfer_inline_write /* transfer_inline_write */ }; struct pipe_resource * svga_buffer_create(struct pipe_screen *screen, const struct pipe_resource *template) { struct svga_screen *ss = svga_screen(screen); struct svga_buffer *sbuf; sbuf = CALLOC_STRUCT(svga_buffer); if(!sbuf) goto error1; sbuf->b.b = *template; sbuf->b.vtbl = &svga_buffer_vtbl; pipe_reference_init(&sbuf->b.b.reference, 1); sbuf->b.b.screen = screen; if(svga_buffer_needs_hw_storage(template->bind)) { if(svga_buffer_create_host_surface(ss, sbuf) != PIPE_OK) goto error2; } else { sbuf->swbuf = align_malloc(template->width0, 64); if(!sbuf->swbuf) goto error2; } debug_reference(&sbuf->b.b.reference, (debug_reference_descriptor)debug_describe_resource, 0); return &sbuf->b.b; error2: FREE(sbuf); error1: return NULL; } struct pipe_resource * svga_user_buffer_create(struct pipe_screen *screen, void *ptr, unsigned bytes, unsigned bind) { struct svga_buffer *sbuf; sbuf = CALLOC_STRUCT(svga_buffer); if(!sbuf) goto no_sbuf; pipe_reference_init(&sbuf->b.b.reference, 1); sbuf->b.vtbl = &svga_buffer_vtbl; sbuf->b.b.screen = screen; sbuf->b.b.format = PIPE_FORMAT_R8_UNORM; /* ?? */ sbuf->b.b.usage = PIPE_USAGE_IMMUTABLE; sbuf->b.b.bind = bind; sbuf->b.b.width0 = bytes; sbuf->b.b.height0 = 1; sbuf->b.b.depth0 = 1; sbuf->b.b.array_size = 1; sbuf->swbuf = ptr; sbuf->user = TRUE; debug_reference(&sbuf->b.b.reference, (debug_reference_descriptor)debug_describe_resource, 0); return &sbuf->b.b; no_sbuf: return NULL; }