From 659fbf26daf73735fd8836726ee194cb22e67370 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 5 May 2026 00:40:17 +0800 Subject: [PATCH] [Fix] (MG_Backend/DirectVulkan): fix depth sampler state, fixing BSL shadow --- .../DirectVulkan/Renderer/UniformManager.cpp | 9 +- .../DirectVulkan/Renderer/UniformManager.h | 1 + .../Renderer/VkSamplerManager.cpp | 91 +++++++++++++++++-- .../DirectVulkan/Renderer/VkSamplerManager.h | 11 ++- .../Renderer/VkTextureManager.cpp | 32 +++++++ .../DirectVulkan/Renderer/VkTextureManager.h | 9 ++ .../DirectVulkan/Renderer/VulkanRenderer.cpp | 6 ++ 7 files changed, 148 insertions(+), 11 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp index a3852041..b25e4b0b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp @@ -225,7 +225,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } outImageInfo = { - .sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse), + .sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse, *texture), .imageView = resource->fullView, .imageLayout = resource->layout, }; @@ -253,8 +253,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { samplerBindingOverride.texture->GetExternalIndex()); outImageInfo = { - .sampler = m_samplerManager->GetOrCreateSampler(*samplerBindingOverride.sampler), - .imageView = resource->fullView, + .sampler = m_samplerManager->GetOrCreateSampler(*samplerBindingOverride.sampler, + *samplerBindingOverride.texture), + .imageView = samplerBindingOverride.imageView != VK_NULL_HANDLE ? + samplerBindingOverride.imageView : + resource->fullView, .imageLayout = resource->layout, }; return outImageInfo.sampler != VK_NULL_HANDLE; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h index 79cb8f0b..c96ba1fa 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h @@ -28,6 +28,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 binding = 0; MG_State::GLState::ITextureObject* texture = nullptr; const MG_State::GLState::SamplerObject* sampler = nullptr; + VkImageView imageView = VK_NULL_HANDLE; }; Bool Initialize(VkDevice device, VkBufferManager* bufferManager, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp index da58c3aa..82249c9b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp @@ -10,7 +10,37 @@ #include "MG_State/GLState/Core.h" +#include + namespace MobileGL::MG_Backend::DirectVulkan { + namespace { + Bool UsesBorderColor(const MG_State::GLState::SamplerObject& sampler) { + return sampler.GetWrapS() == SamplerWrapMode::ClampToBorder || + sampler.GetWrapT() == SamplerWrapMode::ClampToBorder || + sampler.GetWrapR() == SamplerWrapMode::ClampToBorder; + } + + Bool IsDepthTextureFormat(TextureInternalFormat format) { + switch (format) { + case TextureInternalFormat::DepthComponent: + case TextureInternalFormat::DepthComponent16: + case TextureInternalFormat::DepthComponent24: + case TextureInternalFormat::DepthComponent32: + case TextureInternalFormat::DepthComponent32F: + case TextureInternalFormat::Depth24Stencil8: + case TextureInternalFormat::Depth32FStencil8: + case TextureInternalFormat::DepthStencil: + return true; + default: + return false; + } + } + + Bool NearlyEqual(Float lhs, Float rhs) { + return std::fabs(lhs - rhs) <= 1e-6f; + } + } // namespace + Bool VkSamplerManager::Initialize(const InitInfo& initInfo) { Shutdown(); @@ -34,7 +64,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_config = nullptr; } - Uint64 VkSamplerManager::BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler) const { + Uint64 VkSamplerManager::BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture) const { MOBILEGL_ASSERT(m_config != nullptr, "VkSamplerManager::BuildSamplerKey: m_config is null"); XXHASH_VERIFY(XXH64_reset(m_hashState, m_config->CacheVersion)); @@ -58,13 +89,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { 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(); + const auto compareFunc = ResolveCompareFunc(sampler, texture); XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc))); + const auto borderColor = ResolveVkBorderColor(sampler, texture); + XXHASH_VERIFY(XXH64_update(m_hashState, &borderColor, sizeof(borderColor))); return XXH64_digest(m_hashState); } - VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler) { - const Uint64 key = BuildSamplerKey(sampler); + VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture) { + const Uint64 key = BuildSamplerKey(sampler, texture); auto it = m_samplers.find(key); if (it != m_samplers.end()) { return it->second.handle; @@ -82,10 +116,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { samplerInfo.anisotropyEnable = VK_FALSE; samplerInfo.maxAnisotropy = 1.0f; samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE; - samplerInfo.compareOp = ToVkCompareOp(sampler.GetSamplerCompareFunc()); + samplerInfo.compareOp = ToVkCompareOp(ResolveCompareFunc(sampler, texture)); samplerInfo.minLod = sampler.GetMinLod(); samplerInfo.maxLod = sampler.GetMaxLod(); - samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + samplerInfo.borderColor = ResolveVkBorderColor(sampler, texture); samplerInfo.unnormalizedCoordinates = VK_FALSE; VkSampler vkSampler = VK_NULL_HANDLE; @@ -153,4 +187,49 @@ namespace MobileGL::MG_Backend::DirectVulkan { return VK_COMPARE_OP_ALWAYS; } } + + SamplerCompareFunc VkSamplerManager::ResolveCompareFunc(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture) { + const auto compareFunc = sampler.GetSamplerCompareFunc(); + if (sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture && + IsDepthTextureFormat(texture.GetFormat()) && compareFunc == SamplerCompareFunc::Always) { + return SamplerCompareFunc::LessEqual; + } + + return compareFunc; + } + + VkBorderColor VkSamplerManager::ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture) { + if (!UsesBorderColor(sampler)) { + return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + } + + const auto& borderColor = texture.GetBorderColor(); + const Bool isDepthTexture = IsDepthTextureFormat(texture.GetFormat()); + + if (isDepthTexture) { + if (NearlyEqual(borderColor.x(), 1.0f)) { + return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + } + if (NearlyEqual(borderColor.x(), 0.0f)) { + return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + } + } + + const Bool rgbZero = NearlyEqual(borderColor.x(), 0.0f) && NearlyEqual(borderColor.y(), 0.0f) && + NearlyEqual(borderColor.z(), 0.0f); + if (rgbZero && NearlyEqual(borderColor.w(), 0.0f)) { + return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + } + if (rgbZero && NearlyEqual(borderColor.w(), 1.0f)) { + return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + } + if (NearlyEqual(borderColor.x(), 1.0f) && NearlyEqual(borderColor.y(), 1.0f) && + NearlyEqual(borderColor.z(), 1.0f) && NearlyEqual(borderColor.w(), 1.0f)) { + return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + } + + return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h index 08a6508c..edd62f3c 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h @@ -15,6 +15,7 @@ namespace MobileGL::MG_State::GLState { class SamplerObject; +class ITextureObject; } namespace MobileGL::MG_Backend::DirectVulkan { @@ -28,7 +29,8 @@ public: Bool Initialize(const InitInfo& initInfo); void Shutdown(); - VkSampler GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler); + VkSampler GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture); private: struct SamplerCacheEntry { @@ -37,11 +39,16 @@ private: Uint16 version = 0; }; - Uint64 BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler) const; + Uint64 BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture) const; static VkFilter ToVkFilter(SamplerFilterMode mode); static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode); static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode); static VkCompareOp ToVkCompareOp(SamplerCompareFunc func); + static SamplerCompareFunc ResolveCompareFunc(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture); + static VkBorderColor ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture); VkDevice m_device = VK_NULL_HANDLE; const VulkanRendererConfig* m_config = nullptr; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index cd9c7842..40cdbe1a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -181,6 +181,34 @@ namespace MobileGL::MG_Backend::DirectVulkan { return perMipView; } + VkImageView VkTextureManager::GetOrCreateSampledViewAtMipLevel(MG_State::GLState::ITextureObject& texture, + Uint32 mipLevel) { + TextureResource* resource = SyncTextureAndGetDescriptor(texture); + if (resource == nullptr || resource->image == VK_NULL_HANDLE || mipLevel >= resource->mipLevels) { + return VK_NULL_HANDLE; + } + + if (resource->perMipSampledViews.size() != resource->mipLevels) { + resource->perMipSampledViews.resize(resource->mipLevels, VK_NULL_HANDLE); + } + + VkImageView& perMipSampledView = resource->perMipSampledViews[mipLevel]; + if (perMipSampledView != VK_NULL_HANDLE) { + return perMipSampledView; + } + + const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture); + perMipSampledView = CreateImageView(resource->image, resource->format, resource->aspect, mipLevel, 1, + &sampledComponents); + if (perMipSampledView == VK_NULL_HANDLE) { + MGLOG_D("%s: CreateImageView failed for textureId=%d mipLevel=%u", __func__, texture.GetExternalIndex(), + mipLevel); + return VK_NULL_HANDLE; + } + + return perMipSampledView; + } + void VkTextureManager::UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout) { MOBILEGL_ASSERT(texture != nullptr, "UpdateTrackedImageLayout: texture is null"); auto it = m_textureResources.find(texture); @@ -366,6 +394,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (resource.perMipViews.size() != mipLevels) { resource.perMipViews.resize(mipLevels, VK_NULL_HANDLE); } + if (resource.perMipSampledViews.size() != mipLevels) { + resource.perMipSampledViews.resize(mipLevels, VK_NULL_HANDLE); + } return true; } @@ -402,6 +433,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { resource.extent = {static_cast(texelSize.x()), static_cast(texelSize.y())}; resource.mipLevels = mipLevels; resource.perMipViews.assign(mipLevels, VK_NULL_HANDLE); + resource.perMipSampledViews.assign(mipLevels, VK_NULL_HANDLE); resource.sampledBaseMipLevel = 0; resource.sampledLevelCount = mipLevels; resource.format = format; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h index 7f798fb8..d37be0b9 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -33,6 +33,7 @@ public: VmaAllocation allocation = nullptr; VkImageView fullView = VK_NULL_HANDLE; Vector perMipViews; + Vector perMipSampledViews; VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; VkExtent2D extent = {0, 0}; Uint32 mipLevels = 1; @@ -49,6 +50,7 @@ public: std::swap(this->allocation, that.allocation); std::swap(this->fullView, that.fullView); std::swap(this->perMipViews, that.perMipViews); + std::swap(this->perMipSampledViews, that.perMipSampledViews); std::swap(this->layout, that.layout); std::swap(this->extent, that.extent); std::swap(this->mipLevels, that.mipLevels); @@ -68,11 +70,17 @@ public: vkDestroyImageView(s_device, attachmentView, nullptr); } } + for (const auto sampledView : perMipSampledViews) { + if (sampledView != VK_NULL_HANDLE) { + vkDestroyImageView(s_device, sampledView, nullptr); + } + } if (image != VK_NULL_HANDLE && allocation != nullptr) { vmaDestroyImage(s_allocator, image, allocation); } fullView = VK_NULL_HANDLE; perMipViews.clear(); + perMipSampledViews.clear(); image = VK_NULL_HANDLE; allocation = nullptr; layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -99,6 +107,7 @@ public: TextureResource* SyncTextureAndGetDescriptor( MG_State::GLState::ITextureObject& texture); VkImageView GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel); + VkImageView GetOrCreateSampledViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel); void UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout); Bool TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 05effa90..1683b901 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -1036,6 +1036,11 @@ void main() { sourceTexture->GetExternalIndex()); return false; } + const VkImageView sourceImageView = + m_textureManager->GetOrCreateSampledViewAtMipLevel(*sourceTexture, srcBinding.mipLevel); + MOBILEGL_ASSERT(sourceImageView != VK_NULL_HANDLE, + "TryBlitToDefaultFramebufferWithShader: failed to create sampled view for textureId=%d mip=%u", + sourceTexture->GetExternalIndex(), srcBinding.mipLevel); auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(drawFbo, m_imageIndexAcquired); const Bool ok = VkRenderPassManager::BeginRenderPass(frame.commandBuffer, renderPassEntry); @@ -1107,6 +1112,7 @@ void main() { .texture = sourceTexture.get(), .sampler = (filter == GL_LINEAR ? m_blitResources.linearSampler.get() : m_blitResources.nearestSampler.get()), + .imageView = sourceImageView, }; ProgramFactory::CompileOptionFlags blitTransformFlags = 0; const auto& blitProgramObj = m_programFactory->GetOrCreateProgram(*m_blitResources.program, blitTransformFlags);