mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
Reduces memory peak during window resize
This commit is contained in:
@@ -875,11 +875,13 @@ void CVideoMode::UpdateRenderer(int w, int h)
|
||||
|
||||
SViewPort vp = { 0, 0, w, h };
|
||||
|
||||
// We need to separate destroy and create of objects to be able to flush
|
||||
// all queued to destroy resources during WaitUntilIdle.
|
||||
if (CRenderer::IsInitialised())
|
||||
g_Renderer.DestroyViewSizeDependentObjects();
|
||||
|
||||
if (g_VideoMode.m_BackendDevice)
|
||||
{
|
||||
// TODO: implement freeing window size dependent resources before
|
||||
// waiting to reduce a memory spike.
|
||||
|
||||
// We need to wait until we can destroy the swapchain because it
|
||||
// might be in use.
|
||||
g_VideoMode.m_BackendDevice->WaitUntilIdle();
|
||||
@@ -888,7 +890,7 @@ void CVideoMode::UpdateRenderer(int w, int h)
|
||||
}
|
||||
|
||||
if (CRenderer::IsInitialised())
|
||||
g_Renderer.Resize(w, h);
|
||||
g_Renderer.CreateViewSizeDependentObjects(w, h);
|
||||
|
||||
if (g_GUI)
|
||||
g_GUI->UpdateResolution();
|
||||
|
||||
@@ -103,11 +103,6 @@ CPostprocManager::CPostprocManager(Renderer::Backend::IDevice* device)
|
||||
{
|
||||
}
|
||||
|
||||
CPostprocManager::~CPostprocManager()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
bool CPostprocManager::IsEnabled() const
|
||||
{
|
||||
const bool isDepthStencilFormatPresent =
|
||||
@@ -143,7 +138,7 @@ void CPostprocManager::Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
void CPostprocManager::Initialize()
|
||||
void CPostprocManager::Initialize(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
if (m_IsInitialized)
|
||||
return;
|
||||
@@ -168,8 +163,9 @@ void CPostprocManager::Initialize()
|
||||
std::back_inserter(m_AllowedSampleCounts),
|
||||
[maxSamples](const uint32_t sampleCount) { return sampleCount <= maxSamples; } );
|
||||
|
||||
// The screen size starts out correct and then must be updated with Resize()
|
||||
RecalculateSize(g_Renderer.GetWidth(), g_Renderer.GetHeight());
|
||||
// The screen size starts out correct and then must be updated with
|
||||
// Create/DestroyViewSizeDependentObjects()
|
||||
RecalculateSize(width, height);
|
||||
|
||||
RecreateBuffers();
|
||||
m_IsInitialized = true;
|
||||
@@ -229,15 +225,27 @@ void CPostprocManager::InitializePBR()
|
||||
}
|
||||
}
|
||||
|
||||
void CPostprocManager::Resize()
|
||||
void CPostprocManager::Recreate()
|
||||
{
|
||||
RecalculateSize(g_Renderer.GetWidth(), g_Renderer.GetHeight());
|
||||
DestroyViewSizeDependentObjects();
|
||||
CreateViewSizeDependentObjects(m_UnscaledWidth, m_UnscaledHeight);
|
||||
}
|
||||
|
||||
void CPostprocManager::CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
RecalculateSize(width, height);
|
||||
|
||||
// If the buffers were intialized, recreate them to the new size.
|
||||
if (m_IsInitialized)
|
||||
RecreateBuffers();
|
||||
}
|
||||
|
||||
void CPostprocManager::DestroyViewSizeDependentObjects()
|
||||
{
|
||||
Cleanup();
|
||||
DestroyMultisampleBuffer();
|
||||
}
|
||||
|
||||
void CPostprocManager::RecreateBuffers()
|
||||
{
|
||||
Cleanup();
|
||||
@@ -986,8 +994,6 @@ void CPostprocManager::CreateMultisampleBuffer()
|
||||
|
||||
void CPostprocManager::DestroyMultisampleBuffer()
|
||||
{
|
||||
if (m_UsingMultisampleBuffer)
|
||||
return;
|
||||
m_MultisampleFramebuffer.reset();
|
||||
m_MultisampleColorTex.reset();
|
||||
m_MultisampleDepthTex.reset();
|
||||
|
||||
@@ -37,17 +37,19 @@ class CPostprocManager
|
||||
{
|
||||
public:
|
||||
CPostprocManager(Renderer::Backend::IDevice* device);
|
||||
~CPostprocManager();
|
||||
|
||||
// Returns true if the the manager can be used.
|
||||
bool IsEnabled() const;
|
||||
|
||||
// Create all buffers/textures in GPU memory and set default effect.
|
||||
// @note Must be called before using in the renderer. May be called multiple times.
|
||||
void Initialize();
|
||||
void Initialize(const uint32_t width, const uint32_t height);
|
||||
|
||||
// Update the size of the screen
|
||||
void Resize();
|
||||
// Recreate needed resources. Useful to call on settings changes.
|
||||
void Recreate();
|
||||
|
||||
void CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height);
|
||||
void DestroyViewSizeDependentObjects();
|
||||
|
||||
// Returns a list of xml files found in shaders/effects/postproc.
|
||||
static std::vector<CStrW> GetPostEffects();
|
||||
|
||||
@@ -476,7 +476,7 @@ void CRenderer::ReloadShaders()
|
||||
m->ShadersDirty = false;
|
||||
}
|
||||
|
||||
bool CRenderer::Open(int width, int height)
|
||||
bool CRenderer::Open(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
m->IsOpen = true;
|
||||
|
||||
@@ -487,21 +487,27 @@ bool CRenderer::Open(int width, int height)
|
||||
m->debugRenderer.Initialize();
|
||||
|
||||
if (m->postprocManager.IsEnabled())
|
||||
m->postprocManager.Initialize();
|
||||
m->postprocManager.Initialize(width, height);
|
||||
|
||||
m->sceneRenderer.Initialize();
|
||||
m->sceneRenderer.Initialize(width, height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CRenderer::Resize(int width, int height)
|
||||
void CRenderer::CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
m_Width = width;
|
||||
m_Height = height;
|
||||
|
||||
m->postprocManager.Resize();
|
||||
m->postprocManager.CreateViewSizeDependentObjects(width, height);
|
||||
|
||||
m->sceneRenderer.Resize(width, height);
|
||||
m->sceneRenderer.CreateViewSizeDependentObjects(width, height);
|
||||
}
|
||||
|
||||
void CRenderer::DestroyViewSizeDependentObjects()
|
||||
{
|
||||
m->sceneRenderer.DestroyViewSizeDependentObjects();
|
||||
m->postprocManager.DestroyViewSizeDependentObjects();
|
||||
}
|
||||
|
||||
bool CRenderer::ShouldRender() const
|
||||
@@ -588,7 +594,7 @@ void CRenderer::RenderFrameImpl(
|
||||
CPostprocManager& postprocManager{GetPostprocManager()};
|
||||
if (postprocManager.IsEnabled())
|
||||
{
|
||||
postprocManager.Initialize();
|
||||
postprocManager.Initialize(m_Width, m_Height);
|
||||
RenderGameWithPostProcessingAndGUI(
|
||||
swapChain, gameView, postprocManager, renderGUI, renderLogger);
|
||||
}
|
||||
@@ -897,7 +903,10 @@ void CRenderer::RenderBigScreenShot(const bool needsPresent)
|
||||
|
||||
// Resize various things so that the sizes and aspect ratios are correct
|
||||
{
|
||||
g_Renderer.Resize(tileWidth, tileHeight);
|
||||
DestroyViewSizeDependentObjects();
|
||||
m->device->WaitUntilIdle();
|
||||
CreateViewSizeDependentObjects(tileWidth, tileHeight);
|
||||
|
||||
SViewPort vp = { 0, 0, tileWidth, tileHeight };
|
||||
g_Game->GetView()->SetViewport(vp);
|
||||
}
|
||||
@@ -948,8 +957,12 @@ void CRenderer::RenderBigScreenShot(const bool needsPresent)
|
||||
|
||||
// Restore the viewport settings
|
||||
{
|
||||
g_Renderer.Resize(g_VideoMode.GetWindowWidth(), g_VideoMode.GetWindowHeight());
|
||||
DestroyViewSizeDependentObjects();
|
||||
m->device->WaitUntilIdle();
|
||||
|
||||
SViewPort vp = { 0, 0, g_VideoMode.GetWindowWidth(), g_VideoMode.GetWindowHeight() };
|
||||
CreateViewSizeDependentObjects(vp.m_Width, vp.m_Height);
|
||||
|
||||
g_Game->GetView()->SetViewport(vp);
|
||||
g_Game->GetView()->SetCamera(oldCamera);
|
||||
}
|
||||
|
||||
@@ -82,10 +82,10 @@ public:
|
||||
~CRenderer();
|
||||
|
||||
// open up the renderer: performs any necessary initialisation
|
||||
bool Open(int width, int height);
|
||||
bool Open(const uint32_t width, const uint32_t height);
|
||||
|
||||
// resize renderer view
|
||||
void Resize(int width, int height);
|
||||
void CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height);
|
||||
void DestroyViewSizeDependentObjects();
|
||||
|
||||
// return view width
|
||||
int GetWidth() const { return m_Width; }
|
||||
|
||||
@@ -170,7 +170,7 @@ void CRenderingOptions::ReadConfigAndSetupHooks()
|
||||
m_ConfigHooks->Setup("renderer.scale", []()
|
||||
{
|
||||
if (CRenderer::IsInitialised())
|
||||
g_Renderer.GetPostprocManager().Resize();
|
||||
g_Renderer.GetPostprocManager().Recreate();
|
||||
});
|
||||
|
||||
m_ConfigHooks->Setup("renderer.upscale.technique", []()
|
||||
|
||||
@@ -365,22 +365,30 @@ void CSceneRenderer::ReloadShaders([[maybe_unused]] Renderer::Backend::IDevice*
|
||||
}
|
||||
}
|
||||
|
||||
void CSceneRenderer::Initialize()
|
||||
void CSceneRenderer::Initialize(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
// Let component renderers perform one-time initialization after graphics capabilities and
|
||||
// the shader path have been determined.
|
||||
m->waterManager.Initialize();
|
||||
m->terrainRenderer.Initialize();
|
||||
m->overlayRenderer.Initialize();
|
||||
|
||||
CreateViewSizeDependentObjects(width, height);
|
||||
}
|
||||
|
||||
// resize renderer view
|
||||
void CSceneRenderer::Resize(int /*width*/, int /*height*/)
|
||||
void CSceneRenderer::CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
// need to recreate the shadow map object to resize the shadow texture
|
||||
m->shadow.RecreateTexture();
|
||||
m->shadow.CreateViewSizeDependentObjects(width, height);
|
||||
|
||||
m->waterManager.RecreateOrLoadTexturesIfNeeded();
|
||||
m->waterManager.CreateViewSizeDependentObjects(width, height);
|
||||
}
|
||||
|
||||
void CSceneRenderer::DestroyViewSizeDependentObjects()
|
||||
{
|
||||
m->waterManager.DestroyViewSizeDependentObjects();
|
||||
|
||||
m->shadow.DestroyViewSizeDependentObjects();
|
||||
}
|
||||
|
||||
void CSceneRenderer::BeginFrame()
|
||||
|
||||
@@ -64,8 +64,10 @@ public:
|
||||
CSceneRenderer(Renderer::Backend::IDevice* device);
|
||||
~CSceneRenderer();
|
||||
|
||||
void Initialize();
|
||||
void Resize(int width, int height);
|
||||
void Initialize(const uint32_t width, const uint32_t height);
|
||||
|
||||
void CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height);
|
||||
void DestroyViewSizeDependentObjects();
|
||||
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
|
||||
@@ -131,7 +131,7 @@ struct ShadowMapInternals
|
||||
CCamera SavedViewCamera;
|
||||
|
||||
void CalculateShadowMatrices(const int cascade);
|
||||
void CreateTexture();
|
||||
void CreateTexture(const uint32_t width, const uint32_t height);
|
||||
void UpdateCascadesParameters();
|
||||
};
|
||||
|
||||
@@ -227,25 +227,29 @@ ShadowMap::~ShadowMap()
|
||||
delete m;
|
||||
}
|
||||
|
||||
// Force the texture/buffer/etc to be recreated, particularly when the renderer's
|
||||
// size has changed
|
||||
void ShadowMap::RecreateTexture()
|
||||
void ShadowMap::CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
m->UpdateCascadesParameters();
|
||||
|
||||
m->CreateTexture(width, height);
|
||||
}
|
||||
|
||||
void ShadowMap::DestroyViewSizeDependentObjects()
|
||||
{
|
||||
m->Framebuffer.reset();
|
||||
m->Texture.reset();
|
||||
m->DummyTexture.reset();
|
||||
}
|
||||
|
||||
m->UpdateCascadesParameters();
|
||||
|
||||
// (Texture will be constructed in next SetupFrame)
|
||||
void ShadowMap::RecreateTexture()
|
||||
{
|
||||
DestroyViewSizeDependentObjects();
|
||||
CreateViewSizeDependentObjects(g_Renderer.GetWidth(), g_Renderer.GetHeight());
|
||||
}
|
||||
|
||||
// SetupFrame: camera and light direction for this frame
|
||||
void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
|
||||
{
|
||||
if (!m->Texture)
|
||||
m->CreateTexture();
|
||||
|
||||
CVector3D x(0, 1, 0), eyepos;
|
||||
|
||||
CVector3D z = lightdir;
|
||||
@@ -486,7 +490,7 @@ void ShadowMapInternals::CalculateShadowMatrices(const int cascade)
|
||||
}
|
||||
|
||||
// Create the shadow map
|
||||
void ShadowMapInternals::CreateTexture()
|
||||
void ShadowMapInternals::CreateTexture(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
// Cleanup
|
||||
Framebuffer.reset();
|
||||
@@ -509,7 +513,7 @@ void ShadowMapInternals::CreateTexture()
|
||||
break;
|
||||
// Ultra
|
||||
case 2:
|
||||
shadowMapSize = std::max(round_up_to_pow2(std::max(g_Renderer.GetWidth(), g_Renderer.GetHeight())), 4096);
|
||||
shadowMapSize = std::max(round_up_to_pow2(std::max(width, height)), 4096u);
|
||||
break;
|
||||
// Medium as is
|
||||
default:
|
||||
|
||||
@@ -42,11 +42,14 @@ public:
|
||||
|
||||
/**
|
||||
* RecreateTexture: Destroy the current shadow texture and force creation of
|
||||
* a new one. Useful when the renderer's size has changed and the texture
|
||||
* should be resized too.
|
||||
* a new one. Useful when shadows settings have changed and the texture
|
||||
* should be resized/updated too.
|
||||
*/
|
||||
void RecreateTexture();
|
||||
|
||||
void CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height);
|
||||
void DestroyViewSizeDependentObjects();
|
||||
|
||||
/**
|
||||
* SetupFrame: Configure light space for the given camera and light direction,
|
||||
* create the shadow texture if necessary, etc.
|
||||
|
||||
@@ -236,6 +236,31 @@ int WaterManager::LoadWaterTextures()
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WaterManager::CreateViewSizeDependentObjects(const uint32_t, const uint32_t)
|
||||
{
|
||||
RecreateOrLoadTexturesIfNeeded();
|
||||
}
|
||||
|
||||
void WaterManager::DestroyViewSizeDependentObjects()
|
||||
{
|
||||
m_ReflectionFramebuffer.reset();
|
||||
m_ReflectionTexture.reset();
|
||||
m_ReflFboDepthTexture.reset();
|
||||
|
||||
m_ReflectionFramebufferInitialized = false;
|
||||
|
||||
m_RefractionFramebuffer.reset();
|
||||
m_RefractionTexture.reset();
|
||||
m_RefrFboDepthTexture.reset();
|
||||
|
||||
m_RefractionFramebufferInitialized = false;
|
||||
|
||||
m_FancyEffectsFramebuffer.reset();
|
||||
m_FancyEffectsOccludersFramebuffer.reset();
|
||||
m_FancyTexture.reset();
|
||||
m_FancyTextureDepth.reset();
|
||||
}
|
||||
|
||||
void WaterManager::RecreateOrLoadTexturesIfNeeded()
|
||||
{
|
||||
// Use screen-sized textures for minimum artifacts.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -154,6 +154,9 @@ public:
|
||||
*/
|
||||
void RecreateOrLoadTexturesIfNeeded();
|
||||
|
||||
void CreateViewSizeDependentObjects(const uint32_t width, const uint32_t height);
|
||||
void DestroyViewSizeDependentObjects();
|
||||
|
||||
/**
|
||||
* ReloadWaterNormalTextures: Reload the normal textures so that changing
|
||||
* water type in Atlas will actually do the right thing.
|
||||
|
||||
@@ -836,6 +836,14 @@ void CDevice::OnPresent()
|
||||
++m_FrameID;
|
||||
}
|
||||
|
||||
void CDevice::OnSwapChainRecreation()
|
||||
{
|
||||
// We're sure that a client must call WaitUntilIdle to be able to
|
||||
// recreate a swapchain.
|
||||
ProcessObjectToDestroyQueue(true);
|
||||
ProcessDeviceObjectToDestroyQueue(true);
|
||||
}
|
||||
|
||||
bool CDevice::IsTextureFormatSupported(const Format format) const
|
||||
{
|
||||
switch (format)
|
||||
|
||||
@@ -164,6 +164,8 @@ public:
|
||||
|
||||
void OnPresent();
|
||||
|
||||
void OnSwapChainRecreation();
|
||||
|
||||
void SetObjectName(VkObjectType type, const void* handle, const char* name)
|
||||
{
|
||||
SetObjectName(type, reinterpret_cast<uint64_t>(handle), name);
|
||||
|
||||
@@ -59,6 +59,16 @@ std::unique_ptr<CSwapChain> CSwapChain::Create(
|
||||
int surfaceDrawableWidth, int surfaceDrawableHeight,
|
||||
const bool vsync, std::unique_ptr<ISwapChain> oldSwapChain)
|
||||
{
|
||||
// We don't need to wait to destroy the depth texture as it's independent
|
||||
// from VkSwapchainKHR.
|
||||
if (oldSwapChain)
|
||||
{
|
||||
oldSwapChain->As<CSwapChain>()->m_DepthTexture.reset();
|
||||
|
||||
// Workaround to free the depth texture immediately.
|
||||
device->OnSwapChainRecreation();
|
||||
}
|
||||
|
||||
// It seems some drivers might not reuse the same swapchain memory. So
|
||||
// to avoid higher memory peaks destroy the old swapchain before.
|
||||
const bool destroyOldSwapchainBefore{
|
||||
@@ -225,6 +235,8 @@ std::unique_ptr<CSwapChain> CSwapChain::Create(
|
||||
ENSURE_VK_SUCCESS(getSwapchainImagesResult);
|
||||
ENSURE(imageCount > 0);
|
||||
|
||||
// We create a depth texture to mimic GL behavior where SDL creates it for
|
||||
// a backbuffer.
|
||||
swapChain->m_DepthTexture = CTexture::Create(
|
||||
device, "SwapChainDepthTexture", ITexture::Type::TEXTURE_2D,
|
||||
ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
|
||||
|
||||
Reference in New Issue
Block a user