st/nine: Implement NineDevice9_GetAvailableTextureMem

Implement a device private memory counter similar to Win 7.

Only textures and surfaces increment vidmem and may return
ERR_OUTOFVIDEOMEMORY. Vertexbuffers and indexbuffers creation always
succeedes, even when out of video memory.

Fixes "Vampire: The Masquerade - Bloodlines" allocating resources until crash.
Fixes "Age of Conan" allocating resources until crash.
Fixes failing WINE test device.c test_vidmem_accounting().

Signed-off-by: Patrick Rudolph <siro@das-labor.org>
Reviewed-by: Axel Davy <axel.davy@ens.fr>
This commit is contained in:
Patrick Rudolph 2015-09-30 16:42:10 +02:00 committed by Axel Davy
parent a961ec335d
commit 1a893ac886
4 changed files with 50 additions and 5 deletions

View File

@ -175,6 +175,19 @@ NineDevice9_ctor( struct NineDevice9 *This,
/* Create first, it messes up our state. */
This->hud = hud_create(This->pipe, This->cso); /* NULL result is fine */
/* Available memory counter. Updated only for allocations with this device
* instance. This is the Win 7 behavior.
* Win XP shares this counter across multiple devices. */
This->available_texture_mem = This->screen->get_param(This->screen, PIPE_CAP_VIDEO_MEMORY);
if (This->available_texture_mem < 4096)
This->available_texture_mem <<= 20;
else
This->available_texture_mem = UINT_MAX;
/* We cap texture memory usage to 80% of what is reported free initially
* This helps get closer Win behaviour. For example VertexBuffer allocation
* still succeeds when texture allocation fails. */
This->available_texture_limit = This->available_texture_mem * 20LL / 100LL;
/* create implicit swapchains */
This->nswapchains = ID3DPresentGroup_GetMultiheadCount(This->present);
This->swapchains = CALLOC(This->nswapchains,
@ -540,11 +553,7 @@ NineDevice9_TestCooperativeLevel( struct NineDevice9 *This )
UINT WINAPI
NineDevice9_GetAvailableTextureMem( struct NineDevice9 *This )
{
const unsigned mem = This->screen->get_param(This->screen, PIPE_CAP_VIDEO_MEMORY);
if (mem < 4096)
return mem << 20;
else
return UINT_MAX;
return This->available_texture_mem;
}
HRESULT WINAPI

View File

@ -139,6 +139,8 @@ struct NineDevice9
struct pipe_resource *dummy_vbo;
BOOL device_needs_reset;
int minor_version_num;
long long available_texture_mem;
long long available_texture_limit;
};
static inline struct NineDevice9 *
NineDevice9( void *data )

View File

@ -29,6 +29,7 @@
#include "util/u_hash_table.h"
#include "util/u_inlines.h"
#include "util/u_resource.h"
#include "nine_pdata.h"
@ -61,6 +62,33 @@ NineResource9_ctor( struct NineResource9 *This,
if (Allocate) {
assert(!initResource);
/* On Windows it is possible allocation fails when
* IDirect3DDevice9::GetAvailableTextureMem() still reports
* enough free space.
*
* Some games allocate surfaces
* in a loop until they receive D3DERR_OUTOFVIDEOMEMORY to measure
* the available texture memory size.
*
* We are not using the drivers VRAM statistics because:
* * This would add overhead to each resource allocation.
* * Freeing memory is lazy and takes some time, but applications
* expects the memory counter to change immediately after allocating
* or freeing memory.
*
* Vertexbuffers and indexbuffers are not accounted !
*/
if (This->info.target != PIPE_BUFFER) {
This->size = util_resource_size(&This->info);
This->base.device->available_texture_mem -= This->size;
if (This->base.device->available_texture_mem <=
This->base.device->available_texture_limit) {
return D3DERR_OUTOFVIDEOMEMORY;
}
}
DBG("(%p) Creating pipe_resource.\n", This);
This->resource = screen->resource_create(screen, &This->info);
if (!This->resource)
@ -91,6 +119,10 @@ NineResource9_dtor( struct NineResource9 *This )
* still hold a reference. */
pipe_resource_reference(&This->resource, NULL);
/* NOTE: size is 0, unless something has actually been allocated */
if (This->base.device)
This->base.device->available_texture_mem += This->size;
NineUnknown_dtor(&This->base);
}

View File

@ -45,6 +45,8 @@ struct NineResource9
/* for [GS]etPrivateData/FreePrivateData */
struct util_hash_table *pdata;
long long size;
};
static inline struct NineResource9 *
NineResource9( void *data )