From 4377c46826f358be17f7825890e264c979dcf18f Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 26 Feb 2022 21:38:36 +1100 Subject: [PATCH] Direct3D11: Support running out of memory when creating textures (partially) Failures in mipmap creation, shader resource view creation etc will still cause program exit --- src/Game.c | 22 +++++++++++----------- src/Game.h | 3 +++ src/Graphics_D3D11.c | 12 ++++++++++-- src/Graphics_D3D9.c | 2 +- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Game.c b/src/Game.c index d59496612..8a73d701c 100644 --- a/src/Game.c +++ b/src/Game.c @@ -141,6 +141,17 @@ void Game_CycleViewDistance(void) { } } +cc_bool Game_ReduceVRAM(void) { + if (Game_UserViewDistance <= 16) return false; + Game_UserViewDistance /= 2; + Game_UserViewDistance = max(16, Game_UserViewDistance); + + MapRenderer_Refresh(); + Game_SetViewDistance(Game_UserViewDistance); + Chat_AddRaw("&cOut of VRAM! Halving view distance.."); + return true; +} + void Game_SetViewDistance(int distance) { distance = min(distance, Game_MaxViewDistance); @@ -263,16 +274,6 @@ static void HandleOnNewMapLoaded(void* obj) { } } -static void HandleLowVRAMDetected(void* obj) { - if (Game_UserViewDistance <= 16) Logger_Abort("Out of video memory!"); - Game_UserViewDistance /= 2; - Game_UserViewDistance = max(16, Game_UserViewDistance); - - MapRenderer_Refresh(); - Game_SetViewDistance(Game_UserViewDistance); - Chat_AddRaw("&cOut of VRAM! Halving view distance.."); -} - static void HandleInactiveChanged(void* obj) { if (WindowInfo.Inactive) { Chat_AddRaw(LOWPERF_ENTER_MESSAGE); @@ -373,7 +374,6 @@ static void Game_Load(void) { Event_Register_(&WorldEvents.NewMap, NULL, HandleOnNewMap); Event_Register_(&WorldEvents.MapLoaded, NULL, HandleOnNewMapLoaded); - Event_Register_(&GfxEvents.LowVRAMDetected, NULL, HandleLowVRAMDetected); Event_Register_(&WindowEvents.Resized, NULL, Game_OnResize); Event_Register_(&WindowEvents.Closing, NULL, Game_Free); Event_Register_(&WindowEvents.InactiveChanged, NULL, HandleInactiveChanged); diff --git a/src/Game.h b/src/Game.h index 43d77af09..93966aa0f 100644 --- a/src/Game.h +++ b/src/Game.h @@ -55,6 +55,9 @@ extern const char* const FpsLimit_Names[FPS_LIMIT_COUNT]; void Game_ToggleFullscreen(void); void Game_CycleViewDistance(void); +/* Attempts to reduce VRAM usage (e.g. reducing view distance) */ +/* Returns false if VRAM cannot be reduced any further */ +cc_bool Game_ReduceVRAM(void); void Game_SetViewDistance(int distance); void Game_UserSetViewDistance(int distance); diff --git a/src/Graphics_D3D11.c b/src/Graphics_D3D11.c index 8aa25a6a3..b68e159f5 100644 --- a/src/Graphics_D3D11.c +++ b/src/Graphics_D3D11.c @@ -239,8 +239,16 @@ GfxResourceID Gfx_CreateTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipm src = NULL; } - hr = ID3D11Device_CreateTexture2D(device, &desc, src, &tex); - if (hr) Logger_Abort2(hr, "Failed to create texture"); + while ((hr = ID3D11Device_CreateTexture2D(device, &desc, src, &tex))) + { + if (hr == E_OUTOFMEMORY) { + // insufficient VRAM or RAM left + if (!Game_ReduceVRAM()) return NULL; + } else { + // unknown issue, do not even try to handle it + Logger_Abort2(hr, "Failed to create texture"); + } + } hr = ID3D11Device_CreateShaderResourceView(device, tex, NULL, &view); if (hr) Logger_Abort2(hr, "Failed to create view"); diff --git a/src/Graphics_D3D9.c b/src/Graphics_D3D9.c index 5bcc92222..3efd4c5ef 100644 --- a/src/Graphics_D3D9.c +++ b/src/Graphics_D3D9.c @@ -238,7 +238,7 @@ static cc_bool D3D9_CheckResult(cc_result res, const char* func) { if (!res) return true; if (res == D3DERR_OUTOFVIDEOMEMORY || res == E_OUTOFMEMORY) { - Event_RaiseVoid(&GfxEvents.LowVRAMDetected); + if (!Game_ReduceVRAM()) Logger_Abort("Out of video memory!"); } else { Logger_Abort2(res, func); }