diff --git a/CMakeLists.txt b/CMakeLists.txt index dd5bfaaf8d6d..91426ac0c95b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,9 @@ set(FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB "8" CACHE STRING "Size of the Metal handle arena, default 8." ) +# Enable exceptions by default in spirv-cross. +set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF) + # ================================================================================================== # CMake policies # ================================================================================================== @@ -339,6 +342,7 @@ endif() if (CYGWIN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) endif() if (MSVC) @@ -375,6 +379,7 @@ endif() # saved by -fno-exception and 10 KiB saved by -fno-rtti). if (ANDROID OR IOS OR WEBGL) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-rtti") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) if (ANDROID OR WEBGL) # Omitting unwind info prevents the generation of readable stack traces in crash reports on iOS @@ -386,6 +391,7 @@ endif() # std::visit, which is not supported on iOS 11.0 when exceptions are enabled. if (IOS) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-exceptions") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) endif() # With WebGL, we disable RTTI even for debug builds because we pass emscripten::val back and forth diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index c3161a7700b9..b731a9ccf55c 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -8,3 +8,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut +- matc: Support optimizations for ESSL 1.0 code. diff --git a/filament/src/materials/blitLow.mat b/filament/src/materials/blitLow.mat index 4bebc8612e60..09e6c79920c2 100644 --- a/filament/src/materials/blitLow.mat +++ b/filament/src/materials/blitLow.mat @@ -35,7 +35,7 @@ vertex { fragment { void postProcess(inout PostProcessInputs postProcess) { -#if __VERSION__ == 100 +#if FILAMENT_EFFECTIVE_VERSION == 100 postProcess.color = texture2D(materialParams_color, variable_vertex.xy); #else postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0); diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 871bc2980022..00bdde0eff6d 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -32,6 +32,7 @@ #include "MetalArgumentBuffer.h" #include "SpirvFixup.h" +#include "utils/ostream.h" #include <filament/MaterialEnums.h> @@ -395,7 +396,9 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co break; case MaterialBuilder::Optimization::SIZE: case MaterialBuilder::Optimization::PERFORMANCE: - fullOptimization(tShader, config, internalConfig); + if (!fullOptimization(tShader, config, internalConfig)) { + return false; + } break; } @@ -478,7 +481,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, } } -void GLSLPostProcessor::fullOptimization(const TShader& tShader, +bool GLSLPostProcessor::fullOptimization(const TShader& tShader, GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const { SpirvBlob spirv; @@ -546,7 +549,16 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, } } +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS *internalConfig.glslOutput = glslCompiler.compile(); +#else + try { + *internalConfig.glslOutput = glslCompiler.compile(); + } catch (spirv_cross::CompilerError e) { + slog.e << "ERROR: " << e.what() << io::endl; + return false; + } +#endif // spirv-cross automatically redeclares gl_ClipDistance if it's used. Some drivers don't // like this, so we simply remove it. @@ -559,6 +571,7 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, str.replace(found, clipDistanceDefinition.length(), ""); } } + return true; } std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer( diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index 8e3c2e1e4ac8..c13dece6369f 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -93,7 +93,7 @@ class GLSLPostProcessor { ShaderMinifier minifier; }; - void fullOptimization(const glslang::TShader& tShader, + bool fullOptimization(const glslang::TShader& tShader, GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const; void preprocessOptimization(glslang::TShader& tShader, diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index d175c4e1c638..d43fb997340e 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -141,11 +141,10 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics, }); if (featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0 && shaderModel == ShaderModel::MOBILE) { - // ESSL1 code may never be compiled to SPIR-V. mCodeGenPermutations.push_back({ shaderModel, TargetApi::OPENGL, - TargetLanguage::GLSL, + glTargetLanguage, filament::backend::FeatureLevel::FEATURE_LEVEL_0 }); } @@ -1421,15 +1420,15 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& uniforms.push_back({ "objectUniforms.data[0].morphTargetCount", offsetof(PerRenderableUib, data[0].morphTargetCount), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].flagsChannels", offsetof(PerRenderableUib, data[0].flagsChannels), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].objectId", offsetof(PerRenderableUib, data[0].objectId), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].userData", offsetof(PerRenderableUib, data[0].userData), 1, diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index b4818a50fdeb..2c20307b7f4e 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -151,6 +151,36 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade out << "#define FILAMENT_HAS_FEATURE_INSTANCING\n"; } + // During compilation and optimization, __VERSION__ reflects the shader language version of the + // intermediate code, not the version of the final code. spirv-cross automatically adapts + // certain language features (e.g. fragment output) but leaves others untouched (e.g. sampler + // functions, bit shift operations). Client code may have to make decisions based on this + // information, so define a FILAMENT_EFFECTIVE_VERSION constant. + const char *effective_version; + if (mTargetLanguage == TargetLanguage::GLSL) { + effective_version = "__VERSION__"; + } else { + switch (mShaderModel) { + case ShaderModel::MOBILE: + if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) { + effective_version = "300"; + } else { + effective_version = "100"; + } + break; + case ShaderModel::DESKTOP: + if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) { + effective_version = "450"; + } else { + effective_version = "410"; + } + break; + default: + assert(false); + } + } + generateDefine(out, "FILAMENT_EFFECTIVE_VERSION", effective_version); + if (stage == ShaderStage::VERTEX) { CodeGenerator::generateDefine(out, "FLIP_UV_ATTRIBUTE", material.flipUV); CodeGenerator::generateDefine(out, "LEGACY_MORPHING", material.useLegacyMorphing); @@ -271,8 +301,9 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade out << '\n'; out << SHADERS_COMMON_DEFINES_GLSL_DATA; - if (mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0 && - material.featureLevel == FeatureLevel::FEATURE_LEVEL_0) { + if (material.featureLevel == FeatureLevel::FEATURE_LEVEL_0 && + (mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0 + || mTargetLanguage == TargetLanguage::SPIRV)) { // Insert compatibility definitions for ESSL 1.0 functions which were removed in ESSL 3.0. // This is the minimum required value according to the OpenGL ES Shading Language Version @@ -460,11 +491,14 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type, const char* materialTypeString = getOutputTypeName(materialOutputType); const char* typeString = getOutputTypeName(outputType); + bool generate_essl3_code = mTargetLanguage == TargetLanguage::SPIRV + || mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1; + out << "\n#define FRAG_OUTPUT" << index << " " << name.c_str(); - if (mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) { - out << "\n#define FRAG_OUTPUT_AT" << index << " gl_FragColor"; - } else { + if (generate_essl3_code) { out << "\n#define FRAG_OUTPUT_AT" << index << " output_" << name.c_str(); + } else { + out << "\n#define FRAG_OUTPUT_AT" << index << " gl_FragColor"; } out << "\n#define FRAG_OUTPUT_MATERIAL_TYPE" << index << " " << materialTypeString; out << "\n#define FRAG_OUTPUT_PRECISION" << index << " " << precisionString; @@ -472,7 +506,7 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type, out << "\n#define FRAG_OUTPUT_SWIZZLE" << index << " " << swizzleString; out << "\n"; - if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) { + if (generate_essl3_code) { out << "\nlayout(location=" << index << ") out " << precisionString << " " << typeString << " output_" << name.c_str() << ";\n"; } diff --git a/shaders/src/depth_main.fs b/shaders/src/depth_main.fs index 77608f3e15c4..e50d7620f171 100644 --- a/shaders/src/depth_main.fs +++ b/shaders/src/depth_main.fs @@ -57,7 +57,7 @@ void main() { fragColor.xy = computeDepthMomentsVSM(depth); fragColor.zw = computeDepthMomentsVSM(-1.0 / depth); // requires at least RGBA16F #elif defined(VARIANT_HAS_PICKING) -#if MATERIAL_FEATURE_LEVEL == 0 +#if FILAMENT_EFFECTIVE_VERSION == 100 outPicking.a = mod(float(object_uniforms_objectId / 65536), 256.0) / 255.0; outPicking.b = mod(float(object_uniforms_objectId / 256), 256.0) / 255.0; outPicking.g = mod(float(object_uniforms_objectId) , 256.0) / 255.0; diff --git a/third_party/spirv-cross/spirv_glsl.cpp b/third_party/spirv-cross/spirv_glsl.cpp index 0d63d35f8f2f..f57f702c620b 100644 --- a/third_party/spirv-cross/spirv_glsl.cpp +++ b/third_party/spirv-cross/spirv_glsl.cpp @@ -14977,7 +14977,11 @@ string CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const Bitset { auto &execution = get_entry_point(); - if (flags.get(DecorationRelaxedPrecision)) + if (type.basetype == SPIRType::UInt && is_legacy()) { + // HACK: This is a bool. See comment in type_to_glsl(). + qual += "lowp "; + } + else if (flags.get(DecorationRelaxedPrecision)) { bool implied_fmediump = type.basetype == SPIRType::Float && options.fragment.default_float_precision == Options::Mediump && @@ -15503,7 +15507,11 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) if (type.basetype == SPIRType::UInt && is_legacy()) { if (options.es) - SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy ESSL."); + // HACK: spirv-cross changes bools into uints and generates code which compares them to + // zero. Input code will have already been validated as not to have contained any uints, + // so any remaining uints must in fact be bools. However, simply returning "bool" here + // will result in invalid code. Instead, return an int. + return backend.basic_int_type; else require_extension_internal("GL_EXT_gpu_shader4"); }