From 31795675f130894507614fce12bb3461bdcdcde5 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 19 Sep 2026 22:27:43 -0400 Subject: [PATCH] p5f fm: build Vulkan samplers from server-owned values --- .../Renderer/VkSamplerManager.cpp | 87 +++++++++++++++++++ .../DirectVulkan/Renderer/VkSamplerManager.h | 24 +++++ 2 files changed, 111 insertions(+) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp index 0d93f094..07383710 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp @@ -15,7 +15,37 @@ namespace MobileGL::MG_Backend::DirectVulkan { namespace { +#if MOBILEGL_BUILD_DISAGGREGATED + // An accessor view over the wire's POD values, not a GL object with constructor, + // lifetime ID, allocator hooks or mutation callbacks. Both sources below use + // one sampler policy, including format-sensitive border-color conversion. + struct SamplerParametersSource { + const SamplerParameters& Params; + SamplerWrapMode GetWrapS() const { return Params.wrapS; } + SamplerWrapMode GetWrapT() const { return Params.wrapT; } + SamplerWrapMode GetWrapR() const { return Params.wrapR; } + SamplerFilterMode GetMinFilter() const { return Params.minFilter; } + SamplerFilterMode GetMagFilter() const { return Params.magFilter; } + SamplerMipmapMode GetMipmapMode() const { return Params.mipmapMode; } + Float GetMinLod() const { return Params.minLod; } + Float GetMaxLod() const { return Params.maxLod; } + Float GetLodBias() const { return Params.lodBias; } + Float GetMaxAnisotropy() const { return Params.maxAnisotropy; } + SamplerCompareMode GetCompareMode() const { return Params.compareMode; } + SamplerCompareFunc GetSamplerCompareFunc() const { return Params.compareFunc; } + const FloatVec4& GetBorderColor() const { return Params.borderColor; } + const IntVec4& GetBorderColorI() const { return Params.borderColorI; } + const UintVec4& GetBorderColorUI() const { return Params.borderColorUI; } + // Cache identity is entirely content-based; these fields are diagnostics. + Uint GetExternalIndex() const { return 0; } + Uint16 GetVersion() const { return 0; } + }; + + template + Bool UsesBorderColor(const SamplerSource& sampler) { +#else Bool UsesBorderColor(const MG_State::GLState::SamplerObject& sampler) { +#endif return sampler.GetWrapS() == SamplerWrapMode::ClampToBorder || sampler.GetWrapT() == SamplerWrapMode::ClampToBorder || sampler.GetWrapR() == SamplerWrapMode::ClampToBorder; @@ -191,14 +221,24 @@ namespace MobileGL::MG_Backend::DirectVulkan { return std::fabs(lhs - rhs) <= 1e-6f; } +#if MOBILEGL_BUILD_DISAGGREGATED + template + Float ResolveEffectiveMaxLod(const SamplerSource& sampler) { +#else Float ResolveEffectiveMaxLod(const MG_State::GLState::SamplerObject& sampler) { +#endif if (sampler.GetMipmapMode() == SamplerMipmapMode::None) { return 0.0f; } return sampler.GetMaxLod(); } +#if MOBILEGL_BUILD_DISAGGREGATED + template + Float ResolveEffectiveMinLod(const SamplerSource& sampler, Float effectiveMaxLod) { +#else Float ResolveEffectiveMinLod(const MG_State::GLState::SamplerObject& sampler, Float effectiveMaxLod) { +#endif return std::min(sampler.GetMinLod(), effectiveMaxLod); } @@ -209,7 +249,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { // GL's non-mipmapped minification - large enough for lambda to stay positive, small enough // that a NEAREST mip mode still rounds down to level 0. Clamped rather than assigned, so a // texture whose GL_TEXTURE_MAX_LOD really is 0 keeps magnifying as GL says it must. +#if MOBILEGL_BUILD_DISAGGREGATED + template + Float ResolveSingleLevelMaxLod(const SamplerSource& sampler, Bool singleLevelView) { +#else Float ResolveSingleLevelMaxLod(const MG_State::GLState::SamplerObject& sampler, Bool singleLevelView) { +#endif const Float maxLod = ResolveEffectiveMaxLod(sampler); return singleLevelView ? std::min(maxLod, 0.25f) : maxLod; } @@ -230,7 +275,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } +#if MOBILEGL_BUILD_DISAGGREGATED + template + Float VkSamplerManager::ResolveEffectiveMaxAnisotropy(const SamplerSource& sampler, +#else Float VkSamplerManager::ResolveEffectiveMaxAnisotropy(const MG_State::GLState::SamplerObject& sampler, +#endif Bool forceNearestFiltering) const { if (!m_samplerAnisotropySupported) return 1.0f; if (forceNearestFiltering) return 1.0f; @@ -289,7 +339,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } +#if MOBILEGL_BUILD_DISAGGREGATED + template + Uint64 VkSamplerManager::BuildSamplerKey(const SamplerSource& sampler, +#else Uint64 VkSamplerManager::BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler, +#endif Bool forceNearestFiltering, Bool singleLevelView, const ResolvedBorderColor& borderColor) const { MOBILEGL_ASSERT(m_config != nullptr, "VkSamplerManager::BuildSamplerKey: m_config is null"); @@ -335,8 +390,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { return XXH64_digest(m_hashState); } +#if MOBILEGL_BUILD_DISAGGREGATED + template + VkSampler VkSamplerManager::GetOrCreateSamplerImpl(const SamplerSource& sampler, + TextureInternalFormat format, +#else VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler, const MG_State::GLState::ITextureObject& texture, +#endif Bool forceNearestFiltering, Uint32 viewLevelCount) { // A view that exposes a single mip level has no second level to blend with, so GL's // *_MIPMAP_* minification filters degenerate to plain filtering on the base level - @@ -348,7 +409,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { // the default-framebuffer blit shader had to work around with an explicit-LOD sample. const Bool singleLevelView = viewLevelCount == 1; // Resolved once and used for both the key and the create-info; see ResolvedBorderColor. +#if MOBILEGL_BUILD_DISAGGREGATED + const ResolvedBorderColor borderColor = ResolveBorderColor(sampler, format); +#else const ResolvedBorderColor borderColor = ResolveBorderColor(sampler, texture); +#endif const Uint64 key = BuildSamplerKey(sampler, forceNearestFiltering, singleLevelView, borderColor); auto it = m_samplers.find(key); if (it != m_samplers.end()) { @@ -408,6 +473,20 @@ namespace MobileGL::MG_Backend::DirectVulkan { return vkSampler; } +#if MOBILEGL_BUILD_DISAGGREGATED + VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler, + const MG_State::GLState::ITextureObject& texture, + Bool forceNearestFiltering, Uint32 viewLevelCount) { + return GetOrCreateSamplerImpl(sampler, texture.GetFormat(), forceNearestFiltering, viewLevelCount); + } + + VkSampler VkSamplerManager::GetOrCreateSamplerFromParameters(const SamplerParameters& parameters, + TextureInternalFormat format, Bool forceNearestFiltering, Uint32 viewLevelCount) { + return GetOrCreateSamplerImpl(SamplerParametersSource{parameters}, format, + forceNearestFiltering, viewLevelCount); + } +#endif + VkFilter VkSamplerManager::ToVkFilter(SamplerFilterMode mode) { return mode == SamplerFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; } @@ -463,8 +542,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } +#if MOBILEGL_BUILD_DISAGGREGATED + template + VkSamplerManager::ResolvedBorderColor VkSamplerManager::ResolveBorderColor( + const SamplerSource& sampler, TextureInternalFormat format) const { +#else VkSamplerManager::ResolvedBorderColor VkSamplerManager::ResolveBorderColor( const MG_State::GLState::SamplerObject& sampler, const MG_State::GLState::ITextureObject& texture) const { +#endif ResolvedBorderColor resolved{}; if (!UsesBorderColor(sampler)) { return resolved; // FLOAT_TRANSPARENT_BLACK, never sampled @@ -472,7 +557,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { // Border colour is sampler state: a bound sampler object supplies its own, and a texture // with none reaches the very same value through the sampler object it owns. +#if !MOBILEGL_BUILD_DISAGGREGATED const auto format = texture.GetFormat(); +#endif const auto domain = ResolveBorderColorDomain(format); const Bool canUseCustom = m_customBorderColorSupported && m_maxCustomBorderColorSamplers > 0 && m_customBorderColorSamplerCount < m_maxCustomBorderColorSamplers; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h index 2c24aee0..199f326d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h @@ -46,6 +46,12 @@ public: const MG_State::GLState::ITextureObject& texture, Bool forceNearestFiltering = false, Uint32 viewLevelCount = 0); +#if MOBILEGL_BUILD_DISAGGREGATED + // The applier owns these values: effective bound/builtin sampler parameters and + // the sampled texture view's format/range. No frontend object is constructed. + VkSampler GetOrCreateSamplerFromParameters(const SamplerParameters& parameters, + TextureInternalFormat format, Bool forceNearestFiltering = false, Uint32 viewLevelCount = 0); +#endif // Frame boundary hook: ages the sampler cache and destroys samplers not used // for many frames. The key hashes continuous float state (lodBias, LOD clamps, // anisotropy), so an app animating those would otherwise mint an unbounded @@ -86,19 +92,37 @@ private: Bool usesCustomBorderColor = false; }; +#if MOBILEGL_BUILD_DISAGGREGATED + template + VkSampler GetOrCreateSamplerImpl(const SamplerSource& sampler, TextureInternalFormat format, + Bool forceNearestFiltering, Uint32 viewLevelCount); + template + Uint64 BuildSamplerKey(const SamplerSource& sampler, Bool forceNearestFiltering, +#else Uint64 BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler, Bool forceNearestFiltering, +#endif Bool singleLevelView, const ResolvedBorderColor& borderColor) const; static VkFilter ToVkFilter(SamplerFilterMode mode); static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode); static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode); static VkCompareOp ToVkCompareOp(SamplerCompareFunc func); +#if MOBILEGL_BUILD_DISAGGREGATED + template + ResolvedBorderColor ResolveBorderColor(const SamplerSource& sampler, TextureInternalFormat format) const; +#else ResolvedBorderColor ResolveBorderColor(const MG_State::GLState::SamplerObject& sampler, const MG_State::GLState::ITextureObject& texture) const; +#endif // The anisotropy Vulkan will actually apply: 1.0 (i.e. disabled) unless the feature is on and // the sampler filters linearly both ways, otherwise the GL request clamped to the device limit. // GL happily carries GL_TEXTURE_MAX_ANISOTROPY on a NEAREST sampler (Blaze3D's blocks do exactly // that) while Vulkan forbids anisotropyEnable there, so the GL value must never be forwarded raw. +#if MOBILEGL_BUILD_DISAGGREGATED + template + Float ResolveEffectiveMaxAnisotropy(const SamplerSource& sampler, +#else Float ResolveEffectiveMaxAnisotropy(const MG_State::GLState::SamplerObject& sampler, +#endif Bool forceNearestFiltering) const; VkDevice m_device = VK_NULL_HANDLE;