Path: blob/21.2-virgl/src/gallium/drivers/svga/svga_resource_buffer_upload.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**********************************************************/242526#include "os/os_thread.h"27#include "pipe/p_state.h"28#include "pipe/p_defines.h"29#include "util/u_inlines.h"30#include "util/u_math.h"31#include "util/u_memory.h"3233#include "svga_cmd.h"34#include "svga_context.h"35#include "svga_debug.h"36#include "svga_resource_buffer.h"37#include "svga_resource_buffer_upload.h"38#include "svga_screen.h"39#include "svga_winsys.h"4041/**42* Describes a complete SVGA_3D_CMD_UPDATE_GB_IMAGE command43*44*/45struct svga_3d_update_gb_image {46SVGA3dCmdHeader header;47SVGA3dCmdUpdateGBImage body;48};4950struct svga_3d_invalidate_gb_image {51SVGA3dCmdHeader header;52SVGA3dCmdInvalidateGBImage body;53};545556/**57* Allocate a winsys_buffer (ie. DMA, aka GMR memory).58*59* It will flush and retry in case the first attempt to create a DMA buffer60* fails, so it should not be called from any function involved in flushing61* to avoid recursion.62*/63struct svga_winsys_buffer *64svga_winsys_buffer_create( struct svga_context *svga,65unsigned alignment,66unsigned usage,67unsigned size )68{69struct svga_screen *svgascreen = svga_screen(svga->pipe.screen);70struct svga_winsys_screen *sws = svgascreen->sws;71struct svga_winsys_buffer *buf;7273/* Just try */74buf = SVGA_TRY_PTR(sws->buffer_create(sws, alignment, usage, size));75if (!buf) {76SVGA_DBG(DEBUG_DMA|DEBUG_PERF, "flushing context to find %d bytes GMR\n",77size);7879/* Try flushing all pending DMAs */80svga_retry_enter(svga);81svga_context_flush(svga, NULL);82buf = sws->buffer_create(sws, alignment, usage, size);83svga_retry_exit(svga);84}8586return buf;87}888990/**91* Destroy HW storage if separate from the host surface.92* In the GB case, the HW storage is associated with the host surface93* and is therefore a No-op.94*/95void96svga_buffer_destroy_hw_storage(struct svga_screen *ss, struct svga_buffer *sbuf)97{98struct svga_winsys_screen *sws = ss->sws;99100assert(sbuf->map.count == 0);101assert(sbuf->hwbuf);102if (sbuf->hwbuf) {103sws->buffer_destroy(sws, sbuf->hwbuf);104sbuf->hwbuf = NULL;105}106}107108109110/**111* Allocate DMA'ble or Updatable storage for the buffer.112*113* Called before mapping a buffer.114*/115enum pipe_error116svga_buffer_create_hw_storage(struct svga_screen *ss,117struct svga_buffer *sbuf,118unsigned bind_flags)119{120assert(!sbuf->user);121122if (ss->sws->have_gb_objects) {123assert(sbuf->handle || !sbuf->dma.pending);124return svga_buffer_create_host_surface(ss, sbuf, bind_flags);125}126if (!sbuf->hwbuf) {127struct svga_winsys_screen *sws = ss->sws;128unsigned alignment = 16;129unsigned usage = 0;130unsigned size = sbuf->b.width0;131132sbuf->hwbuf = sws->buffer_create(sws, alignment, usage, size);133if (!sbuf->hwbuf)134return PIPE_ERROR_OUT_OF_MEMORY;135136assert(!sbuf->dma.pending);137}138139return PIPE_OK;140}141142143/**144* Allocate graphics memory for vertex/index/constant/etc buffer (not145* textures).146*/147enum pipe_error148svga_buffer_create_host_surface(struct svga_screen *ss,149struct svga_buffer *sbuf,150unsigned bind_flags)151{152enum pipe_error ret = PIPE_OK;153154assert(!sbuf->user);155156if (!sbuf->handle) {157boolean validated;158159sbuf->key.flags = 0;160161sbuf->key.format = SVGA3D_BUFFER;162if (bind_flags & PIPE_BIND_VERTEX_BUFFER) {163sbuf->key.flags |= SVGA3D_SURFACE_HINT_VERTEXBUFFER;164sbuf->key.flags |= SVGA3D_SURFACE_BIND_VERTEX_BUFFER;165}166if (bind_flags & PIPE_BIND_INDEX_BUFFER) {167sbuf->key.flags |= SVGA3D_SURFACE_HINT_INDEXBUFFER;168sbuf->key.flags |= SVGA3D_SURFACE_BIND_INDEX_BUFFER;169}170if (bind_flags & PIPE_BIND_CONSTANT_BUFFER)171sbuf->key.flags |= SVGA3D_SURFACE_BIND_CONSTANT_BUFFER;172173if (bind_flags & PIPE_BIND_STREAM_OUTPUT)174sbuf->key.flags |= SVGA3D_SURFACE_BIND_STREAM_OUTPUT;175176if (bind_flags & PIPE_BIND_SAMPLER_VIEW)177sbuf->key.flags |= SVGA3D_SURFACE_BIND_SHADER_RESOURCE;178179if (bind_flags & PIPE_BIND_COMMAND_ARGS_BUFFER) {180assert(ss->sws->have_sm5);181sbuf->key.flags |= SVGA3D_SURFACE_DRAWINDIRECT_ARGS;182}183184if (!bind_flags && sbuf->b.usage == PIPE_USAGE_STAGING) {185/* This surface is to be used with the186* SVGA3D_CMD_DX_TRANSFER_FROM_BUFFER command, and no other187* bind flags are allowed to be set for this surface.188*/189sbuf->key.flags = SVGA3D_SURFACE_TRANSFER_FROM_BUFFER;190}191192if (sbuf->b.flags & PIPE_RESOURCE_FLAG_MAP_PERSISTENT) {193/* This surface can be mapped persistently. We use194* coherent memory to avoid implementing memory barriers for195* persistent non-coherent memory for now.196*/197sbuf->key.coherent = 1;198}199200sbuf->key.size.width = sbuf->b.width0;201sbuf->key.size.height = 1;202sbuf->key.size.depth = 1;203204sbuf->key.numFaces = 1;205sbuf->key.numMipLevels = 1;206sbuf->key.cachable = 1;207sbuf->key.arraySize = 1;208sbuf->key.sampleCount = 0;209210SVGA_DBG(DEBUG_DMA, "surface_create for buffer sz %d\n",211sbuf->b.width0);212213sbuf->handle = svga_screen_surface_create(ss, bind_flags,214sbuf->b.usage,215&validated, &sbuf->key);216if (!sbuf->handle)217return PIPE_ERROR_OUT_OF_MEMORY;218219/* Always set the discard flag on the first time the buffer is written220* as svga_screen_surface_create might have passed a recycled host221* buffer.222*/223sbuf->dma.flags.discard = TRUE;224225SVGA_DBG(DEBUG_DMA, " --> got sid %p sz %d (buffer)\n",226sbuf->handle, sbuf->b.width0);227228/* Add the new surface to the buffer surface list */229ret = svga_buffer_add_host_surface(sbuf, sbuf->handle, &sbuf->key,230bind_flags);231232if (ss->sws->have_gb_objects) {233/* Initialize the surface with zero */234ss->sws->surface_init(ss->sws, sbuf->handle, svga_surface_size(&sbuf->key),235sbuf->key.flags);236}237}238239return ret;240}241242243/**244* Recreates a host surface with the new bind flags.245*/246enum pipe_error247svga_buffer_recreate_host_surface(struct svga_context *svga,248struct svga_buffer *sbuf,249unsigned bind_flags)250{251enum pipe_error ret = PIPE_OK;252struct svga_winsys_surface *old_handle = sbuf->handle;253254assert(sbuf->bind_flags != bind_flags);255assert(old_handle);256257sbuf->handle = NULL;258259/* Create a new resource with the requested bind_flags */260ret = svga_buffer_create_host_surface(svga_screen(svga->pipe.screen),261sbuf, bind_flags);262if (ret == PIPE_OK) {263/* Copy the surface data */264assert(sbuf->handle);265SVGA_RETRY(svga, SVGA3D_vgpu10_BufferCopy(svga->swc, old_handle,266sbuf->handle,2670, 0, sbuf->b.width0));268}269270/* Set the new bind flags for this buffer resource */271sbuf->bind_flags = bind_flags;272273return ret;274}275276277/**278* Returns TRUE if the surface bind flags is compatible with the new bind flags.279*/280static boolean281compatible_bind_flags(unsigned bind_flags,282unsigned tobind_flags)283{284if ((bind_flags & tobind_flags) == tobind_flags)285return TRUE;286else if ((bind_flags|tobind_flags) & PIPE_BIND_CONSTANT_BUFFER)287return FALSE;288else289return TRUE;290}291292293/**294* Returns a buffer surface from the surface list295* that has the requested bind flags or its existing bind flags296* can be promoted to include the new bind flags.297*/298static struct svga_buffer_surface *299svga_buffer_get_host_surface(struct svga_buffer *sbuf,300unsigned bind_flags)301{302struct svga_buffer_surface *bufsurf;303304LIST_FOR_EACH_ENTRY(bufsurf, &sbuf->surfaces, list) {305if (compatible_bind_flags(bufsurf->bind_flags, bind_flags))306return bufsurf;307}308return NULL;309}310311312/**313* Adds the host surface to the buffer surface list.314*/315enum pipe_error316svga_buffer_add_host_surface(struct svga_buffer *sbuf,317struct svga_winsys_surface *handle,318struct svga_host_surface_cache_key *key,319unsigned bind_flags)320{321struct svga_buffer_surface *bufsurf;322323bufsurf = CALLOC_STRUCT(svga_buffer_surface);324if (!bufsurf)325return PIPE_ERROR_OUT_OF_MEMORY;326327bufsurf->bind_flags = bind_flags;328bufsurf->handle = handle;329bufsurf->key = *key;330331/* add the surface to the surface list */332list_add(&bufsurf->list, &sbuf->surfaces);333334/* Set the new bind flags for this buffer resource */335sbuf->bind_flags = bind_flags;336337return PIPE_OK;338}339340341/**342* Start using the specified surface for this buffer resource.343*/344void345svga_buffer_bind_host_surface(struct svga_context *svga,346struct svga_buffer *sbuf,347struct svga_buffer_surface *bufsurf)348{349/* Update the to-bind surface */350assert(bufsurf->handle);351assert(sbuf->handle);352353/* If we are switching from stream output to other buffer,354* make sure to copy the buffer content.355*/356if (sbuf->bind_flags & PIPE_BIND_STREAM_OUTPUT) {357SVGA_RETRY(svga, SVGA3D_vgpu10_BufferCopy(svga->swc, sbuf->handle,358bufsurf->handle,3590, 0, sbuf->b.width0));360}361362/* Set this surface as the current one */363sbuf->handle = bufsurf->handle;364sbuf->key = bufsurf->key;365sbuf->bind_flags = bufsurf->bind_flags;366}367368369/**370* Prepare a host surface that can be used as indicated in the371* tobind_flags. If the existing host surface is not created372* with the necessary binding flags and if the new bind flags can be373* combined with the existing bind flags, then we will recreate a374* new surface with the combined bind flags. Otherwise, we will create375* a surface for that incompatible bind flags.376* For example, if a stream output buffer is reused as a constant buffer,377* since constant buffer surface cannot be bound as a stream output surface,378* two surfaces will be created, one for stream output,379* and another one for constant buffer.380*/381enum pipe_error382svga_buffer_validate_host_surface(struct svga_context *svga,383struct svga_buffer *sbuf,384unsigned tobind_flags)385{386struct svga_buffer_surface *bufsurf;387enum pipe_error ret = PIPE_OK;388389/* Flush any pending upload first */390svga_buffer_upload_flush(svga, sbuf);391392/* First check from the cached buffer surface list to see if there is393* already a buffer surface that has the requested bind flags, or394* surface with compatible bind flags that can be promoted.395*/396bufsurf = svga_buffer_get_host_surface(sbuf, tobind_flags);397398if (bufsurf) {399if ((bufsurf->bind_flags & tobind_flags) == tobind_flags) {400/* there is a surface with the requested bind flags */401svga_buffer_bind_host_surface(svga, sbuf, bufsurf);402} else {403404/* Recreate a host surface with the combined bind flags */405ret = svga_buffer_recreate_host_surface(svga, sbuf,406bufsurf->bind_flags |407tobind_flags);408409/* Destroy the old surface */410svga_screen_surface_destroy(svga_screen(sbuf->b.screen),411&bufsurf->key, &bufsurf->handle);412413list_del(&bufsurf->list);414FREE(bufsurf);415}416} else {417/* Need to create a new surface if the bind flags are incompatible,418* such as constant buffer surface & stream output surface.419*/420ret = svga_buffer_recreate_host_surface(svga, sbuf,421tobind_flags);422}423return ret;424}425426427void428svga_buffer_destroy_host_surface(struct svga_screen *ss,429struct svga_buffer *sbuf)430{431struct svga_buffer_surface *bufsurf, *next;432433LIST_FOR_EACH_ENTRY_SAFE(bufsurf, next, &sbuf->surfaces, list) {434SVGA_DBG(DEBUG_DMA, " ungrab sid %p sz %d\n",435bufsurf->handle, sbuf->b.width0);436svga_screen_surface_destroy(ss, &bufsurf->key, &bufsurf->handle);437FREE(bufsurf);438}439}440441442/**443* Insert a number of preliminary UPDATE_GB_IMAGE commands in the444* command buffer, equal to the current number of mapped ranges.445* The UPDATE_GB_IMAGE commands will be patched with the446* actual ranges just before flush.447*/448static enum pipe_error449svga_buffer_upload_gb_command(struct svga_context *svga,450struct svga_buffer *sbuf)451{452struct svga_winsys_context *swc = svga->swc;453SVGA3dCmdUpdateGBImage *update_cmd;454struct svga_3d_update_gb_image *whole_update_cmd = NULL;455const uint32 numBoxes = sbuf->map.num_ranges;456struct pipe_resource *dummy;457unsigned i;458459if (swc->force_coherent || sbuf->key.coherent)460return PIPE_OK;461462assert(svga_have_gb_objects(svga));463assert(numBoxes);464assert(sbuf->dma.updates == NULL);465466if (sbuf->dma.flags.discard) {467struct svga_3d_invalidate_gb_image *cicmd = NULL;468SVGA3dCmdInvalidateGBImage *invalidate_cmd;469const unsigned total_commands_size =470sizeof(*invalidate_cmd) + numBoxes * sizeof(*whole_update_cmd);471472/* Allocate FIFO space for one INVALIDATE_GB_IMAGE command followed by473* 'numBoxes' UPDATE_GB_IMAGE commands. Allocate all at once rather474* than with separate commands because we need to properly deal with475* filling the command buffer.476*/477invalidate_cmd = SVGA3D_FIFOReserve(swc,478SVGA_3D_CMD_INVALIDATE_GB_IMAGE,479total_commands_size, 1 + numBoxes);480if (!invalidate_cmd)481return PIPE_ERROR_OUT_OF_MEMORY;482483cicmd = container_of(invalidate_cmd, struct svga_3d_invalidate_gb_image, body);484cicmd->header.size = sizeof(*invalidate_cmd);485swc->surface_relocation(swc, &invalidate_cmd->image.sid, NULL,486sbuf->handle,487(SVGA_RELOC_WRITE |488SVGA_RELOC_INTERNAL |489SVGA_RELOC_DMA));490invalidate_cmd->image.face = 0;491invalidate_cmd->image.mipmap = 0;492493/* The whole_update_command is a SVGA3dCmdHeader plus the494* SVGA3dCmdUpdateGBImage command.495*/496whole_update_cmd = (struct svga_3d_update_gb_image *) &invalidate_cmd[1];497/* initialize the first UPDATE_GB_IMAGE command */498whole_update_cmd->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE;499update_cmd = &whole_update_cmd->body;500501} else {502/* Allocate FIFO space for 'numBoxes' UPDATE_GB_IMAGE commands */503const unsigned total_commands_size =504sizeof(*update_cmd) + (numBoxes - 1) * sizeof(*whole_update_cmd);505506update_cmd = SVGA3D_FIFOReserve(swc,507SVGA_3D_CMD_UPDATE_GB_IMAGE,508total_commands_size, numBoxes);509if (!update_cmd)510return PIPE_ERROR_OUT_OF_MEMORY;511512/* The whole_update_command is a SVGA3dCmdHeader plus the513* SVGA3dCmdUpdateGBImage command.514*/515whole_update_cmd = container_of(update_cmd, struct svga_3d_update_gb_image, body);516}517518/* Init the first UPDATE_GB_IMAGE command */519whole_update_cmd->header.size = sizeof(*update_cmd);520swc->surface_relocation(swc, &update_cmd->image.sid, NULL, sbuf->handle,521SVGA_RELOC_WRITE | SVGA_RELOC_INTERNAL);522update_cmd->image.face = 0;523update_cmd->image.mipmap = 0;524525/* Save pointer to the first UPDATE_GB_IMAGE command so that we can526* fill in the box info below.527*/528sbuf->dma.updates = whole_update_cmd;529530/*531* Copy the face, mipmap, etc. info to all subsequent commands.532* Also do the surface relocation for each subsequent command.533*/534for (i = 1; i < numBoxes; ++i) {535whole_update_cmd++;536memcpy(whole_update_cmd, sbuf->dma.updates, sizeof(*whole_update_cmd));537538swc->surface_relocation(swc, &whole_update_cmd->body.image.sid, NULL,539sbuf->handle,540SVGA_RELOC_WRITE | SVGA_RELOC_INTERNAL);541}542543/* Increment reference count */544sbuf->dma.svga = svga;545dummy = NULL;546pipe_resource_reference(&dummy, &sbuf->b);547SVGA_FIFOCommitAll(swc);548549swc->hints |= SVGA_HINT_FLAG_CAN_PRE_FLUSH;550sbuf->dma.flags.discard = FALSE;551552svga->hud.num_resource_updates++;553554return PIPE_OK;555}556557558/**559* Issue DMA commands to transfer guest memory to the host.560* Note that the memory segments (offset, size) will be patched in561* later in the svga_buffer_upload_flush() function.562*/563static enum pipe_error564svga_buffer_upload_hb_command(struct svga_context *svga,565struct svga_buffer *sbuf)566{567struct svga_winsys_context *swc = svga->swc;568struct svga_winsys_buffer *guest = sbuf->hwbuf;569struct svga_winsys_surface *host = sbuf->handle;570const SVGA3dTransferType transfer = SVGA3D_WRITE_HOST_VRAM;571SVGA3dCmdSurfaceDMA *cmd;572const uint32 numBoxes = sbuf->map.num_ranges;573SVGA3dCopyBox *boxes;574SVGA3dCmdSurfaceDMASuffix *pSuffix;575unsigned region_flags;576unsigned surface_flags;577struct pipe_resource *dummy;578579assert(!svga_have_gb_objects(svga));580581if (transfer == SVGA3D_WRITE_HOST_VRAM) {582region_flags = SVGA_RELOC_READ;583surface_flags = SVGA_RELOC_WRITE;584}585else if (transfer == SVGA3D_READ_HOST_VRAM) {586region_flags = SVGA_RELOC_WRITE;587surface_flags = SVGA_RELOC_READ;588}589else {590assert(0);591return PIPE_ERROR_BAD_INPUT;592}593594assert(numBoxes);595596cmd = SVGA3D_FIFOReserve(swc,597SVGA_3D_CMD_SURFACE_DMA,598sizeof *cmd + numBoxes * sizeof *boxes + sizeof *pSuffix,5992);600if (!cmd)601return PIPE_ERROR_OUT_OF_MEMORY;602603swc->region_relocation(swc, &cmd->guest.ptr, guest, 0, region_flags);604cmd->guest.pitch = 0;605606swc->surface_relocation(swc, &cmd->host.sid, NULL, host, surface_flags);607cmd->host.face = 0;608cmd->host.mipmap = 0;609610cmd->transfer = transfer;611612sbuf->dma.boxes = (SVGA3dCopyBox *)&cmd[1];613sbuf->dma.svga = svga;614615/* Increment reference count */616dummy = NULL;617pipe_resource_reference(&dummy, &sbuf->b);618619pSuffix = (SVGA3dCmdSurfaceDMASuffix *)((uint8_t*)cmd + sizeof *cmd + numBoxes * sizeof *boxes);620pSuffix->suffixSize = sizeof *pSuffix;621pSuffix->maximumOffset = sbuf->b.width0;622pSuffix->flags = sbuf->dma.flags;623624SVGA_FIFOCommitAll(swc);625626swc->hints |= SVGA_HINT_FLAG_CAN_PRE_FLUSH;627sbuf->dma.flags.discard = FALSE;628629svga->hud.num_buffer_uploads++;630631return PIPE_OK;632}633634635/**636* Issue commands to transfer guest memory to the host.637*/638static enum pipe_error639svga_buffer_upload_command(struct svga_context *svga, struct svga_buffer *sbuf)640{641if (svga_have_gb_objects(svga)) {642return svga_buffer_upload_gb_command(svga, sbuf);643} else {644return svga_buffer_upload_hb_command(svga, sbuf);645}646}647648649/**650* Patch up the upload DMA command reserved by svga_buffer_upload_command651* with the final ranges.652*/653void654svga_buffer_upload_flush(struct svga_context *svga, struct svga_buffer *sbuf)655{656unsigned i;657struct pipe_resource *dummy;658659if (!sbuf->dma.pending || svga->swc->force_coherent ||660sbuf->key.coherent) {661//debug_printf("no dma pending on buffer\n");662return;663}664665assert(sbuf->handle);666assert(sbuf->map.num_ranges);667assert(sbuf->dma.svga == svga);668669/*670* Patch the DMA/update command with the final copy box.671*/672if (svga_have_gb_objects(svga)) {673struct svga_3d_update_gb_image *update = sbuf->dma.updates;674675assert(update);676677for (i = 0; i < sbuf->map.num_ranges; ++i, ++update) {678SVGA3dBox *box = &update->body.box;679680SVGA_DBG(DEBUG_DMA, " bytes %u - %u\n",681sbuf->map.ranges[i].start, sbuf->map.ranges[i].end);682683box->x = sbuf->map.ranges[i].start;684box->y = 0;685box->z = 0;686box->w = sbuf->map.ranges[i].end - sbuf->map.ranges[i].start;687box->h = 1;688box->d = 1;689690assert(box->x <= sbuf->b.width0);691assert(box->x + box->w <= sbuf->b.width0);692693svga->hud.num_bytes_uploaded += box->w;694svga->hud.num_buffer_uploads++;695}696}697else {698assert(sbuf->hwbuf);699assert(sbuf->dma.boxes);700SVGA_DBG(DEBUG_DMA, "dma to sid %p\n", sbuf->handle);701702for (i = 0; i < sbuf->map.num_ranges; ++i) {703SVGA3dCopyBox *box = sbuf->dma.boxes + i;704705SVGA_DBG(DEBUG_DMA, " bytes %u - %u\n",706sbuf->map.ranges[i].start, sbuf->map.ranges[i].end);707708box->x = sbuf->map.ranges[i].start;709box->y = 0;710box->z = 0;711box->w = sbuf->map.ranges[i].end - sbuf->map.ranges[i].start;712box->h = 1;713box->d = 1;714box->srcx = sbuf->map.ranges[i].start;715box->srcy = 0;716box->srcz = 0;717718assert(box->x <= sbuf->b.width0);719assert(box->x + box->w <= sbuf->b.width0);720721svga->hud.num_bytes_uploaded += box->w;722svga->hud.num_buffer_uploads++;723}724}725726/* Reset sbuf for next use/upload */727728sbuf->map.num_ranges = 0;729730assert(sbuf->head.prev && sbuf->head.next);731list_del(&sbuf->head); /* remove from svga->dirty_buffers list */732sbuf->dma.pending = FALSE;733sbuf->dma.flags.discard = FALSE;734sbuf->dma.flags.unsynchronized = FALSE;735736sbuf->dma.svga = NULL;737sbuf->dma.boxes = NULL;738sbuf->dma.updates = NULL;739740/* Decrement reference count (and potentially destroy) */741dummy = &sbuf->b;742pipe_resource_reference(&dummy, NULL);743}744745746/**747* Note a dirty range.748*749* This function only notes the range down. It doesn't actually emit a DMA750* upload command. That only happens when a context tries to refer to this751* buffer, and the DMA upload command is added to that context's command752* buffer.753*754* We try to lump as many contiguous DMA transfers together as possible.755*/756void757svga_buffer_add_range(struct svga_buffer *sbuf, unsigned start, unsigned end)758{759unsigned i;760unsigned nearest_range;761unsigned nearest_dist;762763assert(end > start);764765if (sbuf->map.num_ranges < SVGA_BUFFER_MAX_RANGES) {766nearest_range = sbuf->map.num_ranges;767nearest_dist = ~0;768} else {769nearest_range = SVGA_BUFFER_MAX_RANGES - 1;770nearest_dist = 0;771}772773/*774* Try to grow one of the ranges.775*/776for (i = 0; i < sbuf->map.num_ranges; ++i) {777const int left_dist = start - sbuf->map.ranges[i].end;778const int right_dist = sbuf->map.ranges[i].start - end;779const int dist = MAX2(left_dist, right_dist);780781if (dist <= 0) {782/*783* Ranges are contiguous or overlapping -- extend this one and return.784*785* Note that it is not this function's task to prevent overlapping786* ranges, as the GMR was already given so it is too late to do787* anything. If the ranges overlap here it must surely be because788* PIPE_MAP_UNSYNCHRONIZED was set.789*/790sbuf->map.ranges[i].start = MIN2(sbuf->map.ranges[i].start, start);791sbuf->map.ranges[i].end = MAX2(sbuf->map.ranges[i].end, end);792return;793}794else {795/*796* Discontiguous ranges -- keep track of the nearest range.797*/798if (dist < nearest_dist) {799nearest_range = i;800nearest_dist = dist;801}802}803}804805/*806* We cannot add a new range to an existing DMA command, so patch-up the807* pending DMA upload and start clean.808*/809810svga_buffer_upload_flush(sbuf->dma.svga, sbuf);811812assert(!sbuf->dma.pending);813assert(!sbuf->dma.svga);814assert(!sbuf->dma.boxes);815816if (sbuf->map.num_ranges < SVGA_BUFFER_MAX_RANGES) {817/*818* Add a new range.819*/820821sbuf->map.ranges[sbuf->map.num_ranges].start = start;822sbuf->map.ranges[sbuf->map.num_ranges].end = end;823++sbuf->map.num_ranges;824} else {825/*826* Everything else failed, so just extend the nearest range.827*828* It is OK to do this because we always keep a local copy of the829* host buffer data, for SW TNL, and the host never modifies the buffer.830*/831832assert(nearest_range < SVGA_BUFFER_MAX_RANGES);833assert(nearest_range < sbuf->map.num_ranges);834sbuf->map.ranges[nearest_range].start =835MIN2(sbuf->map.ranges[nearest_range].start, start);836sbuf->map.ranges[nearest_range].end =837MAX2(sbuf->map.ranges[nearest_range].end, end);838}839}840841842843/**844* Copy the contents of the malloc buffer to a hardware buffer.845*/846static enum pipe_error847svga_buffer_update_hw(struct svga_context *svga, struct svga_buffer *sbuf,848unsigned bind_flags)849{850assert(!sbuf->user);851if (!svga_buffer_has_hw_storage(sbuf)) {852struct svga_screen *ss = svga_screen(sbuf->b.screen);853enum pipe_error ret;854boolean retry;855void *map;856unsigned i;857858assert(sbuf->swbuf);859if (!sbuf->swbuf)860return PIPE_ERROR;861862ret = svga_buffer_create_hw_storage(svga_screen(sbuf->b.screen), sbuf,863bind_flags);864if (ret != PIPE_OK)865return ret;866867mtx_lock(&ss->swc_mutex);868map = svga_buffer_hw_storage_map(svga, sbuf, PIPE_MAP_WRITE, &retry);869assert(map);870assert(!retry);871if (!map) {872mtx_unlock(&ss->swc_mutex);873svga_buffer_destroy_hw_storage(ss, sbuf);874return PIPE_ERROR;875}876877/* Copy data from malloc'd swbuf to the new hardware buffer */878for (i = 0; i < sbuf->map.num_ranges; i++) {879unsigned start = sbuf->map.ranges[i].start;880unsigned len = sbuf->map.ranges[i].end - start;881memcpy((uint8_t *) map + start, (uint8_t *) sbuf->swbuf + start, len);882}883884if (svga->swc->force_coherent || sbuf->key.coherent)885sbuf->map.num_ranges = 0;886887svga_buffer_hw_storage_unmap(svga, sbuf);888889/* This user/malloc buffer is now indistinguishable from a gpu buffer */890assert(sbuf->map.count == 0);891if (sbuf->map.count == 0) {892if (sbuf->user)893sbuf->user = FALSE;894else895align_free(sbuf->swbuf);896sbuf->swbuf = NULL;897}898899mtx_unlock(&ss->swc_mutex);900}901902return PIPE_OK;903}904905906/**907* Upload the buffer to the host in a piecewise fashion.908*909* Used when the buffer is too big to fit in the GMR aperture.910* This function should never get called in the guest-backed case911* since we always have a full-sized hardware storage backing the912* host surface.913*/914static enum pipe_error915svga_buffer_upload_piecewise(struct svga_screen *ss,916struct svga_context *svga,917struct svga_buffer *sbuf)918{919struct svga_winsys_screen *sws = ss->sws;920const unsigned alignment = sizeof(void *);921const unsigned usage = 0;922unsigned i;923924assert(sbuf->map.num_ranges);925assert(!sbuf->dma.pending);926assert(!svga_have_gb_objects(svga));927928SVGA_DBG(DEBUG_DMA, "dma to sid %p\n", sbuf->handle);929930for (i = 0; i < sbuf->map.num_ranges; ++i) {931const struct svga_buffer_range *range = &sbuf->map.ranges[i];932unsigned offset = range->start;933unsigned size = range->end - range->start;934935while (offset < range->end) {936struct svga_winsys_buffer *hwbuf;937uint8_t *map;938939if (offset + size > range->end)940size = range->end - offset;941942hwbuf = sws->buffer_create(sws, alignment, usage, size);943while (!hwbuf) {944size /= 2;945if (!size)946return PIPE_ERROR_OUT_OF_MEMORY;947hwbuf = sws->buffer_create(sws, alignment, usage, size);948}949950SVGA_DBG(DEBUG_DMA, " bytes %u - %u\n",951offset, offset + size);952953map = sws->buffer_map(sws, hwbuf,954PIPE_MAP_WRITE |955PIPE_MAP_DISCARD_RANGE);956assert(map);957if (map) {958memcpy(map, (const char *) sbuf->swbuf + offset, size);959sws->buffer_unmap(sws, hwbuf);960}961962SVGA_RETRY(svga, SVGA3D_BufferDMA(svga->swc,963hwbuf, sbuf->handle,964SVGA3D_WRITE_HOST_VRAM,965size, 0, offset, sbuf->dma.flags));966sbuf->dma.flags.discard = FALSE;967968sws->buffer_destroy(sws, hwbuf);969970offset += size;971}972}973974sbuf->map.num_ranges = 0;975976return PIPE_OK;977}978979980/**981* Get (or create/upload) the winsys surface handle so that we can982* refer to this buffer in fifo commands.983* This function will create the host surface, and in the GB case also the984* hardware storage. In the non-GB case, the hardware storage will be created985* if there are mapped ranges and the data is currently in a malloc'ed buffer.986*/987struct svga_winsys_surface *988svga_buffer_handle(struct svga_context *svga, struct pipe_resource *buf,989unsigned tobind_flags)990{991struct pipe_screen *screen = svga->pipe.screen;992struct svga_screen *ss = svga_screen(screen);993struct svga_buffer *sbuf;994enum pipe_error ret;995996if (!buf)997return NULL;998999sbuf = svga_buffer(buf);10001001assert(!sbuf->user);10021003if (sbuf->handle) {1004if ((sbuf->bind_flags & tobind_flags) != tobind_flags) {1005/* If the allocated resource's bind flags do not include the1006* requested bind flags, validate the host surface.1007*/1008ret = svga_buffer_validate_host_surface(svga, sbuf, tobind_flags);1009if (ret != PIPE_OK)1010return NULL;1011}1012} else {1013/* If there is no resource handle yet, then combine the buffer bind1014* flags and the tobind_flags if they are compatible.1015* If not, just use the tobind_flags for creating the resource handle.1016*/1017if (compatible_bind_flags(sbuf->bind_flags, tobind_flags))1018sbuf->bind_flags = sbuf->bind_flags | tobind_flags;1019else1020sbuf->bind_flags = tobind_flags;10211022assert((sbuf->bind_flags & tobind_flags) == tobind_flags);10231024/* This call will set sbuf->handle */1025if (svga_have_gb_objects(svga)) {1026ret = svga_buffer_update_hw(svga, sbuf, sbuf->bind_flags);1027} else {1028ret = svga_buffer_create_host_surface(ss, sbuf, sbuf->bind_flags);1029}1030if (ret != PIPE_OK)1031return NULL;1032}10331034assert(sbuf->handle);1035if (svga->swc->force_coherent || sbuf->key.coherent)1036return sbuf->handle;10371038if (sbuf->map.num_ranges) {1039if (!sbuf->dma.pending) {1040/* No pending DMA/update commands yet. */10411042/* Migrate the data from swbuf -> hwbuf if necessary */1043ret = svga_buffer_update_hw(svga, sbuf, sbuf->bind_flags);1044if (ret == PIPE_OK) {1045/* Emit DMA or UpdateGBImage commands */1046SVGA_RETRY_OOM(svga, ret, svga_buffer_upload_command(svga, sbuf));1047if (ret == PIPE_OK) {1048sbuf->dma.pending = TRUE;1049assert(!sbuf->head.prev && !sbuf->head.next);1050list_addtail(&sbuf->head, &svga->dirty_buffers);1051}1052}1053else if (ret == PIPE_ERROR_OUT_OF_MEMORY) {1054/*1055* The buffer is too big to fit in the GMR aperture, so break it in1056* smaller pieces.1057*/1058ret = svga_buffer_upload_piecewise(ss, svga, sbuf);1059}10601061if (ret != PIPE_OK) {1062/*1063* Something unexpected happened above. There is very little that1064* we can do other than proceeding while ignoring the dirty ranges.1065*/1066assert(0);1067sbuf->map.num_ranges = 0;1068}1069}1070else {1071/*1072* There a pending dma already. Make sure it is from this context.1073*/1074assert(sbuf->dma.svga == svga);1075}1076}10771078assert(sbuf->map.num_ranges == 0 || sbuf->dma.pending);10791080return sbuf->handle;1081}108210831084void1085svga_context_flush_buffers(struct svga_context *svga)1086{1087struct list_head *curr, *next;10881089SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_BUFFERSFLUSH);10901091curr = svga->dirty_buffers.next;1092next = curr->next;1093while (curr != &svga->dirty_buffers) {1094struct svga_buffer *sbuf = LIST_ENTRY(struct svga_buffer, curr, head);10951096assert(p_atomic_read(&sbuf->b.reference.count) != 0);1097assert(sbuf->dma.pending);10981099svga_buffer_upload_flush(svga, sbuf);11001101curr = next;1102next = curr->next;1103}11041105SVGA_STATS_TIME_POP(svga_sws(svga));1106}110711081109