Skip to content

Commit

Permalink
Enable optimizations for ESSL 1.0 code
Browse files Browse the repository at this point in the history
The CL introducing the ESSL 1.0 chunk in materials inadvertently disabled
optimizations for said code. This commit reintroduces those optimizations and
fixes associated bugs which manifested. In particular, spirv-cross was
generating uints for bools; this has been fixed with a hack. Additionally,
spirv-cross is now compiled with exceptions enabled so that matc can gracefully
fail and show the code which failed to compile rather than abruptly aborting.
  • Loading branch information
elizagamedev committed Nov 13, 2023
1 parent 485ae1b commit b370128
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 22 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,7 @@ add_subdirectory(${EXTERNAL}/getopt)
if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
# spirv-tools must come before filamat, as filamat relies on the presence of the
# spirv-tools_SOURCE_DIR variable.
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF)
add_subdirectory(${EXTERNAL}/spirv-tools)
add_subdirectory(${EXTERNAL}/glslang/tnt)
add_subdirectory(${EXTERNAL}/spirv-cross/tnt)
Expand Down
1 change: 1 addition & 0 deletions NEW_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).

- engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See
`Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**]
- matc: Support optimizations for ESSL 1.0 code.
2 changes: 1 addition & 1 deletion filament/src/materials/blitLow.mat
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ vertex {

fragment {
void postProcess(inout PostProcessInputs postProcess) {
#if __VERSION__ == 100
#if EFFECTIVE_VERSION == 100
postProcess.color = texture2D(materialParams_color, variable_vertex.xy);
#else
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0);
Expand Down
15 changes: 12 additions & 3 deletions libs/filamat/src/GLSLPostProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "MetalArgumentBuffer.h"
#include "SpirvFixup.h"
#include "utils/ostream.h"

#include <filament/MaterialEnums.h>

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -546,7 +549,12 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader,
}
}

*internalConfig.glslOutput = glslCompiler.compile();
try {
*internalConfig.glslOutput = glslCompiler.compile();
} catch (spirv_cross::CompilerError e) {
slog.e << "ERROR: " << e.what() << io::endl;
return false;
}

// spirv-cross automatically redeclares gl_ClipDistance if it's used. Some drivers don't
// like this, so we simply remove it.
Expand All @@ -559,6 +567,7 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader,
str.replace(found, clipDistanceDefinition.length(), "");
}
}
return true;
}

std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
Expand Down
2 changes: 1 addition & 1 deletion libs/filamat/src/GLSLPostProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 4 additions & 5 deletions libs/filamat/src/MaterialBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
}
Expand Down Expand Up @@ -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,
Expand Down
46 changes: 40 additions & 6 deletions libs/filamat/src/shaders/CodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 an 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, "EFFECTIVE_VERSION", effective_version);

if (stage == ShaderStage::VERTEX) {
CodeGenerator::generateDefine(out, "FLIP_UV_ATTRIBUTE", material.flipUV);
CodeGenerator::generateDefine(out, "LEGACY_MORPHING", material.useLegacyMorphing);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -460,19 +491,22 @@ 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;
out << "\n#define FRAG_OUTPUT_TYPE" << index << " " << typeString;
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";
}
Expand Down
8 changes: 4 additions & 4 deletions shaders/src/depth_main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ 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
outPicking.a = float((object_uniforms_objectId / 65536) % 256) / 255.0;
outPicking.b = float((object_uniforms_objectId / 256) % 256) / 255.0;
outPicking.g = float( object_uniforms_objectId % 256) / 255.0;
#if 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;
outPicking.r = vertex_position.z / vertex_position.w;
#else
outPicking.x = intBitsToFloat(object_uniforms_objectId);
Expand Down
12 changes: 10 additions & 2 deletions third_party/spirv-cross/spirv_glsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down Expand Up @@ -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");
}
Expand Down

0 comments on commit b370128

Please sign in to comment.