diff --git a/code/graphics/vulkan/VulkanRenderer.cpp b/code/graphics/vulkan/VulkanRenderer.cpp index a0e1f426051..902a8042b8b 100644 --- a/code/graphics/vulkan/VulkanRenderer.cpp +++ b/code/graphics/vulkan/VulkanRenderer.cpp @@ -17,6 +17,7 @@ #include "mod_table/mod_table.h" #include +#include VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE @@ -800,6 +801,29 @@ bool VulkanRenderer::isTextureCompressionBCSupported() const return m_deviceFeatures.textureCompressionBC == VK_TRUE; } +bool VulkanRenderer::isTextureCompressionS3TCSupported() const +{ + if (!m_physicalDevice) { + return false; + } + + if (m_deviceFeatures.textureCompressionBC == VK_TRUE) + return true; + + constexpr std::array formats = { + vk::Format::eBc1RgbaUnormBlock, // DXT1 + vk::Format::eBc2UnormBlock, // DXT3 + vk::Format::eBc3UnormBlock, // DXT5 + }; + + constexpr vk::FormatFeatureFlags required = vk::FormatFeatureFlagBits::eSampledImage | vk::FormatFeatureFlagBits::eSampledImageFilterLinear; + + return std::all_of(formats.begin(), formats.end(), [this, required](vk::Format fmt) { + const auto props = m_physicalDevice.getFormatProperties(fmt); + return (props.optimalTilingFeatures & required) == required; + }); +} + bool VulkanRenderer::isDepthClampSupported() const { if (!m_physicalDevice) { diff --git a/code/graphics/vulkan/VulkanRenderer.h b/code/graphics/vulkan/VulkanRenderer.h index 3d890b18a84..7610b0d551f 100644 --- a/code/graphics/vulkan/VulkanRenderer.h +++ b/code/graphics/vulkan/VulkanRenderer.h @@ -192,6 +192,11 @@ class VulkanRenderer { */ bool isTextureCompressionBCSupported() const; + /** + * @brief Check if BC 1/2/3 texture compression is supported + */ + bool isTextureCompressionS3TCSupported() const; + /** * @brief Check if depth clamping is supported (used by the shadow pass) */ diff --git a/code/graphics/vulkan/gr_vulkan.cpp b/code/graphics/vulkan/gr_vulkan.cpp index 8fb01daa2a0..c3f5dd52765 100644 --- a/code/graphics/vulkan/gr_vulkan.cpp +++ b/code/graphics/vulkan/gr_vulkan.cpp @@ -140,7 +140,8 @@ bool vulkan_is_capable(gr_capability capability) return getRendererInstance()->isTextureCompressionBCSupported(); case gr_capability::CAPABILITY_S3TC: // Vulkan always supports BC1/BC2/BC3 (S3TC equivalent) as core features - return true; + // But this is optional on mobile gpus, so check anyway + return getRendererInstance()->isTextureCompressionS3TCSupported(); case gr_capability::CAPABILITY_LARGE_SHADER: // Same troubleshooting switch as OpenGL: -no_large_shaders splits the model // ubershader into per-flag-combination variants for drivers that choke on the