FL0 materials are always compile via spirv-cross

This commit is contained in:
Mathias Agopian
2026-04-01 12:21:43 -07:00
committed by Mathias Agopian
parent 62ac2950c9
commit 8ec732dadc
13 changed files with 66 additions and 97 deletions

View File

@@ -34,8 +34,8 @@ vertex {
fragment {
void postProcess(inout PostProcessInputs postProcess) {
#if FILAMENT_EFFECTIVE_VERSION == 100
postProcess.color = texture2D(materialParams_color, variable_vertex.xy);
#if MATERIAL_FEATURE_LEVEL == 0
postProcess.color = texture(materialParams_color, variable_vertex.xy);
#else
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, materialParams.levelOfDetail);
#endif

View File

@@ -37,7 +37,7 @@ fragment {
sky = materialParams.color;
} else {
#if MATERIAL_FEATURE_LEVEL == 0
sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0);
sky = vec4(texture(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0);
#else
sky = vec4(textureLod(materialParams_skybox, variable_eyeDirection.xyz, 0.0).rgb, 1.0);
sky.rgb *= frameUniforms.iblLuminance;

View File

@@ -22,7 +22,7 @@ fragment {
prepareMaterial(material);
vec2 uv = getUV0();
uv.y = 1.0 - uv.y;
vec4 albedo = texture2D(materialParams_albedo, uv);
vec4 albedo = texture(materialParams_albedo, uv);
material.baseColor = getColor() * albedo;
material.baseColor.rgb *= material.baseColor.a;
}

View File

@@ -22,7 +22,7 @@ fragment {
prepareMaterial(material);
vec2 uv = getUV0();
uv.y = 1.0 - uv.y;
vec4 albedo = texture2D(materialParams_albedo, uv);
vec4 albedo = texture(materialParams_albedo, uv);
material.baseColor = getColor() * albedo;
material.baseColor.rgb *= material.baseColor.a;
}

View File

@@ -950,6 +950,38 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
if (found != std::string::npos) {
str.replace(found, clipDistanceDefinition.length(), "");
}
// Validate the transpiled ESSL1 shader dynamically before considering it successful.
// This proactively catches unsupported SPIR-V -> ESSL1 translation quirks (like textureLod)
// at compile-time since we can't easily test all variants on physical GLES 2.0 devices.
if (config.featureLevel == 0) {
// preampitively forbid spirv-cross from cheating and polyfilling disabled features
auto const& exts = glslCompiler.get_required_extensions();
for (auto const& ext : exts) {
if (ext != "GL_OES_standard_derivatives" &&
ext != "GL_OES_EGL_image_external" &&
ext != "GL_EXT_shader_framebuffer_fetch" &&
ext != "GL_EXT_shader_framebuffer_fetch_non_coherent") {
slog.e << "ERROR: Feature Level 0 shaders cannot require: " << ext << ". "
<< "spirv-cross attempted to unilaterally inject it." << io::endl;
return false;
}
}
TShader validateShader(internalConfig.shLang);
// The cleaner must be declared after the TShader to manage the glslang memory pool
// teardown order correctly and safely destroy the AST.
GLSLangCleaner const validateCleaner;
const char* shaderCString = str.c_str();
validateShader.setStrings(&shaderCString, 1);
bool const validateOk = validateShader.parse(&DefaultTBuiltInResource, glslOptions.version, false, EShMsgDefault);
if (!validateOk) {
slog.e << "ESSL1 Validation failed:\n" << validateShader.getInfoLog() << io::endl;
return false;
}
}
}
return true;
}

View File

@@ -131,7 +131,7 @@ void MaterialBuilderBase::prepare(bool const vulkanSemantics,
// OpenGL is a special case. If we're doing any optimization, then we need to go to Spir-V.
TargetLanguage glTargetLanguage = mOptimization > Optimization::PREPROCESSOR ?
TargetLanguage::SPIRV : TargetLanguage::GLSL;
if (vulkanSemantics) {
if (vulkanSemantics || featureLevel == backend::FeatureLevel::FEATURE_LEVEL_0) {
// Currently GLSLPostProcessor.cpp is incapable of compiling SPIRV to GLSL without
// running the optimizer. For now we just activate the optimizer in that case.
mOptimization = Optimization::PERFORMANCE;
@@ -167,7 +167,7 @@ void MaterialBuilderBase::prepare(bool const vulkanSemantics,
&& featureLevel == backend::FeatureLevel::FEATURE_LEVEL_0
&& shaderModel == ShaderModel::MOBILE) {
mCodeGenPermutations.push_back({
shaderModel,
ShaderModel::MOBILE,
TargetApi::OPENGL,
glTargetLanguage,
backend::FeatureLevel::FEATURE_LEVEL_0
@@ -938,6 +938,15 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
JobSystem::Job* parent = jobSystem.createJob();
for (const auto& v : variants) {
if (params.featureLevel == FeatureLevel::FEATURE_LEVEL_0) {
assert_invariant(params.shaderModel == ShaderModel::MOBILE);
assert_invariant(params.targetApi == TargetApi::OPENGL);
if (filament::Variant::isStereoVariant(v.variant)) {
// the stereo variant can never be used at feature level 0
continue;
}
}
JobSystem::Job* job = jobs::createJob(jobSystem, parent, [&]() {
if (cancelJobs.load()) {
return;
@@ -1319,8 +1328,7 @@ error:
CodeGenParams const semanticCodeGenParams = {
.shaderModel = ShaderModel::MOBILE,
.targetApi = TargetApi::OPENGL,
.targetLanguage = (info.featureLevel == FeatureLevel::FEATURE_LEVEL_0) ?
TargetLanguage::GLSL : TargetLanguage::SPIRV,
.targetLanguage = TargetLanguage::SPIRV,
.featureLevel = info.featureLevel,
};

View File

@@ -202,36 +202,6 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
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);
switch (material.stereoscopicType) {
case StereoscopicType::INSTANCED:
generateDefine(out, "FILAMENT_STEREO_INSTANCED", true);
@@ -253,17 +223,11 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
generateDefine(out, "MATERIAL_HAS_CUSTOM_DEPTH", material.userMaterialHasCustomDepth);
}
if (mTargetLanguage == TargetLanguage::SPIRV ||
mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) {
if (stage == ShaderStage::VERTEX) {
generateDefine(out, "VARYING", "out");
generateDefine(out, "ATTRIBUTE", "in");
} else if (stage == ShaderStage::FRAGMENT) {
generateDefine(out, "VARYING", "in");
}
} else {
generateDefine(out, "VARYING", "varying");
generateDefine(out, "ATTRIBUTE", "attribute");
if (stage == ShaderStage::VERTEX) {
generateDefine(out, "VARYING", "out");
generateDefine(out, "ATTRIBUTE", "in");
} else if (stage == ShaderStage::FRAGMENT) {
generateDefine(out, "VARYING", "in");
}
auto getShadingDefine = [](Shading shading) -> const char* {
@@ -361,31 +325,6 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
out << '\n';
out << SHADERS_COMMON_DEFINES_GLSL_DATA;
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
// 1.00 document. glslang forbids defining symbols beginning with gl_ as const, hence the
// #define.
generateDefine(out, "gl_MaxVaryingVectors", "8");
generateDefine(out, "texture2D", "texture");
generateDefine(out, "texture2DProj", "textureProj");
generateDefine(out, "texture3D", "texture");
generateDefine(out, "texture3DProj", "textureProj");
generateDefine(out, "textureCube", "texture");
if (stage == ShaderStage::VERTEX) {
generateDefine(out, "texture2DLod", "textureLod");
generateDefine(out, "texture2DProjLod", "textureProjLod");
generateDefine(out, "texture3DLod", "textureLod");
generateDefine(out, "texture3DProjLod", "textureProjLod");
generateDefine(out, "textureCubeLod", "textureLod");
}
}
// Api level enforcement.
generateDefine(out, "CLIENT_MATERIAL_API_LEVEL", apiLevel);
generateDefine(out, "UNSTABLE_MATERIAL_API_LEVEL", filament::UNSTABLE_MATERIAL_API_LEVEL);

View File

@@ -689,6 +689,8 @@ std::string ShaderGenerator::createSurfaceComputeProgram(ShaderModel const shade
generateUserSpecConstants(cg, s, mConstants);
CodeGenerator::generateDefine(s, "MATERIAL_FEATURE_LEVEL", uint32_t(featureLevel));
CodeGenerator::generateSurfaceTypes(s, ShaderStage::COMPUTE);
cg.generateUniforms(s, ShaderStage::COMPUTE,

View File

@@ -923,7 +923,7 @@ TEST_F(MaterialCompiler, FeatureLevel0Sampler2D) {
std::string shaderCode(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = texture2D(materialParams_sampler, vec2(0.0, 0.0));
material.baseColor = texture(materialParams_sampler, vec2(0.0, 0.0));
}
)");
filamat::MaterialBuilder builder;
@@ -941,7 +941,7 @@ TEST_F(MaterialCompiler, SamplerTransformName) {
void material(inout MaterialInputs material) {
prepareMaterial(material);
vec3 uvw = materialParams.sampler_transform * vec3(0.0, 0.0, 0.0);
material.baseColor = texture2D(materialParams_sampler, uvw.xy);
material.baseColor = texture(materialParams_sampler, uvw.xy);
}
)");
filamat::MaterialBuilder builder;
@@ -960,7 +960,7 @@ TEST_F(MaterialCompiler, SamplerMissingTransformName) {
void material(inout MaterialInputs material) {
prepareMaterial(material);
vec3 uvw = materialParams.sampler_transform * vec3(0.0, 0.0, 0.0);
material.baseColor = texture2D(materialParams_sampler, uvw.xy);
material.baseColor = texture(materialParams_sampler, uvw.xy);
}
)");
filamat::MaterialBuilder builder;

View File

@@ -46,7 +46,7 @@ highp mat4 getViewFromClipMatrix() {
return frameUniforms.viewFromClipMatrix;
}
#if FILAMENT_EFFECTIVE_VERSION > 100
#if MATERIAL_FEATURE_LEVEL > 0
/** @public-api */
highp mat4 getEyeFromViewMatrix() {
return frameUniforms.eyeFromViewMatrix[getEyeIndex()];

View File

@@ -1,14 +1,10 @@
#if defined(VARIANT_HAS_VSM)
layout(location = 0) out highp vec4 fragColor;
#elif defined(VARIANT_HAS_PICKING)
# if __VERSION__ == 100
highp vec4 outPicking;
# else
# if MATERIAL_FEATURE_LEVEL == 0
# if MATERIAL_FEATURE_LEVEL == 0
layout(location = 0) out highp vec4 outPicking;
# else
# else
layout(location = 0) out highp uvec2 outPicking;
# endif
# endif
#else
// not color output
@@ -55,7 +51,7 @@ void main() {
highp float depth = vertex_worldPosition.w;
fragColor = computeDepthMomentsVSM(depth);
#elif defined(VARIANT_HAS_PICKING)
#if FILAMENT_EFFECTIVE_VERSION == 100
#if MATERIAL_FEATURE_LEVEL == 0
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;
@@ -64,9 +60,7 @@ void main() {
outPicking.x = uint(object_uniforms_objectId);
outPicking.y = floatBitsToUint(vertex_position.z / vertex_position.w);
#endif
#if __VERSION__ == 100
gl_FragData[0] = outPicking;
#endif
#else
// that's it
#endif

View File

@@ -23,7 +23,7 @@ float getObjectUserData() {
// Attributes access
//------------------------------------------------------------------------------
#if __VERSION__ >= 300
#if MATERIAL_FEATURE_LEVEL > 0
/** @public-api */
int getVertexIndex() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)

View File

@@ -1,8 +1,4 @@
#if __VERSION__ == 100
vec4 fragColor;
#else
layout(location = 0) out vec4 fragColor;
#endif
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
void blendPostLightingColor(const MaterialInputs material, inout vec4 color) {
@@ -119,7 +115,5 @@ void main() {
}
#endif
#if __VERSION__ == 100
gl_FragData[0] = fragColor;
#endif
}