From 256dff7fd456850c023f6d0122b92c54bf4bb372 Mon Sep 17 00:00:00 2001 From: trompetin17 Date: Tue, 8 Jul 2025 10:30:54 -0500 Subject: [PATCH] Font: make atlas uploads queue-aware on Vulkan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Goal ---- Avoid corrupting the dynamic font-atlas on Vulkan by blocking any re-uploads until the command buffer that created / last updated the texture has actually been submitted. What changed ------------ * **Queue-aware textures** * Added `queueSubmitAware` flag to `IDevice::CreateTexture*` APIs. * `Vulkan::CTexture` now stores two booleans: - `m_QueueSubmitAware` – opt-in per texture. - `m_PendingQueueSubmit` – set to *true* the moment an upload is recorded, cleared once the submit scheduler has flushed. * `CRingCommandContext::ScheduleUpload` marks the texture as pending (`SetPendingQueueSubmit(true)`). * **Device-side watcher** * `Vulkan::CDevice` keeps a `m_TextureUploadWatcherQueue`. Each frame it checks textures that were uploaded ≥ `NUMBER_OF_FRAMES_IN_FLIGHT` frames ago and clears their pending flag. * New helpers `ScheduleTextureUploadWatch `, `ProcessTextureUploadWatchQueue()`. * **Font code** * Atlas texture is now created with `queueSubmitAware = true`. * `CFont::UploadTextureAtlasToGPU()` early-outs when `IsPendingQueueSubmit()` returns *true*, instead of tracking a submit-handle or the manual `m_IsLoadingTextureToGPU` flag (removed). Why this is better ------------------ The logic to wait for a flush is localised inside the rendering backend, so `CFont` only needs to ask *“is my texture busy?”*. This removes the fragile submit-handle bookkeeping and works even if the scheduler issues multiple submits per frame in future. Result ------ Atlas uploads are deferred until the previous submit completes, eliminating the intermittent glyph corruption on the Vulkan backend while leaving GL and the dummy backend unchanged. --- source/graphics/Font.cpp | 11 +++---- source/graphics/Font.h | 1 - source/renderer/backend/IDevice.h | 4 +-- source/renderer/backend/ITexture.h | 4 ++- source/renderer/backend/dummy/Device.cpp | 6 ++-- source/renderer/backend/dummy/Device.h | 4 +-- source/renderer/backend/dummy/Texture.h | 5 ++- source/renderer/backend/gl/Device.cpp | 6 ++-- source/renderer/backend/gl/Device.h | 6 ++-- source/renderer/backend/gl/Texture.h | 4 ++- source/renderer/backend/vulkan/Device.cpp | 31 ++++++++++++++++--- source/renderer/backend/vulkan/Device.h | 11 +++++-- .../backend/vulkan/RingCommandContext.cpp | 2 ++ .../renderer/backend/vulkan/SubmitScheduler.h | 1 + source/renderer/backend/vulkan/Texture.cpp | 16 ++++++++-- source/renderer/backend/vulkan/Texture.h | 15 +++++++-- 16 files changed, 93 insertions(+), 34 deletions(-) diff --git a/source/graphics/Font.cpp b/source/graphics/Font.cpp index c5928faea4..4438279224 100644 --- a/source/graphics/Font.cpp +++ b/source/graphics/Font.cpp @@ -26,7 +26,9 @@ #include "ps/Profiler2.h" #include "ps/containers/Span.h" #include "renderer/Renderer.h" +#include "renderer/backend/Backend.h" #include "renderer/backend/IDevice.h" +#include "renderer/backend/vulkan/Device.h" #include "renderer/backend/IDeviceCommandContext.h" #include "renderer/backend/ITexture.h" #include "renderer/backend/Sampler.h" @@ -288,7 +290,7 @@ bool CFont::ConstructTextureAtlas() Renderer::Backend::ITexture::Usage::TRANSFER_DST | Renderer::Backend::ITexture::Usage::SAMPLED, m_TextureFormat, - textureSize, textureSize, defaultSamplerDesc + textureSize, textureSize, defaultSamplerDesc, 1, 1, true )); if (!m_Texture) @@ -297,8 +299,6 @@ bool CFont::ConstructTextureAtlas() return false; } - m_IsLoadingTextureToGPU = true; - // Initialise texture with transparency, for the areas we don't // overwrite with uploading later. m_TexData = std::make_unique(m_AtlasSize); @@ -493,10 +493,7 @@ std::optional CFont::GenerateGlyphBitmap(FT_Glyph& glyph, u16 codepoi void CFont::UploadTextureAtlasToGPU() { - if (std::exchange(m_IsLoadingTextureToGPU, false)) - return; - - if (!m_IsDirty) + if (m_Texture->GetBackendTexture()->IsPendingQueueSubmit() || !m_IsDirty) return; Renderer::Backend::IDeviceCommandContext* deviceCommandContext = g_Renderer.GetDeviceCommandContext(); diff --git a/source/graphics/Font.h b/source/graphics/Font.h index 1570bbef2d..ff1e5a28d7 100644 --- a/source/graphics/Font.h +++ b/source/graphics/Font.h @@ -162,7 +162,6 @@ private: int m_AtlasPadding; bool m_IsDirty{false}; - bool m_IsLoadingTextureToGPU{false}; float m_StrokeWidth{0.0f}; float m_Scale{1.0f}; diff --git a/source/renderer/backend/IDevice.h b/source/renderer/backend/IDevice.h index ecc4fb3ec5..ec2f6a38a5 100644 --- a/source/renderer/backend/IDevice.h +++ b/source/renderer/backend/IDevice.h @@ -122,12 +122,12 @@ public: virtual std::unique_ptr CreateTexture( const char* name, const ITexture::Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) = 0; + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware = false) = 0; virtual std::unique_ptr CreateTexture2D( const char* name, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1) = 0; + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1, const bool queueSubmitAware = false) = 0; /** * @see IFramebuffer diff --git a/source/renderer/backend/ITexture.h b/source/renderer/backend/ITexture.h index b2083d6346..820ceeae9d 100644 --- a/source/renderer/backend/ITexture.h +++ b/source/renderer/backend/ITexture.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -58,6 +58,8 @@ public: virtual uint32_t GetWidth() const = 0; virtual uint32_t GetHeight() const = 0; virtual uint32_t GetMIPLevelCount() const = 0; + + virtual bool IsPendingQueueSubmit() const = 0; }; } // namespace Backend diff --git a/source/renderer/backend/dummy/Device.cpp b/source/renderer/backend/dummy/Device.cpp index 70a7df4c69..af56e9a08a 100644 --- a/source/renderer/backend/dummy/Device.cpp +++ b/source/renderer/backend/dummy/Device.cpp @@ -97,7 +97,7 @@ std::unique_ptr CDevice::CreateTexture( const char* /*name*/, const CTexture::Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, const Sampler::Desc& /*defaultSamplerDesc*/, const uint32_t MIPLevelCount, - const uint32_t /*sampleCount*/) + const uint32_t /*sampleCount*/, const bool /*queueSubmitAware*/) { return CTexture::Create(this, type, usage, format, width, height, MIPLevelCount); } @@ -105,10 +105,10 @@ std::unique_ptr CDevice::CreateTexture( std::unique_ptr CDevice::CreateTexture2D( const char* name, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware) { return CreateTexture(name, ITexture::Type::TEXTURE_2D, usage, - format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount); + format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount, queueSubmitAware); } std::unique_ptr CDevice::CreateFramebuffer( diff --git a/source/renderer/backend/dummy/Device.h b/source/renderer/backend/dummy/Device.h index 29d2607ce0..640189a8ef 100644 --- a/source/renderer/backend/dummy/Device.h +++ b/source/renderer/backend/dummy/Device.h @@ -69,12 +69,12 @@ public: std::unique_ptr CreateTexture( const char* name, const ITexture::Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) override; + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware = false) override; std::unique_ptr CreateTexture2D( const char* name, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1) override; + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1, const bool queueSubmitAware = false) override; std::unique_ptr CreateFramebuffer( const char* name, SColorAttachment* colorAttachment, diff --git a/source/renderer/backend/dummy/Texture.h b/source/renderer/backend/dummy/Texture.h index 91ee199552..6aece1a36b 100644 --- a/source/renderer/backend/dummy/Texture.h +++ b/source/renderer/backend/dummy/Texture.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -51,6 +51,9 @@ public: uint32_t GetHeight() const override { return m_Height; } uint32_t GetMIPLevelCount() const override { return m_MIPLevelCount; } + // Dummy backend does not support queue submission. + bool IsPendingQueueSubmit() const override { return false; } + private: friend class CDevice; diff --git a/source/renderer/backend/gl/Device.cpp b/source/renderer/backend/gl/Device.cpp index a6e4d36021..c698d4c20b 100644 --- a/source/renderer/backend/gl/Device.cpp +++ b/source/renderer/backend/gl/Device.cpp @@ -908,7 +908,7 @@ std::unique_ptr CDevice::CreateVertexInputLayout( std::unique_ptr CDevice::CreateTexture( const char* name, const ITexture::Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool /*queueSubmitAware*/) { return CTexture::Create(this, name, type, usage, format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount); @@ -917,10 +917,10 @@ std::unique_ptr CDevice::CreateTexture( std::unique_ptr CDevice::CreateTexture2D( const char* name, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware) { return CreateTexture(name, CTexture::Type::TEXTURE_2D, usage, - format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount); + format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount, queueSubmitAware); } std::unique_ptr CDevice::CreateFramebuffer( diff --git a/source/renderer/backend/gl/Device.h b/source/renderer/backend/gl/Device.h index e5927e8148..796c65235f 100644 --- a/source/renderer/backend/gl/Device.h +++ b/source/renderer/backend/gl/Device.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -84,12 +84,12 @@ public: std::unique_ptr CreateTexture( const char* name, const ITexture::Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) override; + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware) override; std::unique_ptr CreateTexture2D( const char* name, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1) override; + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1, const bool queueSubmitAware = false) override; std::unique_ptr CreateFramebuffer( const char* name, SColorAttachment* colorAttachment, diff --git a/source/renderer/backend/gl/Texture.h b/source/renderer/backend/gl/Texture.h index c7e0d986aa..b1929691ff 100644 --- a/source/renderer/backend/gl/Texture.h +++ b/source/renderer/backend/gl/Texture.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -58,6 +58,8 @@ public: GLuint GetHandle() const { return m_Handle; } + // GL doesn's support queue submmit, so we don't need to track pending. + bool IsPendingQueueSubmit() const override { return false; } private: friend class CDevice; diff --git a/source/renderer/backend/vulkan/Device.cpp b/source/renderer/backend/vulkan/Device.cpp index 6036f65e99..b65dc08e57 100644 --- a/source/renderer/backend/vulkan/Device.cpp +++ b/source/renderer/backend/vulkan/Device.cpp @@ -677,6 +677,7 @@ CDevice::~CDevice() if (m_QueryPool) vkDestroyQueryPool(GetVkDevice(), m_QueryPool, nullptr); + ProcessTextureUploadWatchQueue(true); ProcessDeviceObjectToDestroyQueue(true); m_RenderPassManager.reset(); @@ -749,21 +750,21 @@ std::unique_ptr CDevice::CreateVertexInputLayout( std::unique_ptr CDevice::CreateTexture( const char* name, const ITexture::Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware) { return CTexture::Create( this, name, type, usage, format, width, height, - defaultSamplerDesc, MIPLevelCount, sampleCount); + defaultSamplerDesc, MIPLevelCount, sampleCount, queueSubmitAware); } std::unique_ptr CDevice::CreateTexture2D( const char* name, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware) { return CreateTexture( name, ITexture::Type::TEXTURE_2D, usage, format, - width, height, defaultSamplerDesc, MIPLevelCount, sampleCount); + width, height, defaultSamplerDesc, MIPLevelCount, sampleCount, queueSubmitAware); } std::unique_ptr CDevice::CreateFramebuffer( @@ -832,6 +833,7 @@ void CDevice::Present() m_SubmitScheduler->Present(*m_SwapChain); + ProcessTextureUploadWatchQueue(); ProcessObjectToDestroyQueue(); ProcessDeviceObjectToDestroyQueue(); @@ -1012,6 +1014,10 @@ void CDevice::ScheduleBufferToDestroy(const DeviceObjectUID uid) { m_BufferToDestroyQueue.push({m_FrameID, uid}); } +void CDevice::ScheduleTextureUploadWatch(CTexture* texture) +{ + m_TextureUploadWatcherQueue.push({ m_FrameID, texture }); +} void CDevice::SetObjectName(VkObjectType type, const uint64_t handle, const char* name) { @@ -1115,6 +1121,18 @@ void CDevice::ProcessDeviceObjectToDestroyQueue(const bool ignoreFrameID) } } +void CDevice::ProcessTextureUploadWatchQueue(const bool ignoreFrameID) +{ + while (!m_TextureUploadWatcherQueue.empty() && + (ignoreFrameID || m_TextureUploadWatcherQueue.front().first + NUMBER_OF_FRAMES_IN_FLIGHT < m_FrameID)) + { + CTexture* texture = m_TextureUploadWatcherQueue.front().second; + if (texture) + texture->SetPendingQueueSubmit(false); + m_TextureUploadWatcherQueue.pop(); + } +} + CTexture* CDevice::GetCurrentBackbufferTexture() { return IsSwapChainValid() ? m_SwapChain->GetCurrentBackbufferTexture() : nullptr; @@ -1147,6 +1165,11 @@ std::unique_ptr CreateDevice(SDL_Window* window) return Vulkan::CDevice::Create(window); } +uint32_t CDevice::GetCurrentSchedulerHandle() const +{ + return m_SubmitScheduler ? m_SubmitScheduler->GetCurrentHandle() : CSubmitScheduler::INVALID_SUBMIT_HANDLE; +} + } // namespace Vulkan } // namespace Backend diff --git a/source/renderer/backend/vulkan/Device.h b/source/renderer/backend/vulkan/Device.h index 88ff72071d..7d4a394ba5 100644 --- a/source/renderer/backend/vulkan/Device.h +++ b/source/renderer/backend/vulkan/Device.h @@ -56,6 +56,7 @@ class CRingCommandContext; class CSamplerManager; class CSubmitScheduler; class CSwapChain; +class CTexture; class CDevice final : public IDevice { @@ -90,12 +91,12 @@ public: std::unique_ptr CreateTexture( const char* name, const ITexture::Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) override; + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware = false) override; std::unique_ptr CreateTexture2D( const char* name, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1) override; + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1, const bool queueSubmitAware = false) override; std::unique_ptr CreateFramebuffer( const char* name, SColorAttachment* colorAttachment, @@ -159,6 +160,8 @@ public: void ScheduleBufferToDestroy(const DeviceObjectUID uid); + void ScheduleTextureUploadWatch(CTexture* texture); + void SetObjectName(VkObjectType type, const void* handle, const char* name) { SetObjectName(type, reinterpret_cast(handle), name); @@ -182,6 +185,8 @@ public: DeviceObjectUID GenerateNextDeviceObjectUID(); + uint32_t GetCurrentSchedulerHandle() const; + private: CDevice(); @@ -189,6 +194,7 @@ private: bool IsSwapChainValid(); void ProcessObjectToDestroyQueue(const bool ignoreFrameID = false); void ProcessDeviceObjectToDestroyQueue(const bool ignoreFrameID = false); + void ProcessTextureUploadWatchQueue(const bool ignoreFrameID = false); bool IsFormatSupportedForUsage(const Format format, const uint32_t usage) const; @@ -239,6 +245,7 @@ private: }; std::queue m_ObjectToDestroyQueue; std::queue> m_TextureToDestroyQueue; + std::queue> m_TextureUploadWatcherQueue; std::queue> m_BufferToDestroyQueue; std::unique_ptr m_RenderPassManager; diff --git a/source/renderer/backend/vulkan/RingCommandContext.cpp b/source/renderer/backend/vulkan/RingCommandContext.cpp index f55adff9b6..7efb15e78c 100644 --- a/source/renderer/backend/vulkan/RingCommandContext.cpp +++ b/source/renderer/backend/vulkan/RingCommandContext.cpp @@ -164,6 +164,7 @@ void CRingCommandContext::ScheduleUpload( const uint32_t level, const uint32_t layer) { ENSURE(texture->GetType() != ITexture::Type::TEXTURE_2D_MULTISAMPLE); + const Format format = texture->GetFormat(); if (texture->GetType() != ITexture::Type::TEXTURE_CUBE) ENSURE(layer == 0); @@ -223,6 +224,7 @@ void CRingCommandContext::ScheduleUpload( VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask); texture->SetInitialized(); + texture->SetPendingQueueSubmit(true); } void CRingCommandContext::ScheduleUpload( diff --git a/source/renderer/backend/vulkan/SubmitScheduler.h b/source/renderer/backend/vulkan/SubmitScheduler.h index 8746058a76..8f81f58318 100644 --- a/source/renderer/backend/vulkan/SubmitScheduler.h +++ b/source/renderer/backend/vulkan/SubmitScheduler.h @@ -65,6 +65,7 @@ public: void Flush(); + SubmitHandle GetCurrentHandle() const { return m_CurrentHandle; }; private: CSubmitScheduler(CDevice* device, VkQueue queue); diff --git a/source/renderer/backend/vulkan/Texture.cpp b/source/renderer/backend/vulkan/Texture.cpp index f75429574f..12c4819a8b 100644 --- a/source/renderer/backend/vulkan/Texture.cpp +++ b/source/renderer/backend/vulkan/Texture.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -24,6 +24,8 @@ #include "renderer/backend/vulkan/SamplerManager.h" #include "renderer/backend/vulkan/Utilities.h" +#include + namespace Renderer { @@ -38,7 +40,7 @@ std::unique_ptr CTexture::Create( CDevice* device, const char* name, const Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, const Sampler::Desc& defaultSamplerDesc, - const uint32_t MIPLevelCount, const uint32_t sampleCount) + const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware) { std::unique_ptr texture(new CTexture(device)); @@ -50,6 +52,7 @@ std::unique_ptr CTexture::Create( texture->m_MIPLevelCount = MIPLevelCount; texture->m_SampleCount = sampleCount; texture->m_LayerCount = type == ITexture::Type::TEXTURE_CUBE ? 6 : 1; + texture->m_QueueSubmitAware = queueSubmitAware; if (type == Type::TEXTURE_2D_MULTISAMPLE) ENSURE(sampleCount > 1); @@ -376,6 +379,15 @@ CTexture::~CTexture() m_Device->ScheduleTextureToDestroy(m_UID); } +void CTexture::SetPendingQueueSubmit(bool pending) +{ + if (!m_QueueSubmitAware) + return; + + if (!std::exchange(m_PendingQueueSubmit, pending) && pending) + m_Device->ScheduleTextureUploadWatch(this); +} + IDevice* CTexture::GetDevice() { return m_Device; diff --git a/source/renderer/backend/vulkan/Texture.h b/source/renderer/backend/vulkan/Texture.h index bea5123941..155b4465ff 100644 --- a/source/renderer/backend/vulkan/Texture.h +++ b/source/renderer/backend/vulkan/Texture.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -54,6 +54,9 @@ public: uint32_t GetSampleCount() const { return m_SampleCount; } uint32_t GetLayerCount() const { return m_LayerCount; } + bool IsPendingQueueSubmit() const override { return m_PendingQueueSubmit; } + void SetPendingQueueSubmit(bool pending); + VkImage GetImage() { return m_Image; } VkImageView GetAttachmentImageView() { return m_AttachmentImageView; } VkImageView GetSamplerImageView() { return m_SamplerImageView; } @@ -86,7 +89,7 @@ private: CDevice* device, const char* name, const Type type, const uint32_t usage, const Format format, const uint32_t width, const uint32_t height, const Sampler::Desc& defaultSamplerDesc, - const uint32_t MIPLevelCount, const uint32_t sampleCount); + const uint32_t MIPLevelCount, const uint32_t sampleCount, const bool queueSubmitAware = false); static std::unique_ptr WrapBackbufferImage( CDevice* device, const char* name, const VkImage image, const VkFormat format, @@ -128,6 +131,14 @@ private: // It's safe to store the current state while we use a single device command // context. bool m_Initialized = false; + + // We store a flag to indicate that the texture is in Queue submit. + bool m_PendingQueueSubmit = false; + + // If true, the texture is aware of queue submission useful for + // for dynamic textures that are updated frequently and needs to wait for + // the queue submission to finish before overwriting the texture data. + bool m_QueueSubmitAware = false; }; } // namespace Vulkan