From f80f7cdd33dbb010efa1454b5cc3ce67f652e040 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Jan 2021 22:23:22 +0100 Subject: [PATCH] Fix crash on Android if GPU doesn't support linear sampling of the shadow map --- Source/Engine/Graphics/GPULimits.h | 4 +- Source/Engine/Renderer/MotionBlurPass.cpp | 6 +-- Source/Engine/Renderer/ShadowsPass.cpp | 57 +++++++++++++++-------- Source/Engine/Renderer/ShadowsPass.h | 1 + 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/Source/Engine/Graphics/GPULimits.h b/Source/Engine/Graphics/GPULimits.h index 485f5d866..b50714fcd 100644 --- a/Source/Engine/Graphics/GPULimits.h +++ b/Source/Engine/Graphics/GPULimits.h @@ -174,10 +174,10 @@ API_ENUM(Attributes="Flags") enum class FormatSupport : int32 DECLARE_ENUM_OPERATORS(FormatSupport); // Helper macro to check if given format does not support a given set of feature flags -#define FORMAT_FEATURES_ARE_NOT_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & formatSupportFlags) != static_cast(formatSupportFlags)) +#define FORMAT_FEATURES_ARE_NOT_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & (formatSupportFlags)) != static_cast(formatSupportFlags)) // Helper macro to check if given format does support a given set of feature flags -#define FORMAT_FEATURES_ARE_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & formatSupportFlags) == static_cast(formatSupportFlags)) +#define FORMAT_FEATURES_ARE_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & (formatSupportFlags)) == static_cast(formatSupportFlags)) /// /// The features exposed for a particular format. diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index f25d14c51..609712e1f 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -57,11 +57,11 @@ bool MotionBlurPass::Init() // Prepare formats for the buffers auto format = MOTION_VECTORS_PIXEL_FORMAT; - if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(format).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(format).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) { - if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32_Float).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32_Float).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) format = PixelFormat::R32G32_Float; - else if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R16G16B16A16_Float).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + else if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R16G16B16A16_Float).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) format = PixelFormat::R16G16B16A16_Float; else format = PixelFormat::R32G32B32A32_Float; diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 5ec9d6446..11812a45c 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -6,6 +6,7 @@ #include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Content/Content.h" +#include "Engine/Graphics/PixelFormatExtensions.h" #if USE_EDITOR #include "Engine/Renderer/Lightmaps.h" #endif @@ -77,6 +78,19 @@ bool ShadowsPass::Init() _shader.Get()->OnReloading.Bind(this); #endif + // If GPU doesn't support linear sampling for the shadow map then fallback to the single sample on lowest quality + const auto formatTexture = PixelFormatExtensions::FindShaderResourceFormat(SHADOW_MAPS_FORMAT, false); + const auto formatFeaturesDepth = GPUDevice::Instance->GetFormatFeatures(SHADOW_MAPS_FORMAT); + const auto formatFeaturesTexture = GPUDevice::Instance->GetFormatFeatures(formatTexture); + _supportsShadows = FORMAT_FEATURES_ARE_SUPPORTED(formatFeaturesDepth.Support, FormatSupport::DepthStencil | FormatSupport::Texture2D) + && FORMAT_FEATURES_ARE_SUPPORTED(formatFeaturesTexture.Support, FormatSupport::ShaderSample | FormatSupport::ShaderSampleComparison); + if (!_supportsShadows) + { + LOG(Warning, "GPU doesn't support shadows rendering"); + LOG(Warning, "Format: {0} features support: {1}", (int32)SHADOW_MAPS_FORMAT, (uint32)formatFeaturesDepth.Support); + LOG(Warning, "Format: {0} features support: {1}", (int32)formatTexture, (uint32)formatFeaturesTexture.Support); + } + return false; } @@ -130,24 +144,27 @@ void ShadowsPass::updateShadowMapSize() // Select new size _currentShadowMapsQuality = Graphics::ShadowMapsQuality; - switch (_currentShadowMapsQuality) + if (_supportsShadows) { - case Quality::Ultra: - newSizeCSM = 2048; - newSizeCube = 1024; - break; - case Quality::High: - newSizeCSM = 1024; - newSizeCube = 1024; - break; - case Quality::Medium: - newSizeCSM = 1024; - newSizeCube = 512; - break; - case Quality::Low: - newSizeCSM = 512; - newSizeCube = 256; - break; + switch (_currentShadowMapsQuality) + { + case Quality::Ultra: + newSizeCSM = 2048; + newSizeCube = 1024; + break; + case Quality::High: + newSizeCSM = 1024; + newSizeCube = 1024; + break; + case Quality::Medium: + newSizeCSM = 1024; + newSizeCube = 512; + break; + case Quality::Low: + newSizeCSM = 512; + newSizeCube = 256; + break; + } } // Check if size will change @@ -194,7 +211,7 @@ bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererPo const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - return fade > ZeroTolerance; + return fade > ZeroTolerance && _supportsShadows; } bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSpotLightData& light) @@ -206,12 +223,12 @@ bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSp const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - return fade > ZeroTolerance; + return fade > ZeroTolerance && _supportsShadows; } bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererDirectionalLightData& light) { - return true; + return _supportsShadows; } void ShadowsPass::Prepare(RenderContext& renderContext, GPUContext* context) diff --git a/Source/Engine/Renderer/ShadowsPass.h b/Source/Engine/Renderer/ShadowsPass.h index 8deef8258..3d3190670 100644 --- a/Source/Engine/Renderer/ShadowsPass.h +++ b/Source/Engine/Renderer/ShadowsPass.h @@ -25,6 +25,7 @@ private: GPUPipelineStatePermutationsPs(Quality::MAX) * 2 * 2> _psShadowDir; GPUPipelineStatePermutationsPs(Quality::MAX) * 2> _psShadowPoint; GPUPipelineStatePermutationsPs(Quality::MAX) * 2> _psShadowSpot; + bool _supportsShadows; // Shadow maps stuff int32 _shadowMapsSizeCSM;