diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp index 41e8e60e..1f17a194 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp @@ -717,16 +717,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { "ProgramFactory::ReflectLayout: sampler binding %u exceeds maxBindings=%u for '%s'", binding, m_maxBindings, uniformName.c_str()); + const Int location = program.GetUniformLocation(uniformName); + if (location < 0) { + continue; + } + MOBILEGL_ASSERT(entry.bindingKinds[binding] == DescriptorBindingKind::None || entry.bindingKinds[binding] == DescriptorBindingKind::CombinedImageSampler, "ProgramFactory::ReflectLayout: descriptor binding %u has conflicting kinds for sampler '%s'", binding, uniformName.c_str()); entry.bindingKinds[binding] = DescriptorBindingKind::CombinedImageSampler; - const Int location = program.GetUniformLocation(uniformName); - const TextureTarget target = - location >= 0 ? UniformTypeToTextureTarget(program.GetUniformType(static_cast(location))) - : ReflectImageTraitsToTextureTarget(sampler->image); + const TextureTarget target = UniformTypeToTextureTarget(program.GetUniformType(static_cast(location))); MOBILEGL_ASSERT(target != TextureTarget::Unknown, "ProgramFactory::ReflectLayout: failed to resolve sampler target for '%s'", uniformName.c_str()); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp index 96a7f841..f0f24ea6 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp @@ -10,10 +10,38 @@ #include "MG_State/GLState/Core.h" #include "MG_State/GLState/ProgramState/ProgramObject.h" +#include "MG_State/GLState/TextureState/TextureObject2D.h" #include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h" #include namespace MobileGL::MG_Backend::DirectVulkan { + namespace { + constexpr Uint kFallbackTextureExternalIndexBase = 0xFFFF0000u; + + SharedPtr CreateFallbackTexture2D(Uint externalIndex) { + static constexpr Array kBlackPixel = {0, 0, 0, 255}; + + auto texture = MakeShared(externalIndex); + texture->SetInternalFormat(TextureInternalFormat::RGBA8); + texture->AllocateStorage(TextureUploadTarget::Texture2D, 0, + {.texelSize = {1, 1, 1}, .byteSize = kBlackPixel.size()}); + texture->UpdateMipmapSubData(TextureUploadTarget::Texture2D, 0, + {.data = const_cast(kBlackPixel.data()), + .size = kBlackPixel.size()}); + texture->SetBaseLevel(0); + texture->SetMaxLevel(0); + + const auto& sampler = texture->GetSamplerObject(); + MOBILEGL_ASSERT(sampler != nullptr, "CreateFallbackTexture2D: texture sampler is null"); + sampler->SetWrapS(SamplerWrapMode::ClampToEdge); + sampler->SetWrapT(SamplerWrapMode::ClampToEdge); + sampler->SetMinFilter(SamplerFilterMode::Nearest); + sampler->SetMagFilter(SamplerFilterMode::Nearest); + sampler->SetMipmapMode(SamplerMipmapMode::None); + return texture; + } + } + static Bool FindFramebufferAttachmentForTexture(const MG_State::GLState::FramebufferObject& framebuffer, const MG_State::GLState::ITextureObject& texture, FramebufferAttachmentType& outAttachment, Int& outLevel) { @@ -145,6 +173,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_peakDescriptorSetsObserved = 0; m_textureManager = nullptr; m_samplerManager = nullptr; + for (auto& fallbackTexture : m_samplerFallbackTextures) { + fallbackTexture.reset(); + } } void UniformManager::BeginFrame(Uint32 frameIndex) { @@ -292,13 +323,38 @@ namespace MobileGL::MG_Backend::DirectVulkan { outTexture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject(); if (!outTexture) { - MGLOG_E("ResolveSamplerTexture: no texture bound for sampler binding=%u location=%d unit=%d target=%d", + outTexture = GetOrCreateSamplerFallbackTexture(preferredTarget); + } + + if (!outTexture) { + MGLOG_E( + "ResolveSamplerTexture: no texture bound and no fallback available for sampler binding=%u location=%d unit=%d target=%d", binding, location, unit, static_cast(preferredTarget)); return false; } return outTexture != nullptr; } + SharedPtr + UniformManager::GetOrCreateSamplerFallbackTexture(TextureTarget target) const { + const SizeT targetIndex = static_cast(target); + MOBILEGL_ASSERT(targetIndex < m_samplerFallbackTextures.size(), + "GetOrCreateSamplerFallbackTexture: invalid texture target %d", static_cast(target)); + + auto& fallbackTexture = m_samplerFallbackTextures[targetIndex]; + if (fallbackTexture != nullptr) { + return fallbackTexture; + } + + switch (target) { + case TextureTarget::Texture2D: + fallbackTexture = CreateFallbackTexture2D(kFallbackTextureExternalIndexBase + static_cast(targetIndex)); + return fallbackTexture; + default: + return nullptr; + } + } + Bool UniformManager::CollectSampledTextures(const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, Vector& outTextures) { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h index c96ba1fa..f353ee15 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h @@ -65,6 +65,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, Uint32 binding, SharedPtr& outTexture) const; + SharedPtr GetOrCreateSamplerFallbackTexture(TextureTarget target) const; Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, Uint32 binding, VkDescriptorImageInfo& outImageInfo) const; @@ -90,6 +91,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 m_peakDescriptorSetsObserved = 0; VkTextureManager* m_textureManager = nullptr; VkSamplerManager* m_samplerManager = nullptr; + mutable Array, + static_cast(TextureTarget::TextureTargetCount)> m_samplerFallbackTextures{}; }; } // namespace MobileGL::MG_Backend::DirectVulkan