Path: blob/21.2-virgl/src/gallium/frontends/nine/buffer9.c
4561 views
/*1* Copyright 2011 Joakim Sindholt <[email protected]>2* Copyright 2015 Patrick Rudolph <[email protected]>3*4* Permission is hereby granted, free of charge, to any person obtaining a5* copy of this software and associated documentation files (the "Software"),6* to deal in the Software without restriction, including without limitation7* on the rights to use, copy, modify, merge, publish, distribute, sub8* license, and/or sell copies of the Software, and to permit persons to whom9* the Software is furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice (including the next12* paragraph) shall be included in all copies or substantial portions of the13* Software.14*15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL18* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,19* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR20* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE21* USE OR OTHER DEALINGS IN THE SOFTWARE. */2223#include "buffer9.h"24#include "device9.h"25#include "indexbuffer9.h"26#include "nine_buffer_upload.h"27#include "nine_helpers.h"28#include "nine_pipe.h"2930#include "pipe/p_screen.h"31#include "pipe/p_context.h"32#include "pipe/p_state.h"33#include "pipe/p_defines.h"34#include "pipe/p_format.h"35#include "util/u_box.h"36#include "util/u_inlines.h"3738#define DBG_CHANNEL (DBG_INDEXBUFFER|DBG_VERTEXBUFFER)3940HRESULT41NineBuffer9_ctor( struct NineBuffer9 *This,42struct NineUnknownParams *pParams,43D3DRESOURCETYPE Type,44DWORD Usage,45UINT Size,46D3DPOOL Pool )47{48struct pipe_resource *info = &This->base.info;49HRESULT hr;5051DBG("This=%p Size=0x%x Usage=%x Pool=%u\n", This, Size, Usage, Pool);5253user_assert(Pool != D3DPOOL_SCRATCH, D3DERR_INVALIDCALL);5455This->maps = MALLOC(sizeof(struct NineTransfer));56if (!This->maps)57return E_OUTOFMEMORY;58This->nlocks = 0;59This->nmaps = 0;60This->maxmaps = 1;61This->size = Size;6263info->screen = pParams->device->screen;64info->target = PIPE_BUFFER;65info->format = PIPE_FORMAT_R8_UNORM;66info->width0 = Size;67info->flags = 0;6869/* Note: WRITEONLY is just tip for resource placement, the resource70* can still be read (but slower). */71info->bind = (Type == D3DRTYPE_INDEXBUFFER) ? PIPE_BIND_INDEX_BUFFER : PIPE_BIND_VERTEX_BUFFER;7273/* Software vertex processing:74* If the device is full software vertex processing,75* then the buffer is supposed to be used only for sw processing.76* For mixed vertex processing, buffers with D3DUSAGE_SOFTWAREPROCESSING77* can be used for both sw and hw processing.78* These buffers are expected to be stored in RAM.79* Apps expect locking the full buffer with no flags, then80* render a a few primitive, then locking again, etc81* to be a fast pattern. Only the SYSTEMMEM DYNAMIC path82* will give that pattern ok performance in our case.83* An alternative would be when sw processing is detected to84* convert Draw* calls to Draw*Up calls. */85if (Usage & D3DUSAGE_SOFTWAREPROCESSING ||86pParams->device->params.BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) {87Pool = D3DPOOL_SYSTEMMEM;88Usage |= D3DUSAGE_DYNAMIC;89/* Note: the application cannot retrieve Pool and Usage */90}9192/* Always use the DYNAMIC path for SYSTEMMEM.93* If the app uses the vertex buffer is a dynamic fashion,94* this is going to be very significantly faster that way.95* If the app uses the vertex buffer in a static fashion,96* instead of being filled all at once, the buffer will be filled97* little per little, until it is fully filled, thus the perf hit98* will be very small. */99if (Pool == D3DPOOL_SYSTEMMEM)100Usage |= D3DUSAGE_DYNAMIC;101102/* It is hard to find clear information on where to place the buffer in103* memory depending on the flag.104* MSDN: resources are static, except for those with DYNAMIC, thus why you105* can only use DISCARD on them.106* ATI doc: The driver has the liberty it wants for having things static107* or not.108* MANAGED: Ram + uploads to Vram copy at unlock (msdn and nvidia doc say109* at first draw call using the buffer)110* DEFAULT + Usage = 0 => System memory backing for easy read access111* (That doc is very unclear on the details, like whether some copies to112* vram copy are involved or not).113* DEFAULT + WRITEONLY => Vram114* DEFAULT + WRITEONLY + DYNAMIC => Either Vram buffer or GTT_WC, depending on what the driver wants.115* SYSTEMMEM: Same as MANAGED, but handled by the driver instead of the runtime (which means116* some small behavior differences between vendors). Implementing exactly as MANAGED should117* be fine.118*/119if (Pool == D3DPOOL_SYSTEMMEM && Usage & D3DUSAGE_DYNAMIC)120info->usage = PIPE_USAGE_STREAM;121else if (Pool != D3DPOOL_DEFAULT)122info->usage = PIPE_USAGE_DEFAULT;123else if (Usage & D3DUSAGE_DYNAMIC && Usage & D3DUSAGE_WRITEONLY)124info->usage = PIPE_USAGE_STREAM;125else if (Usage & D3DUSAGE_WRITEONLY)126info->usage = PIPE_USAGE_DEFAULT;127/* For the remaining two, PIPE_USAGE_STAGING would probably be128* a good fit according to the doc. However it seems rather a mistake129* from apps to use these (mistakes that do really happen). Try130* to put the flags that are the best compromise between the real131* behaviour and what buggy apps should get for better performance. */132else if (Usage & D3DUSAGE_DYNAMIC)133info->usage = PIPE_USAGE_STREAM;134else135info->usage = PIPE_USAGE_DYNAMIC;136137/* When Writeonly is not set, we don't want to enable the138* optimizations */139This->discard_nooverwrite_only = !!(Usage & D3DUSAGE_WRITEONLY) &&140pParams->device->buffer_upload;141/* if (pDesc->Usage & D3DUSAGE_DONOTCLIP) { } */142/* if (pDesc->Usage & D3DUSAGE_NONSECURE) { } */143/* if (pDesc->Usage & D3DUSAGE_NPATCHES) { } */144/* if (pDesc->Usage & D3DUSAGE_POINTS) { } */145/* if (pDesc->Usage & D3DUSAGE_RTPATCHES) { } */146/* if (pDesc->Usage & D3DUSAGE_TEXTAPI) { } */147148info->height0 = 1;149info->depth0 = 1;150info->array_size = 1;151info->last_level = 0;152info->nr_samples = 0;153info->nr_storage_samples = 0;154155hr = NineResource9_ctor(&This->base, pParams, NULL, TRUE,156Type, Pool, Usage);157158if (FAILED(hr))159return hr;160161if (Pool != D3DPOOL_DEFAULT) {162This->managed.data = align_calloc(163nine_format_get_level_alloc_size(This->base.info.format,164Size, 1, 0), 32);165if (!This->managed.data)166return E_OUTOFMEMORY;167This->managed.dirty = TRUE;168u_box_1d(0, Size, &This->managed.dirty_box);169u_box_1d(0, 0, &This->managed.valid_region);170u_box_1d(0, 0, &This->managed.required_valid_region);171u_box_1d(0, 0, &This->managed.filled_region);172This->managed.can_unsynchronized = true;173This->managed.num_worker_thread_syncs = 0;174list_inithead(&This->managed.list);175list_inithead(&This->managed.list2);176list_add(&This->managed.list2, &pParams->device->managed_buffers);177}178179return D3D_OK;180}181182void183NineBuffer9_dtor( struct NineBuffer9 *This )184{185DBG("This=%p\n", This);186187if (This->maps) {188while (This->nlocks) {189NineBuffer9_Unlock(This);190}191assert(!This->nmaps);192FREE(This->maps);193}194195if (This->base.pool != D3DPOOL_DEFAULT) {196if (This->managed.data)197align_free(This->managed.data);198if (list_is_linked(&This->managed.list))199list_del(&This->managed.list);200if (list_is_linked(&This->managed.list2))201list_del(&This->managed.list2);202}203204if (This->buf)205nine_upload_release_buffer(This->base.base.device->buffer_upload, This->buf);206207NineResource9_dtor(&This->base);208}209210struct pipe_resource *211NineBuffer9_GetResource( struct NineBuffer9 *This, unsigned *offset )212{213if (This->buf)214return nine_upload_buffer_resource_and_offset(This->buf, offset);215*offset = 0;216return NineResource9_GetResource(&This->base);217}218219static void220NineBuffer9_RebindIfRequired( struct NineBuffer9 *This,221struct NineDevice9 *device,222struct pipe_resource *resource,223unsigned offset )224{225int i;226227if (!This->bind_count)228return;229for (i = 0; i < device->caps.MaxStreams; i++) {230if (device->state.stream[i] == (struct NineVertexBuffer9 *)This)231nine_context_set_stream_source_apply(device, i,232resource,233device->state.vtxbuf[i].buffer_offset + offset,234device->state.vtxbuf[i].stride);235}236if (device->state.idxbuf == (struct NineIndexBuffer9 *)This)237nine_context_set_indices_apply(device, resource,238((struct NineIndexBuffer9 *)This)->index_size,239offset);240}241242HRESULT NINE_WINAPI243NineBuffer9_Lock( struct NineBuffer9 *This,244UINT OffsetToLock,245UINT SizeToLock,246void **ppbData,247DWORD Flags )248{249struct NineDevice9 *device = This->base.base.device;250struct pipe_box box;251struct pipe_context *pipe;252void *data;253unsigned usage;254255DBG("This=%p(pipe=%p) OffsetToLock=0x%x, SizeToLock=0x%x, Flags=0x%x\n",256This, This->base.resource,257OffsetToLock, SizeToLock, Flags);258259user_assert(ppbData, E_POINTER);260261if (SizeToLock == 0) {262SizeToLock = This->size - OffsetToLock;263user_warn(OffsetToLock != 0);264}265266/* Write out of bound seems to have to be taken into account for these.267* TODO: Do more tests (is it only at buffer first lock ? etc).268* Since these buffers are supposed to be locked once and never269* writen again (MANAGED or DYNAMIC is used for the other uses cases),270* performance should be unaffected. */271if (!(This->base.usage & D3DUSAGE_DYNAMIC) && This->base.pool == D3DPOOL_DEFAULT)272SizeToLock = This->size - OffsetToLock;273274u_box_1d(OffsetToLock, SizeToLock, &box);275276if (This->base.pool != D3DPOOL_DEFAULT) {277/* MANAGED: READONLY doesn't dirty the buffer, nor278* wait the upload in the worker thread279* SYSTEMMEM: AMD/NVidia: All locks dirty the full buffer. Not on Intel280* For Nvidia, SYSTEMMEM behaves are if there is no worker thread.281* On AMD, READONLY and NOOVERWRITE do dirty the buffer, but do not sync the previous uploads282* in the worker thread. On Intel only NOOVERWRITE has that effect.283* We implement the AMD behaviour. */284if (This->base.pool == D3DPOOL_MANAGED) {285if (!(Flags & D3DLOCK_READONLY)) {286if (!This->managed.dirty) {287assert(list_is_empty(&This->managed.list));288This->managed.dirty = TRUE;289This->managed.dirty_box = box;290/* Flush if regions pending to be uploaded would be dirtied */291if (p_atomic_read(&This->managed.pending_upload)) {292u_box_intersect_1d(&box, &box, &This->managed.upload_pending_regions);293if (box.width != 0)294nine_csmt_process(This->base.base.device);295}296} else297u_box_union_1d(&This->managed.dirty_box, &This->managed.dirty_box, &box);298/* Tests trying to draw while the buffer is locked show that299* SYSTEMMEM/MANAGED buffers are made dirty at Lock time */300BASEBUF_REGISTER_UPDATE(This);301}302} else {303if (!(Flags & (D3DLOCK_READONLY|D3DLOCK_NOOVERWRITE)) &&304p_atomic_read(&This->managed.pending_upload)) {305This->managed.num_worker_thread_syncs++;306/* If we sync too often, pick the vertex_uploader path */307if (This->managed.num_worker_thread_syncs >= 3)308This->managed.can_unsynchronized = false;309nine_csmt_process(This->base.base.device);310/* Note: AS DISCARD is not relevant for SYSTEMMEM,311* NOOVERWRITE might have a similar meaning as what is312* in D3D7 doc. Basically that data from previous draws313* OF THIS FRAME are unaffected. As we flush csmt in Present(),314* we should be correct. In some parts of the doc, the notion315* of frame is implied to be related to Begin/EndScene(),316* but tests show NOOVERWRITE after EndScene() doesn't flush317* the csmt thread. */318}319This->managed.dirty = true;320u_box_1d(0, This->size, &This->managed.dirty_box); /* systemmem non-dynamic */321u_box_1d(0, 0, &This->managed.valid_region); /* systemmem dynamic */322BASEBUF_REGISTER_UPDATE(This);323}324325*ppbData = (char *)This->managed.data + OffsetToLock;326DBG("returning pointer %p\n", *ppbData);327This->nlocks++;328return D3D_OK;329}330331/* Driver ddi doc: READONLY is never passed to the device. So it can only332* have effect on things handled by the driver (MANAGED pool for example).333* Msdn doc: DISCARD and NOOVERWRITE are only for DYNAMIC.334* ATI doc: You can use DISCARD and NOOVERWRITE without DYNAMIC.335* Msdn doc: D3DLOCK_DONOTWAIT is not among the valid flags for buffers.336* Our tests: On win 7 nvidia, D3DLOCK_DONOTWAIT does return337* D3DERR_WASSTILLDRAWING if the resource is in use, except for DYNAMIC.338* Our tests: some apps do use both DISCARD and NOOVERWRITE at the same339* time. On windows it seems to return different pointer in some conditions,340* creation flags and drivers. However these tests indicate having341* NOOVERWRITE win is a valid behaviour (NVidia).342*/343344/* Have NOOVERWRITE win over DISCARD. This is allowed (see above) and345* it prevents overconsuming buffers if apps do use both at the same time. */346if ((Flags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE)) == (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE))347Flags &= ~D3DLOCK_DISCARD;348349if (Flags & D3DLOCK_DISCARD)350usage = PIPE_MAP_WRITE | PIPE_MAP_DISCARD_WHOLE_RESOURCE;351else if (Flags & D3DLOCK_NOOVERWRITE)352usage = PIPE_MAP_WRITE | PIPE_MAP_UNSYNCHRONIZED;353else354/* Do not ask for READ if writeonly and default pool (should be safe enough,355* as the doc says app shouldn't expect reading to work with writeonly). */356usage = (This->base.usage & D3DUSAGE_WRITEONLY) ?357PIPE_MAP_WRITE :358PIPE_MAP_READ_WRITE;359if (Flags & D3DLOCK_DONOTWAIT && !(This->base.usage & D3DUSAGE_DYNAMIC))360usage |= PIPE_MAP_DONTBLOCK;361362This->discard_nooverwrite_only &= !!(Flags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE));363364if (This->nmaps == This->maxmaps) {365struct NineTransfer *newmaps =366REALLOC(This->maps, sizeof(struct NineTransfer)*This->maxmaps,367sizeof(struct NineTransfer)*(This->maxmaps << 1));368if (newmaps == NULL)369return E_OUTOFMEMORY;370371This->maxmaps <<= 1;372This->maps = newmaps;373}374375if (This->buf && !This->discard_nooverwrite_only) {376struct pipe_box src_box;377unsigned offset;378struct pipe_resource *src_res;379DBG("Disabling nine_subbuffer for a buffer having"380"used a nine_subbuffer buffer\n");381/* Copy buffer content to the buffer resource, which382* we will now use.383* Note: The behaviour may be different from what is expected384* with double lock. However applications can't really make expectations385* about double locks, and don't really use them, so that's ok. */386src_res = nine_upload_buffer_resource_and_offset(This->buf, &offset);387u_box_1d(offset, This->size, &src_box);388389pipe = NineDevice9_GetPipe(device);390pipe->resource_copy_region(pipe, This->base.resource, 0, 0, 0, 0,391src_res, 0, &src_box);392/* Release previous resource */393if (This->nmaps >= 1)394This->maps[This->nmaps-1].should_destroy_buf = true;395else396nine_upload_release_buffer(device->buffer_upload, This->buf);397This->buf = NULL;398/* Rebind buffer */399NineBuffer9_RebindIfRequired(This, device, This->base.resource, 0);400}401402This->maps[This->nmaps].transfer = NULL;403This->maps[This->nmaps].is_pipe_secondary = false;404This->maps[This->nmaps].buf = NULL;405This->maps[This->nmaps].should_destroy_buf = false;406407if (This->discard_nooverwrite_only) {408if (This->buf && (Flags & D3DLOCK_DISCARD)) {409/* Release previous buffer */410if (This->nmaps >= 1)411This->maps[This->nmaps-1].should_destroy_buf = true;412else413nine_upload_release_buffer(device->buffer_upload, This->buf);414This->buf = NULL;415}416417if (!This->buf) {418unsigned offset;419struct pipe_resource *res;420This->buf = nine_upload_create_buffer(device->buffer_upload, This->base.info.width0);421res = nine_upload_buffer_resource_and_offset(This->buf, &offset);422NineBuffer9_RebindIfRequired(This, device, res, offset);423}424425if (This->buf) {426This->maps[This->nmaps].buf = This->buf;427This->nmaps++;428This->nlocks++;429DBG("Returning %p\n", nine_upload_buffer_get_map(This->buf) + OffsetToLock);430*ppbData = nine_upload_buffer_get_map(This->buf) + OffsetToLock;431return D3D_OK;432} else {433/* Fallback to normal path, and don't try again */434This->discard_nooverwrite_only = false;435}436}437438/* Previous mappings may need pending commands to write to the439* buffer (staging buffer for example). Before a NOOVERWRITE,440* we thus need a finish, to guarantee any upload is finished.441* Note for discard_nooverwrite_only we don't need to do this442* check as neither discard nor nooverwrite have issues there */443if (This->need_sync_if_nooverwrite && !(Flags & D3DLOCK_DISCARD) &&444(Flags & D3DLOCK_NOOVERWRITE)) {445struct pipe_screen *screen = NineDevice9_GetScreen(device);446struct pipe_fence_handle *fence = NULL;447448pipe = NineDevice9_GetPipe(device);449pipe->flush(pipe, &fence, 0);450(void) screen->fence_finish(screen, NULL, fence, PIPE_TIMEOUT_INFINITE);451screen->fence_reference(screen, &fence, NULL);452}453This->need_sync_if_nooverwrite = !(Flags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE));454455/* When csmt is active, we want to avoid stalls as much as possible,456* and thus we want to create a new resource on discard and map it457* with the secondary pipe, instead of waiting on the main pipe. */458if (Flags & D3DLOCK_DISCARD && device->csmt_active) {459struct pipe_screen *screen = NineDevice9_GetScreen(device);460struct pipe_resource *new_res = nine_resource_create_with_retry(device, screen, &This->base.info);461if (new_res) {462/* Use the new resource */463pipe_resource_reference(&This->base.resource, new_res);464pipe_resource_reference(&new_res, NULL);465usage = PIPE_MAP_WRITE | PIPE_MAP_UNSYNCHRONIZED;466NineBuffer9_RebindIfRequired(This, device, This->base.resource, 0);467This->maps[This->nmaps].is_pipe_secondary = TRUE;468}469} else if (Flags & D3DLOCK_NOOVERWRITE && device->csmt_active)470This->maps[This->nmaps].is_pipe_secondary = TRUE;471472if (This->maps[This->nmaps].is_pipe_secondary)473pipe = device->pipe_secondary;474else475pipe = NineDevice9_GetPipe(device);476477data = pipe->buffer_map(pipe, This->base.resource, 0,478usage, &box, &This->maps[This->nmaps].transfer);479480if (!data) {481DBG("pipe::buffer_map failed\n"482" usage = %x\n"483" box.x = %u\n"484" box.width = %u\n",485usage, box.x, box.width);486487if (Flags & D3DLOCK_DONOTWAIT)488return D3DERR_WASSTILLDRAWING;489return D3DERR_INVALIDCALL;490}491492DBG("returning pointer %p\n", data);493This->nmaps++;494This->nlocks++;495*ppbData = data;496497return D3D_OK;498}499500HRESULT NINE_WINAPI501NineBuffer9_Unlock( struct NineBuffer9 *This )502{503struct NineDevice9 *device = This->base.base.device;504struct pipe_context *pipe;505int i;506DBG("This=%p\n", This);507508user_assert(This->nlocks > 0, D3DERR_INVALIDCALL);509This->nlocks--;510if (This->nlocks > 0)511return D3D_OK; /* Pending unlocks. Wait all unlocks before unmapping */512513if (This->base.pool == D3DPOOL_DEFAULT) {514for (i = 0; i < This->nmaps; i++) {515if (!This->maps[i].buf) {516pipe = This->maps[i].is_pipe_secondary ?517device->pipe_secondary :518nine_context_get_pipe_acquire(device);519pipe->buffer_unmap(pipe, This->maps[i].transfer);520/* We need to flush in case the driver does implicit copies */521if (This->maps[i].is_pipe_secondary)522pipe->flush(pipe, NULL, 0);523else524nine_context_get_pipe_release(device);525} else if (This->maps[i].should_destroy_buf)526nine_upload_release_buffer(device->buffer_upload, This->maps[i].buf);527}528This->nmaps = 0;529}530return D3D_OK;531}532533void534NineBuffer9_SetDirty( struct NineBuffer9 *This )535{536assert(This->base.pool != D3DPOOL_DEFAULT);537538This->managed.dirty = TRUE;539u_box_1d(0, This->size, &This->managed.dirty_box);540BASEBUF_REGISTER_UPDATE(This);541}542543/* Try to remove b from a, supposed to include b */544static void u_box_try_remove_region_1d(struct pipe_box *dst,545const struct pipe_box *a,546const struct pipe_box *b)547{548int x, width;549if (a->x == b->x) {550x = a->x + b->width;551width = a->width - b->width;552} else if ((a->x + a->width) == (b->x + b->width)) {553x = a->x;554width = a->width - b->width;555} else {556x = a->x;557width = a->width;558}559dst->x = x;560dst->width = width;561}562563void564NineBuffer9_Upload( struct NineBuffer9 *This )565{566struct NineDevice9 *device = This->base.base.device;567unsigned upload_flags = 0;568struct pipe_box box_upload;569570assert(This->base.pool != D3DPOOL_DEFAULT && This->managed.dirty);571572if (This->base.pool == D3DPOOL_SYSTEMMEM && This->base.usage & D3DUSAGE_DYNAMIC) {573struct pipe_box region_already_valid;574struct pipe_box conflicting_region;575struct pipe_box *valid_region = &This->managed.valid_region;576struct pipe_box *required_valid_region = &This->managed.required_valid_region;577struct pipe_box *filled_region = &This->managed.filled_region;578/* Try to upload SYSTEMMEM DYNAMIC in an efficient fashion.579* Unlike non-dynamic for which we upload the whole dirty region, try to580* only upload the data needed for the draw. The draw call preparation581* fills This->managed.required_valid_region for that */582u_box_intersect_1d(®ion_already_valid,583valid_region,584required_valid_region);585/* If the required valid region is already valid, nothing to do */586if (region_already_valid.x == required_valid_region->x &&587region_already_valid.width == required_valid_region->width) {588/* Rebind if the region happens to be valid in the original buffer589* but we have since used vertex_uploader */590if (!This->managed.can_unsynchronized)591NineBuffer9_RebindIfRequired(This, device, This->base.resource, 0);592u_box_1d(0, 0, required_valid_region);593return;594}595/* (Try to) Remove valid areas from the region to upload */596u_box_try_remove_region_1d(&box_upload,597required_valid_region,598®ion_already_valid);599assert(box_upload.width > 0);600/* To maintain correctly the valid region, as we will do union later with601* box_upload, we must ensure box_upload is consecutive with valid_region */602if (box_upload.x > valid_region->x + valid_region->width && valid_region->width > 0) {603box_upload.width = box_upload.x + box_upload.width - (valid_region->x + valid_region->width);604box_upload.x = valid_region->x + valid_region->width;605} else if (box_upload.x + box_upload.width < valid_region->x && valid_region->width > 0) {606box_upload.width = valid_region->x - box_upload.x;607}608/* There is conflict if some areas, that are not valid but are filled for previous draw calls,609* intersect with the region we plan to upload. Note by construction valid_region IS610* included in filled_region, thus so is region_already_valid. */611u_box_intersect_1d(&conflicting_region, &box_upload, filled_region);612/* As box_upload could still contain region_already_valid, check the intersection613* doesn't happen to be exactly region_already_valid (it cannot be smaller, see above) */614if (This->managed.can_unsynchronized && (conflicting_region.width == 0 ||615(conflicting_region.x == region_already_valid.x &&616conflicting_region.width == region_already_valid.width))) {617/* No conflicts. */618upload_flags |= PIPE_MAP_UNSYNCHRONIZED;619} else {620/* We cannot use PIPE_MAP_UNSYNCHRONIZED. We must choose between no flag and DISCARD.621* Criterias to discard:622* . Most of the resource was filled (but some apps do allocate a big buffer623* to only use a small part in a round fashion)624* . The region to upload is very small compared to the filled region and625* at the start of the buffer (hints at round usage starting again)626* . The region to upload is very big compared to the required region627* . We have not discarded yet this frame628* If the buffer use pattern seems to sync the worker thread too often,629* revert to the vertex_uploader */630if (This->managed.num_worker_thread_syncs < 3 &&631(filled_region->width > (This->size / 2) ||632(10 * box_upload.width < filled_region->width &&633box_upload.x < (filled_region->x + filled_region->width)/2) ||634box_upload.width > 2 * required_valid_region->width ||635This->managed.frame_count_last_discard != device->frame_count)) {636/* Avoid DISCARDING too much by discarding only if most of the buffer637* has been used */638DBG_FLAG(DBG_INDEXBUFFER|DBG_VERTEXBUFFER,639"Uploading %p DISCARD: valid %d %d, filled %d %d, required %d %d, box_upload %d %d, required already_valid %d %d, conficting %d %d\n",640This, valid_region->x, valid_region->width, filled_region->x, filled_region->width,641required_valid_region->x, required_valid_region->width, box_upload.x, box_upload.width,642region_already_valid.x, region_already_valid.width, conflicting_region.x, conflicting_region.width643);644upload_flags |= PIPE_MAP_DISCARD_WHOLE_RESOURCE;645u_box_1d(0, 0, filled_region);646u_box_1d(0, 0, valid_region);647box_upload = This->managed.required_valid_region;648/* Rebind the buffer if we used intermediate alternative buffer */649if (!This->managed.can_unsynchronized)650NineBuffer9_RebindIfRequired(This, device, This->base.resource, 0);651This->managed.can_unsynchronized = true;652This->managed.frame_count_last_discard = device->frame_count;653} else {654/* Once we use without UNSYNCHRONIZED, we cannot use it anymore.655* Use a different buffer. */656unsigned buffer_offset = 0;657struct pipe_resource *resource = NULL;658This->managed.can_unsynchronized = false;659u_upload_data(device->vertex_uploader,660required_valid_region->x,661required_valid_region->width,66264,663This->managed.data + required_valid_region->x,664&buffer_offset,665&resource);666buffer_offset -= required_valid_region->x;667u_upload_unmap(device->vertex_uploader);668if (resource) {669NineBuffer9_RebindIfRequired(This, device, resource, buffer_offset);670/* Note: This only works because for these types of buffers this function671* is called before every draw call. Else it wouldn't work when the app672* rebinds buffers. In addition it needs this function to be called only673* once per buffers even if bound several times, which we do. */674u_box_1d(0, 0, required_valid_region);675pipe_resource_reference(&resource, NULL);676return;677}678}679}680681u_box_union_1d(filled_region,682filled_region,683&box_upload);684u_box_union_1d(valid_region,685valid_region,686&box_upload);687u_box_1d(0, 0, required_valid_region);688} else689box_upload = This->managed.dirty_box;690691if (box_upload.x == 0 && box_upload.width == This->size) {692upload_flags |= PIPE_MAP_DISCARD_WHOLE_RESOURCE;693}694695if (This->managed.pending_upload) {696u_box_union_1d(&This->managed.upload_pending_regions,697&This->managed.upload_pending_regions,698&box_upload);699} else {700This->managed.upload_pending_regions = box_upload;701}702703DBG_FLAG(DBG_INDEXBUFFER|DBG_VERTEXBUFFER,704"Uploading %p, offset=%d, size=%d, Flags=0x%x\n",705This, box_upload.x, box_upload.width, upload_flags);706nine_context_range_upload(device, &This->managed.pending_upload,707(struct NineUnknown *)This,708This->base.resource,709box_upload.x,710box_upload.width,711upload_flags,712(char *)This->managed.data + box_upload.x);713This->managed.dirty = FALSE;714}715716717