diff --git a/CMakeLists.txt b/CMakeLists.txt index 21e207ef..140dfef1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,7 +227,8 @@ set(SOURCE_FILES MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderTargetManager.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp + MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp + MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp MobileGL/MG_State/GLState/Core.cpp MobileGL/MG_State/GLState/ErrorState/Error.cpp diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp index 9d8761cc..0b71972c 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp @@ -138,7 +138,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool UniformDescriptorBinder::Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount, Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes, - VkTextureSamplerManager* textureSamplerManager, + VkTextureManager* textureManager, VkSamplerManager* samplerManager, VkRenderTargetManager* framebufferManager) { Shutdown(); @@ -147,8 +147,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { MOBILEGL_ASSERT(frameCount > 0, "UniformDescriptorBinder::Initialize requires frameCount > 0"); MOBILEGL_ASSERT(maxBindings > 0, "UniformDescriptorBinder::Initialize requires maxBindings > 0"); MOBILEGL_ASSERT(setsPerFrame > 0, "UniformDescriptorBinder::Initialize requires setsPerFrame > 0"); - MOBILEGL_ASSERT(textureSamplerManager != nullptr, - "UniformDescriptorBinder::Initialize requires valid texture sampler manager"); + MOBILEGL_ASSERT(textureManager != nullptr, + "UniformDescriptorBinder::Initialize requires valid texture manager"); + MOBILEGL_ASSERT(samplerManager != nullptr, + "UniformDescriptorBinder::Initialize requires valid sampler manager"); MOBILEGL_ASSERT(framebufferManager != nullptr, "UniformDescriptorBinder::Initialize requires valid framebuffer manager"); @@ -160,7 +162,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_maxBindings = maxBindings; m_setsPerFrame = setsPerFrame; m_peakDescriptorSetsObserved = 0; - m_textureSamplerManager = textureSamplerManager; + m_textureManager = textureManager; + m_samplerManager = samplerManager; m_framebufferManager = framebufferManager; m_frames.resize(m_frameCount); @@ -223,7 +226,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_maxBindings = 0; m_setsPerFrame = 0; m_peakDescriptorSetsObserved = 0; - m_textureSamplerManager = nullptr; + m_textureManager = nullptr; + m_samplerManager = nullptr; m_framebufferManager = nullptr; } @@ -447,7 +451,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { const MG_State::GLState::ProgramObject& program, const ProgramLayout& layout, Uint32 binding, VkDescriptorImageInfo& outImageInfo) const { - MOBILEGL_ASSERT(m_textureSamplerManager != nullptr, "ResolveSamplerDescriptor: texture sampler manager is null"); + MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptor: texture manager is null"); + MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptor: sampler manager is null"); if (!MG_State::pGLContext || binding >= layout.samplerUniformLocationByBinding.size()) { return false; } @@ -480,6 +485,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } + const MG_State::GLState::SamplerObject* samplerToUse = samplerOverride ? samplerOverride.get() : nullptr; + if (!samplerToUse) { + auto textureSampler = texture->GetSamplerObject(); + if (textureSampler) { + samplerToUse = textureSampler.get(); + } + } + if (!samplerToUse) { + return false; + } + if (m_framebufferManager->Transition(commandBuffer, VkRenderTargetManager::TransitionResource::OffscreenColorTexture, VkRenderTargetManager::TransitionUsage::ShaderRead, @@ -488,7 +504,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (m_framebufferManager->GetOffscreenColorViewByTexture(texture->GetExternalIndex(), offscreenView) && offscreenView != VK_NULL_HANDLE) { VkDescriptorImageInfo sampledInfo{}; - if (!m_textureSamplerManager->SyncTextureAndGetDescriptor(*texture, samplerOverride.get(), sampledInfo)) { + if (!m_textureManager->SyncTextureAndGetDescriptor(*texture, sampledInfo)) { + return false; + } + sampledInfo.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse); + if (sampledInfo.sampler == VK_NULL_HANDLE) { return false; } sampledInfo.imageView = offscreenView; @@ -498,7 +518,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } - return m_textureSamplerManager->SyncTextureAndGetDescriptor(*texture, samplerOverride.get(), outImageInfo); + if (!m_textureManager->SyncTextureAndGetDescriptor(*texture, outImageInfo)) { + return false; + } + outImageInfo.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse); + return outImageInfo.sampler != VK_NULL_HANDLE; } UniformDescriptorBinder::ProgramLayout* UniformDescriptorBinder::GetOrCreateProgramLayout( @@ -767,8 +791,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } static const Uint8 kFallbackData[16] = {}; - MOBILEGL_ASSERT(m_textureSamplerManager != nullptr, - "BindProgramUniformBuffers: texture sampler manager is null"); + MOBILEGL_ASSERT(m_textureManager != nullptr, "BindProgramUniformBuffers: texture manager is null"); + MOBILEGL_ASSERT(m_samplerManager != nullptr, "BindProgramUniformBuffers: sampler manager is null"); Vector writes; writes.reserve(m_maxBindings); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h index 1b73d403..3336dca1 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h @@ -9,7 +9,8 @@ #pragma once #include "VkBufferObject.h" -#include "VkTextureSamplerManager.h" +#include "VkSamplerManager.h" +#include "VkTextureManager.h" #include "../VkIncludes.h" #include #include @@ -32,7 +33,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64, VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024, - VkTextureSamplerManager* textureSamplerManager = nullptr, + VkTextureManager* textureManager = nullptr, VkSamplerManager* samplerManager = nullptr, VkRenderTargetManager* framebufferManager = nullptr); void Shutdown(); @@ -97,7 +98,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 m_maxBindings = 0; Uint32 m_setsPerFrame = 0; Uint32 m_peakDescriptorSetsObserved = 0; - VkTextureSamplerManager* m_textureSamplerManager = nullptr; + VkTextureManager* m_textureManager = nullptr; + VkSamplerManager* m_samplerManager = nullptr; VkRenderTargetManager* m_framebufferManager = nullptr; }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp new file mode 100644 index 00000000..da58c3aa --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp @@ -0,0 +1,156 @@ +// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#include "VkSamplerManager.h" + +#include "MG_State/GLState/Core.h" + +namespace MobileGL::MG_Backend::DirectVulkan { + Bool VkSamplerManager::Initialize(const InitInfo& initInfo) { + Shutdown(); + + m_device = initInfo.device; + m_config = initInfo.config; + MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE && m_config != nullptr, + "VkSamplerManager::Initialize failed: invalid initialization info"); + return true; + } + + void VkSamplerManager::Shutdown() { + for (auto& [_, sampler] : m_samplers) { + if (m_device != VK_NULL_HANDLE && sampler.handle != VK_NULL_HANDLE) { + vkDestroySampler(m_device, sampler.handle, nullptr); + } + sampler.handle = VK_NULL_HANDLE; + } + m_samplers.clear(); + + m_device = VK_NULL_HANDLE; + m_config = nullptr; + } + + Uint64 VkSamplerManager::BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler) const { + MOBILEGL_ASSERT(m_config != nullptr, "VkSamplerManager::BuildSamplerKey: m_config is null"); + XXHASH_VERIFY(XXH64_reset(m_hashState, m_config->CacheVersion)); + + const auto minFilter = sampler.GetMinFilter(); + XXHASH_VERIFY(XXH64_update(m_hashState, &minFilter, sizeof(minFilter))); + const auto magFilter = sampler.GetMagFilter(); + XXHASH_VERIFY(XXH64_update(m_hashState, &magFilter, sizeof(magFilter))); + const auto mipmapMode = sampler.GetMipmapMode(); + XXHASH_VERIFY(XXH64_update(m_hashState, &mipmapMode, sizeof(mipmapMode))); + const auto wrapS = sampler.GetWrapS(); + XXHASH_VERIFY(XXH64_update(m_hashState, &wrapS, sizeof(wrapS))); + const auto wrapT = sampler.GetWrapT(); + XXHASH_VERIFY(XXH64_update(m_hashState, &wrapT, sizeof(wrapT))); + const auto wrapR = sampler.GetWrapR(); + XXHASH_VERIFY(XXH64_update(m_hashState, &wrapR, sizeof(wrapR))); + const auto minLod = sampler.GetMinLod(); + XXHASH_VERIFY(XXH64_update(m_hashState, &minLod, sizeof(minLod))); + const auto maxLod = sampler.GetMaxLod(); + XXHASH_VERIFY(XXH64_update(m_hashState, &maxLod, sizeof(maxLod))); + const auto lodBias = sampler.GetLodBias(); + XXHASH_VERIFY(XXH64_update(m_hashState, &lodBias, sizeof(lodBias))); + const auto compareMode = sampler.GetCompareMode(); + XXHASH_VERIFY(XXH64_update(m_hashState, &compareMode, sizeof(compareMode))); + const auto compareFunc = sampler.GetSamplerCompareFunc(); + XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc))); + return XXH64_digest(m_hashState); + } + + VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler) { + const Uint64 key = BuildSamplerKey(sampler); + auto it = m_samplers.find(key); + if (it != m_samplers.end()) { + return it->second.handle; + } + + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = ToVkFilter(sampler.GetMagFilter()); + samplerInfo.minFilter = ToVkFilter(sampler.GetMinFilter()); + samplerInfo.mipmapMode = ToVkMipmapMode(sampler.GetMipmapMode()); + samplerInfo.addressModeU = ToVkAddressMode(sampler.GetWrapS()); + samplerInfo.addressModeV = ToVkAddressMode(sampler.GetWrapT()); + samplerInfo.addressModeW = ToVkAddressMode(sampler.GetWrapR()); + samplerInfo.mipLodBias = sampler.GetLodBias(); + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.maxAnisotropy = 1.0f; + samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE; + samplerInfo.compareOp = ToVkCompareOp(sampler.GetSamplerCompareFunc()); + samplerInfo.minLod = sampler.GetMinLod(); + samplerInfo.maxLod = sampler.GetMaxLod(); + samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + + VkSampler vkSampler = VK_NULL_HANDLE; + VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &vkSampler), "vkCreateSampler(texture)"); + + SamplerCacheEntry entry{}; + entry.handle = vkSampler; + entry.externalIndex = sampler.GetExternalIndex(); + entry.version = sampler.GetVersion(); + m_samplers[key] = entry; + return vkSampler; + } + + VkFilter VkSamplerManager::ToVkFilter(SamplerFilterMode mode) { + return mode == SamplerFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; + } + + VkSamplerMipmapMode VkSamplerManager::ToVkMipmapMode(SamplerMipmapMode mode) { + switch (mode) { + case SamplerMipmapMode::Nearest: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + case SamplerMipmapMode::Linear: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + case SamplerMipmapMode::None: + default: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + } + } + + VkSamplerAddressMode VkSamplerManager::ToVkAddressMode(SamplerWrapMode mode) { + switch (mode) { + case SamplerWrapMode::ClampToEdge: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case SamplerWrapMode::MirroredRepeat: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + case SamplerWrapMode::Repeat: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case SamplerWrapMode::ClampToBorder: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + case SamplerWrapMode::MirrorClampToEdge: + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + default: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + } + } + + VkCompareOp VkSamplerManager::ToVkCompareOp(SamplerCompareFunc func) { + switch (func) { + case SamplerCompareFunc::Never: + return VK_COMPARE_OP_NEVER; + case SamplerCompareFunc::Less: + return VK_COMPARE_OP_LESS; + case SamplerCompareFunc::Equal: + return VK_COMPARE_OP_EQUAL; + case SamplerCompareFunc::LessEqual: + return VK_COMPARE_OP_LESS_OR_EQUAL; + case SamplerCompareFunc::Greater: + return VK_COMPARE_OP_GREATER; + case SamplerCompareFunc::NotEqual: + return VK_COMPARE_OP_NOT_EQUAL; + case SamplerCompareFunc::GreaterEqual: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + case SamplerCompareFunc::Always: + default: + return VK_COMPARE_OP_ALWAYS; + } + } +} // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h new file mode 100644 index 00000000..08a6508c --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h @@ -0,0 +1,51 @@ +// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#pragma once + +#include "../VkIncludes.h" +#include "../VulkanRendererConfig.h" +#include +#include + +namespace MobileGL::MG_State::GLState { +class SamplerObject; +} + +namespace MobileGL::MG_Backend::DirectVulkan { +class VkSamplerManager { +public: + struct InitInfo { + VkDevice device = VK_NULL_HANDLE; + const VulkanRendererConfig* config = nullptr; + }; + + Bool Initialize(const InitInfo& initInfo); + void Shutdown(); + + VkSampler GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler); + +private: + struct SamplerCacheEntry { + VkSampler handle = VK_NULL_HANDLE; + Uint externalIndex = 0; + Uint16 version = 0; + }; + + Uint64 BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler) const; + static VkFilter ToVkFilter(SamplerFilterMode mode); + static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode); + static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode); + static VkCompareOp ToVkCompareOp(SamplerCompareFunc func); + + VkDevice m_device = VK_NULL_HANDLE; + const VulkanRendererConfig* m_config = nullptr; + UnorderedMap m_samplers; + static inline XXH64_state_t* m_hashState = XXH64_createState(); +}; +} // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp similarity index 60% rename from MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp rename to MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index 57042e5f..7e2c845f 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -1,4 +1,4 @@ -// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp +// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp // Copyright (c) 2025-2026 MobileGL-Dev // Licensed under the GNU Lesser General Public License v3.0: // https://www.gnu.org/licenses/gpl-3.0.txt @@ -6,12 +6,12 @@ // SPDX-License-Identifier: LGPL-3.0-only // End of Source File Header -#include "VkTextureSamplerManager.h" +#include "VkTextureManager.h" #include "MG_State/GLState/Core.h" namespace MobileGL::MG_Backend::DirectVulkan { - Bool VkTextureSamplerManager::Initialize(const InitInfo& initInfo) { + Bool VkTextureManager::Initialize(const InitInfo& initInfo) { Shutdown(); m_device = initInfo.device; @@ -19,40 +19,29 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_allocator = initInfo.allocator; m_commandPool = initInfo.commandPool; m_graphicsQueue = initInfo.graphicsQueue; - m_config = initInfo.config; MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE && m_physicalDevice != VK_NULL_HANDLE && m_allocator != nullptr && - m_commandPool != VK_NULL_HANDLE && m_graphicsQueue != VK_NULL_HANDLE && m_config != nullptr, - "VkTextureSamplerManager::Initialize failed: invalid initialization info"); + m_commandPool != VK_NULL_HANDLE && m_graphicsQueue != VK_NULL_HANDLE, + "VkTextureManager::Initialize failed: invalid initialization info"); return true; } - void VkTextureSamplerManager::Shutdown() { + void VkTextureManager::Shutdown() { for (auto& [_, resource] : m_textureResources) { DestroyTextureResource(resource); } m_textureResources.clear(); - for (auto& [_, sampler] : m_samplers) { - if (m_device != VK_NULL_HANDLE && sampler.handle != VK_NULL_HANDLE) { - vkDestroySampler(m_device, sampler.handle, nullptr); - } - sampler.handle = VK_NULL_HANDLE; - } - m_samplers.clear(); - m_device = VK_NULL_HANDLE; m_physicalDevice = VK_NULL_HANDLE; m_allocator = nullptr; m_commandPool = VK_NULL_HANDLE; m_graphicsQueue = VK_NULL_HANDLE; - m_config = nullptr; } - Bool VkTextureSamplerManager::SyncTextureAndGetDescriptor(const MG_State::GLState::ITextureObject& texture, - const MG_State::GLState::SamplerObject* samplerOverride, - VkDescriptorImageInfo& outImageInfo) { + Bool VkTextureManager::SyncTextureAndGetDescriptor(const MG_State::GLState::ITextureObject& texture, + VkDescriptorImageInfo& outImageInfo) { MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "SyncTextureAndGetDescriptor: m_device == VK_NULL_HANDLE"); auto it = m_textureResources.find(texture.GetExternalIndex()); @@ -67,30 +56,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } - const MG_State::GLState::SamplerObject* samplerToUse = samplerOverride; - if (!samplerToUse) { - auto textureSampler = texture.GetSamplerObject(); - if (textureSampler) { - samplerToUse = textureSampler.get(); - } - } - - VkSampler sampler = VK_NULL_HANDLE; - if (samplerToUse) { - sampler = GetOrCreateSampler(*samplerToUse); - } - if (it->second.view == VK_NULL_HANDLE || sampler == VK_NULL_HANDLE) { + if (it->second.view == VK_NULL_HANDLE) { return false; } - outImageInfo.sampler = sampler; + outImageInfo.sampler = VK_NULL_HANDLE; outImageInfo.imageView = it->second.view; outImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; return true; } - Bool VkTextureSamplerManager::EnsureTextureSynced(TextureResource& resource, - const MG_State::GLState::ITextureObject& texture) { + Bool VkTextureManager::EnsureTextureSynced(TextureResource& resource, const MG_State::GLState::ITextureObject& texture) { TextureUploadTarget level0Target = TextureUploadTarget::Unknown; IntVec3 texelSize{0, 0, 0}; SizeT byteSize = 0; @@ -120,10 +96,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - Bool VkTextureSamplerManager::EnsureTextureResource(TextureResource& resource, - const MG_State::GLState::ITextureObject& texture, - TextureUploadTarget level0Target, const IntVec3& texelSize, - SizeT byteSize) { + Bool VkTextureManager::EnsureTextureResource(TextureResource& resource, const MG_State::GLState::ITextureObject& texture, + TextureUploadTarget level0Target, const IntVec3& texelSize, + SizeT byteSize) { const VkFormat format = ResolveTextureFormat(texture.GetFormat()); if (format == VK_FORMAT_UNDEFINED) { return false; @@ -183,9 +158,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - Bool VkTextureSamplerManager::UploadLevel0(TextureResource& resource, - const MG_State::GLState::TextureObjectMipmap& mipmapTexture, - TextureUploadTarget level0Target, SizeT byteSize) { + Bool VkTextureManager::UploadLevel0(TextureResource& resource, const MG_State::GLState::TextureObjectMipmap& mipmapTexture, + TextureUploadTarget level0Target, SizeT byteSize) { auto& mutableTexture = const_cast(mipmapTexture); const void* source = mutableTexture.MapMipmapData(level0Target, 0); if (source == nullptr || byteSize == 0) { @@ -270,7 +244,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - Bool VkTextureSamplerManager::ExecuteImmediate(const std::function& recorder) const { + Bool VkTextureManager::ExecuteImmediate(const std::function& recorder) const { VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.commandPool = m_commandPool; @@ -300,7 +274,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - void VkTextureSamplerManager::DestroyTextureResource(TextureResource& resource) const { + void VkTextureManager::DestroyTextureResource(TextureResource& resource) const { if (m_device != VK_NULL_HANDLE && resource.view != VK_NULL_HANDLE) { vkDestroyImageView(m_device, resource.view, nullptr); } @@ -315,9 +289,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { resource.format = VK_FORMAT_UNDEFINED; } - Bool VkTextureSamplerManager::ResolveLevel0(const MG_State::GLState::ITextureObject& texture, - TextureUploadTarget& outTarget, IntVec3& outTexelSize, - SizeT& outByteSize) { + Bool VkTextureManager::ResolveLevel0(const MG_State::GLState::ITextureObject& texture, TextureUploadTarget& outTarget, + IntVec3& outTexelSize, SizeT& outByteSize) { const auto* mipTexture = dynamic_cast(&texture); if (!mipTexture) { return false; @@ -332,7 +305,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { return outTexelSize.x() > 0 && outTexelSize.y() > 0 && outByteSize > 0; } - VkFormat VkTextureSamplerManager::ResolveTextureFormat(TextureInternalFormat format) { + VkFormat VkTextureManager::ResolveTextureFormat(TextureInternalFormat format) { switch (format) { case TextureInternalFormat::RGBA: case TextureInternalFormat::RGBA8: @@ -343,125 +316,4 @@ namespace MobileGL::MG_Backend::DirectVulkan { return VK_FORMAT_UNDEFINED; } } - - Uint64 VkTextureSamplerManager::BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler) const { - MOBILEGL_ASSERT(m_config != nullptr, "VkTextureSamplerManager::BuildSamplerKey: m_config is null"); - XXHASH_VERIFY(XXH64_reset(m_hashState, m_config->CacheVersion)); - - const auto minFilter = sampler.GetMinFilter(); - XXHASH_VERIFY(XXH64_update(m_hashState, &minFilter, sizeof(minFilter))); - const auto magFilter = sampler.GetMagFilter(); - XXHASH_VERIFY(XXH64_update(m_hashState, &magFilter, sizeof(magFilter))); - const auto mipmapMode = sampler.GetMipmapMode(); - XXHASH_VERIFY(XXH64_update(m_hashState, &mipmapMode, sizeof(mipmapMode))); - const auto wrapS = sampler.GetWrapS(); - XXHASH_VERIFY(XXH64_update(m_hashState, &wrapS, sizeof(wrapS))); - const auto wrapT = sampler.GetWrapT(); - XXHASH_VERIFY(XXH64_update(m_hashState, &wrapT, sizeof(wrapT))); - const auto wrapR = sampler.GetWrapR(); - XXHASH_VERIFY(XXH64_update(m_hashState, &wrapR, sizeof(wrapR))); - const auto minLod = sampler.GetMinLod(); - XXHASH_VERIFY(XXH64_update(m_hashState, &minLod, sizeof(minLod))); - const auto maxLod = sampler.GetMaxLod(); - XXHASH_VERIFY(XXH64_update(m_hashState, &maxLod, sizeof(maxLod))); - const auto lodBias = sampler.GetLodBias(); - XXHASH_VERIFY(XXH64_update(m_hashState, &lodBias, sizeof(lodBias))); - const auto compareMode = sampler.GetCompareMode(); - XXHASH_VERIFY(XXH64_update(m_hashState, &compareMode, sizeof(compareMode))); - const auto compareFunc = sampler.GetSamplerCompareFunc(); - XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc))); - return XXH64_digest(m_hashState); - } - - VkSampler VkTextureSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler) { - const Uint64 key = BuildSamplerKey(sampler); - auto it = m_samplers.find(key); - if (it != m_samplers.end()) { - return it->second.handle; - } - - VkSamplerCreateInfo samplerInfo{}; - samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerInfo.magFilter = ToVkFilter(sampler.GetMagFilter()); - samplerInfo.minFilter = ToVkFilter(sampler.GetMinFilter()); - samplerInfo.mipmapMode = ToVkMipmapMode(sampler.GetMipmapMode()); - samplerInfo.addressModeU = ToVkAddressMode(sampler.GetWrapS()); - samplerInfo.addressModeV = ToVkAddressMode(sampler.GetWrapT()); - samplerInfo.addressModeW = ToVkAddressMode(sampler.GetWrapR()); - samplerInfo.mipLodBias = sampler.GetLodBias(); - samplerInfo.anisotropyEnable = VK_FALSE; - samplerInfo.maxAnisotropy = 1.0f; - samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE; - samplerInfo.compareOp = ToVkCompareOp(sampler.GetSamplerCompareFunc()); - samplerInfo.minLod = sampler.GetMinLod(); - samplerInfo.maxLod = sampler.GetMaxLod(); - samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; - samplerInfo.unnormalizedCoordinates = VK_FALSE; - - VkSampler vkSampler = VK_NULL_HANDLE; - VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &vkSampler), "vkCreateSampler(texture)"); - - SamplerCacheEntry entry{}; - entry.handle = vkSampler; - entry.externalIndex = sampler.GetExternalIndex(); - entry.version = sampler.GetVersion(); - m_samplers[key] = entry; - return vkSampler; - } - - VkFilter VkTextureSamplerManager::ToVkFilter(SamplerFilterMode mode) { - return mode == SamplerFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; - } - - VkSamplerMipmapMode VkTextureSamplerManager::ToVkMipmapMode(SamplerMipmapMode mode) { - switch (mode) { - case SamplerMipmapMode::Nearest: - return VK_SAMPLER_MIPMAP_MODE_NEAREST; - case SamplerMipmapMode::Linear: - return VK_SAMPLER_MIPMAP_MODE_LINEAR; - case SamplerMipmapMode::None: - default: - return VK_SAMPLER_MIPMAP_MODE_NEAREST; - } - } - - VkSamplerAddressMode VkTextureSamplerManager::ToVkAddressMode(SamplerWrapMode mode) { - switch (mode) { - case SamplerWrapMode::ClampToEdge: - return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - case SamplerWrapMode::MirroredRepeat: - return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; - case SamplerWrapMode::Repeat: - return VK_SAMPLER_ADDRESS_MODE_REPEAT; - case SamplerWrapMode::ClampToBorder: - return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; - case SamplerWrapMode::MirrorClampToEdge: - return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; - default: - return VK_SAMPLER_ADDRESS_MODE_REPEAT; - } - } - - VkCompareOp VkTextureSamplerManager::ToVkCompareOp(SamplerCompareFunc func) { - switch (func) { - case SamplerCompareFunc::Never: - return VK_COMPARE_OP_NEVER; - case SamplerCompareFunc::Less: - return VK_COMPARE_OP_LESS; - case SamplerCompareFunc::Equal: - return VK_COMPARE_OP_EQUAL; - case SamplerCompareFunc::LessEqual: - return VK_COMPARE_OP_LESS_OR_EQUAL; - case SamplerCompareFunc::Greater: - return VK_COMPARE_OP_GREATER; - case SamplerCompareFunc::NotEqual: - return VK_COMPARE_OP_NOT_EQUAL; - case SamplerCompareFunc::GreaterEqual: - return VK_COMPARE_OP_GREATER_OR_EQUAL; - case SamplerCompareFunc::Always: - default: - return VK_COMPARE_OP_ALWAYS; - } - } - } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h similarity index 69% rename from MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h rename to MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h index a255f43b..0fb59738 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -1,4 +1,4 @@ -// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h +// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h // Copyright (c) 2025-2026 MobileGL-Dev // Licensed under the GNU Lesser General Public License v3.0: // https://www.gnu.org/licenses/gpl-3.0.txt @@ -9,19 +9,16 @@ #pragma once #include "../VkIncludes.h" -#include "../VulkanRendererConfig.h" #include -#include #include #include namespace MobileGL::MG_State::GLState { class ITextureObject; -class SamplerObject; } namespace MobileGL::MG_Backend::DirectVulkan { -class VkTextureSamplerManager { +class VkTextureManager { public: struct InitInfo { VkDevice device = VK_NULL_HANDLE; @@ -29,15 +26,12 @@ public: VmaAllocator allocator = nullptr; VkCommandPool commandPool = VK_NULL_HANDLE; VkQueue graphicsQueue = VK_NULL_HANDLE; - const VulkanRendererConfig* config = nullptr; }; Bool Initialize(const InitInfo& initInfo); void Shutdown(); - Bool SyncTextureAndGetDescriptor(const MG_State::GLState::ITextureObject& texture, - const MG_State::GLState::SamplerObject* samplerOverride, - VkDescriptorImageInfo& outImageInfo); + Bool SyncTextureAndGetDescriptor(const MG_State::GLState::ITextureObject& texture, VkDescriptorImageInfo& outImageInfo); private: struct TextureResource { @@ -50,12 +44,6 @@ private: Uint textureExternalIndex = 0; }; - struct SamplerCacheEntry { - VkSampler handle = VK_NULL_HANDLE; - Uint externalIndex = 0; - Uint16 version = 0; - }; - Bool EnsureTextureSynced(TextureResource& resource, const MG_State::GLState::ITextureObject& texture); Bool EnsureTextureResource(TextureResource& resource, const MG_State::GLState::ITextureObject& texture, TextureUploadTarget level0Target, const IntVec3& texelSize, SizeT byteSize); @@ -66,23 +54,13 @@ private: static Bool ResolveLevel0(const MG_State::GLState::ITextureObject& texture, TextureUploadTarget& outTarget, IntVec3& outTexelSize, SizeT& outByteSize); static VkFormat ResolveTextureFormat(TextureInternalFormat format); - Uint64 BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler) const; - - VkSampler GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler); - static VkFilter ToVkFilter(SamplerFilterMode mode); - static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode); - static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode); - static VkCompareOp ToVkCompareOp(SamplerCompareFunc func); VkDevice m_device = VK_NULL_HANDLE; VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; VmaAllocator m_allocator = nullptr; VkCommandPool m_commandPool = VK_NULL_HANDLE; VkQueue m_graphicsQueue = VK_NULL_HANDLE; - const VulkanRendererConfig* m_config = nullptr; UnorderedMap m_textureResources; - UnorderedMap m_samplers; - static inline XXH64_state_t* m_hashState = XXH64_createState(); }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 84650852..33df2aa3 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -214,13 +214,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory creation failed."); m_programFactory = MakeUnique(m_device, m_config); MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory creation failed."); - m_textureSamplerManager = MakeUnique(); - MOBILEGL_ASSERT(m_textureSamplerManager != nullptr, "VkTextureSamplerManager creation failed."); + m_textureManager = MakeUnique(); + MOBILEGL_ASSERT(m_textureManager != nullptr, "VkTextureManager creation failed."); auto succeeded = false; - succeeded = - m_textureSamplerManager->Initialize({m_device, m_physicalDevice.handle, m_allocator, m_commandPool, m_graphicsQueue, - &m_config}); - MOBILEGL_ASSERT(succeeded, "VkTextureSamplerManager initialization failed."); + succeeded = m_textureManager->Initialize( + {m_device, m_physicalDevice.handle, m_allocator, m_commandPool, m_graphicsQueue}); + MOBILEGL_ASSERT(succeeded, "VkTextureManager initialization failed."); + + m_samplerManager = MakeUnique(); + MOBILEGL_ASSERT(m_samplerManager != nullptr, "VkSamplerManager creation failed."); + succeeded = m_samplerManager->Initialize({m_device, &m_config}); + MOBILEGL_ASSERT(succeeded, "VkSamplerManager initialization failed."); m_framebufferManager = MakeUnique(); MOBILEGL_ASSERT(m_framebufferManager != nullptr, "VkFramebufferManager creation failed."); @@ -232,7 +236,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { succeeded = m_uniformDescriptorBinder->Initialize(m_device, m_allocator, m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment, m_config.MaxFramesInFlight, 16, 64, 4 * 1024 * 1024, - m_textureSamplerManager.get(), m_framebufferManager.get()); + m_textureManager.get(), m_samplerManager.get(), + m_framebufferManager.get()); MOBILEGL_ASSERT(succeeded, "UniformDescriptorBinder initialization failed."); m_vertexInputStateFactory = MakeUnique(m_config); MOBILEGL_ASSERT(m_vertexInputStateFactory != nullptr, "VertexInputStateFactory creation failed."); @@ -257,9 +262,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_pipelineFactory.reset(); m_programFactory.reset(); - if (m_textureSamplerManager) { - m_textureSamplerManager->Shutdown(); - m_textureSamplerManager.reset(); + if (m_samplerManager) { + m_samplerManager->Shutdown(); + m_samplerManager.reset(); + } + if (m_textureManager) { + m_textureManager->Shutdown(); + m_textureManager.reset(); } m_vertexInputStateFactory.reset(); for (auto& buffer : m_frameVertexUploadBuffers) { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 920f22f3..2297c26b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -17,7 +17,8 @@ #include "VkBufferObject.h" #include "VkRenderTargetManager.h" #include "VkRenderPassManager.h" -#include "VkTextureSamplerManager.h" +#include "VkSamplerManager.h" +#include "VkTextureManager.h" #include "MG_Util/Math/VectorTypes.h" #include #include @@ -154,7 +155,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { UniquePtr m_vertexInputStateFactory; UniquePtr m_framebufferManager; UniquePtr m_renderPassManager; - UniquePtr m_textureSamplerManager; + UniquePtr m_textureManager; + UniquePtr m_samplerManager; void CreateInstance(); VkResult SetupDebugMessenger();