Compare commits

...

1 Commits

Author SHA1 Message Date
Eliza Velasquez
dc74aa4c0d matc: treat DYN DIR and SRE as spec constants
This commit does not exhaustively replace these variants with spec constants.
Rather, this is a necessary first step into full replacement.

As was the case before this commit, all combinations of variants are still
generated as their own individual sources. However, matc has been changed in the
way that it generates these variants, such that it will be trivial to later
remove them.

Firstly, instead of matc selectively generating specific uniform buffers and
structs specific to these variants per-specialization, generates them *always*
as long as the specialization "has lighting". Specifically, a specialization
"has lighting" if all of the following conditions are true:

- It's a lit material, or it's an unlit material with a shadow multiplier.
- It's not an SSR variant.

As a result, there is some redundant, potentially unused code generated in all
variants.

Additionally, all shader code has been refactored to replace `#if VARIANT_X`
preprocessor directives with `if (MATERIAL_HAS_X)` statements, where
`MATERIAL_HAS_X` is a spec constant. The *only difference* the code generated
between a specialization with DYN, DIR, and SRE turned on or off is the *default
value* of this spec constant.

Technically, assuming that all shader code optimizations are deterministic, we
should see some reduction of material file sizes, since only one line of code
would differ between these variants being turned on and left off. However, we
may add to some of that size given the redundant information added to all other
variants.

I tested this experimentally by compiling gltf_viewer at this commit and the
commit before it in release mode and totalling the size of all of the filamat
files generated.

- Parent commit: 44.44 MiB
- This commit: 36.67 MiB

So we have a reduction of about 8 MiB, which is around 20% of the original file
size.

(Note that I took these measurements a long time ago and they might not be still
accurate...)
2026-03-05 16:06:58 +09:00
15 changed files with 126 additions and 101 deletions

View File

@@ -85,6 +85,9 @@ enum class ReservedSpecializationConstants : uint8_t {
CONFIG_SH_BANDS_COUNT = 9,
CONFIG_SHADOW_SAMPLING_METHOD = 10,
CONFIG_FROXEL_RECORD_BUFFER_HEIGHT = 11,
CONFIG_HAS_DIRECTIONAL_LIGHTING = 12,
CONFIG_HAS_DYNAMIC_LIGHTING = 13,
CONFIG_HAS_SHADOWING = 14,
// check CONFIG_NEXT_RESERVED_SPEC_CONSTANT and CONFIG_MAX_RESERVED_SPEC_CONSTANTS below
};
@@ -107,7 +110,7 @@ constexpr size_t CONFIG_MAX_LIGHT_INDEX = CONFIG_MAX_LIGHT_COUNT - 1;
// Updating this value necessitates a material version bump.
constexpr size_t CONFIG_MAX_RESERVED_SPEC_CONSTANTS = 16;
// The number of the next unassigned reserved spec constant.
constexpr size_t CONFIG_NEXT_RESERVED_SPEC_CONSTANT = 12;
constexpr size_t CONFIG_NEXT_RESERVED_SPEC_CONSTANT = 15;
// The maximum number of shadow maps possible.
// There is currently a maximum limit of 128 shadow maps.

View File

@@ -358,6 +358,21 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
+ReservedSpecializationConstants::CONFIG_SRGB_SWAPCHAIN_EMULATION, false);
}
// Generate specialization constants for "minor variants".
//
// HACK: Setting the default values like this is a temporary measure for switching variants to
// spec constants.
bool const litVariants = material.isLit || material.hasShadowMultiplier;
generateSpecializationConstant(out, "CONFIG_HAS_DIRECTIONAL_LIGHTING",
+ReservedSpecializationConstants::CONFIG_HAS_DIRECTIONAL_LIGHTING,
litVariants && v.hasDirectionalLighting());
generateSpecializationConstant(out, "CONFIG_HAS_DYNAMIC_LIGHTING",
+ReservedSpecializationConstants::CONFIG_HAS_DYNAMIC_LIGHTING,
litVariants && v.hasDynamicLighting());
generateSpecializationConstant(out, "CONFIG_HAS_SHADOWING",
+ReservedSpecializationConstants::CONFIG_HAS_SHADOWING,
litVariants && filament::Variant::isShadowReceiverVariant(v));
out << '\n';
out << SHADERS_COMMON_DEFINES_GLSL_DATA;
@@ -1160,10 +1175,9 @@ io::sstream& CodeGenerator::generateSurfaceLit(io::sstream& out, ShaderStage sta
filament::Variant variant, Shading shading, bool customSurfaceShading) {
if (stage == ShaderStage::FRAGMENT) {
out << SHADERS_SURFACE_LIGHTING_FS_DATA;
if (filament::Variant::isShadowReceiverVariant(variant)) {
if (!filament::Variant::isSSRVariant(variant)) {
out << SHADERS_SURFACE_SHADOWING_FS_DATA;
}
// the only reason we have this assert here is that we used to have a check,
// which seemed unnecessary.
assert_invariant(shading != Shading::UNLIT);
@@ -1191,14 +1205,8 @@ io::sstream& CodeGenerator::generateSurfaceLit(io::sstream& out, ShaderStage sta
out << SHADERS_SURFACE_AMBIENT_OCCLUSION_FS_DATA;
out << SHADERS_SURFACE_LIGHT_INDIRECT_FS_DATA;
if (variant.hasDirectionalLighting()) {
out << SHADERS_SURFACE_LIGHT_DIRECTIONAL_FS_DATA;
}
if (variant.hasDynamicLighting()) {
out << SHADERS_SURFACE_LIGHT_PUNCTUAL_FS_DATA;
}
out << SHADERS_SURFACE_LIGHT_DIRECTIONAL_FS_DATA;
out << SHADERS_SURFACE_LIGHT_PUNCTUAL_FS_DATA;
out << SHADERS_SURFACE_SHADING_LIT_FS_DATA;
}
return out;
@@ -1207,10 +1215,8 @@ io::sstream& CodeGenerator::generateSurfaceLit(io::sstream& out, ShaderStage sta
io::sstream& CodeGenerator::generateSurfaceUnlit(io::sstream& out, ShaderStage stage,
filament::Variant variant, bool hasShadowMultiplier) {
if (stage == ShaderStage::FRAGMENT) {
if (hasShadowMultiplier) {
if (filament::Variant::isShadowReceiverVariant(variant)) {
out << SHADERS_SURFACE_SHADOWING_FS_DATA;
}
if (hasShadowMultiplier && !filament::Variant::isSSRVariant(variant)) {
out << SHADERS_SURFACE_SHADOWING_FS_DATA;
}
out << SHADERS_SURFACE_SHADING_UNLIT_FS_DATA;
}

View File

@@ -51,14 +51,6 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(io::sstream& out,
ShaderStage const stage, MaterialBuilder::FeatureLevel featureLevel,
MaterialInfo const& material, filament::Variant const variant) noexcept {
bool const litVariants = material.isLit || material.hasShadowMultiplier;
CodeGenerator::generateDefine(out, "VARIANT_HAS_DIRECTIONAL_LIGHTING",
litVariants && variant.hasDirectionalLighting());
CodeGenerator::generateDefine(out, "VARIANT_HAS_DYNAMIC_LIGHTING",
litVariants && variant.hasDynamicLighting());
CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING",
litVariants && filament::Variant::isShadowReceiverVariant(variant));
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
filament::Variant::isShadowSampler2DVariant(variant) ||
filament::Variant::isDepthMomentsVariant(variant));
@@ -90,6 +82,8 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(io::sstream& out,
CodeGenerator::generateDefine(out, "MATERIAL_HAS_SHADOW_MULTIPLIER",
material.hasShadowMultiplier);
CodeGenerator::generateDefine(out, "MATERIAL_HAS_LIGHTING", hasLighting(material, variant));
CodeGenerator::generateDefine(out, "MATERIAL_HAS_INSTANCES", material.instanced);
CodeGenerator::generateDefine(out, "MATERIAL_HAS_VERTEX_DOMAIN_DEVICE_JITTERED",
@@ -465,8 +459,7 @@ std::string ShaderGenerator::createSurfaceVertexProgram(ShaderModel const shader
+PerRenderableBindingPoints::OBJECT_UNIFORMS,
UibGenerator::getPerRenderableUib());
const bool litVariants = material.isLit || material.hasShadowMultiplier;
if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) {
if (hasLighting(material, variant)) {
cg.generateUniforms(vs, ShaderStage::FRAGMENT,
DescriptorSetBindingPoints::PER_VIEW,
+PerViewBindingPoints::SHADOWS,
@@ -573,22 +566,17 @@ std::string ShaderGenerator::createSurfaceFragmentProgram(ShaderModel const shad
+PerRenderableBindingPoints::OBJECT_UNIFORMS,
UibGenerator::getPerRenderableUib());
if (variant.hasDynamicLighting()) {
if (hasLighting(material, variant)) {
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
DescriptorSetBindingPoints::PER_VIEW,
+PerViewBindingPoints::LIGHTS,
UibGenerator::getLightsUib());
}
bool const litVariants = material.isLit || material.hasShadowMultiplier;
if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) {
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
DescriptorSetBindingPoints::PER_VIEW,
+PerViewBindingPoints::SHADOWS,
UibGenerator::getShadowUib());
}
if (variant.hasDynamicLighting()) {
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
DescriptorSetBindingPoints::PER_VIEW,
+PerViewBindingPoints::RECORD_BUFFER,
@@ -837,4 +825,10 @@ bool ShaderGenerator::hasStereo(
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
}
bool ShaderGenerator::hasLighting(
MaterialInfo const& material, filament::Variant const variant) noexcept {
return (material.isLit || material.hasShadowMultiplier)
&& !filament::Variant::isSSRVariant(variant);
}
} // namespace filament

View File

@@ -130,6 +130,8 @@ private:
filament::Variant variant,
MaterialBuilder::FeatureLevel featureLevel) noexcept;
static bool hasLighting(MaterialInfo const& material, filament::Variant variant) noexcept;
MaterialBuilder::PropertyList mProperties;
MaterialBuilder::VariableList mVariables;
MaterialBuilder::OutputList mOutputs;

View File

@@ -106,7 +106,8 @@ highp vec3 getNormalizedViewportCoord() {
return vec3(logicalUv, gl_FragCoord.z);
}
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DYNAMIC_LIGHTING)
#if defined(MATERIAL_HAS_LIGHTING)
/* Depends on DYN | SRE */
highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLight) {
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
@@ -124,10 +125,12 @@ bool isDoubleSided() {
}
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
#if defined(MATERIAL_HAS_LIGHTING)
/**
* Returns the cascade index for this fragment (between 0 and CONFIG_MAX_SHADOW_CASCADES - 1).
*
* Depends on DIR | SRE
*/
int getShadowCascade() {
highp float z = mulMat4x4Float3(getViewFromWorldMatrix(), getWorldPosition()).z;
@@ -136,6 +139,7 @@ int getShadowCascade() {
return clamp(greaterZ.x + greaterZ.y + greaterZ.z + greaterZ.w, 0, cascadeCount - 1);
}
/* Depends on DIR | SRE */
highp vec4 getCascadeLightSpacePosition(int cascade) {
// For the first cascade, return the interpolated light space position.
// This branch will be coherent (mostly) for neighboring fragments, and it's worth avoiding
@@ -152,4 +156,3 @@ highp vec4 getCascadeLightSpacePosition(int cascade) {
}
#endif

View File

@@ -48,8 +48,8 @@ void evaluateDirectionalLight(const MaterialInputs material,
#endif
float visibility = 1.0;
#if defined(VARIANT_HAS_SHADOWING)
if (light.NoL > 0.0) {
#if defined(MATERIAL_HAS_LIGHTING)
if (CONFIG_HAS_SHADOWING && light.NoL > 0.0) {
float ssContactShadowOcclusion = 0.0;
int cascade = getShadowCascade();

View File

@@ -154,17 +154,19 @@ Light getLight(const uint lightIndex) {
light.worldPosition = positionFalloff.xyz;
light.channels = int(channels);
light.contactShadows = bool(typeShadow & 0x10u);
#if defined(VARIANT_HAS_DYNAMIC_LIGHTING)
light.lightType = (typeShadow & 0x1u);
#if defined(VARIANT_HAS_SHADOWING)
light.shadowIndex = int((typeShadow >> 8u) & 0xFFu);
light.castsShadows = bool(channels & 0x10000u);
if (light.lightType == LIGHT_TYPE_SPOT) {
light.zLight = dot(shadowUniforms.shadows[light.shadowIndex].lightFromWorldZ, vec4(worldPosition, 1.0));
}
#endif
if (light.lightType == LIGHT_TYPE_SPOT) {
light.attenuation *= getAngleAttenuation(-direction, light.l, scaleOffset);
#if defined(MATERIAL_HAS_LIGHTING)
if (CONFIG_HAS_DYNAMIC_LIGHTING) {
light.lightType = (typeShadow & 0x1u);
if (CONFIG_HAS_SHADOWING) {
light.shadowIndex = int((typeShadow >> 8u) & 0xFFu);
light.castsShadows = bool(channels & 0x10000u);
if (light.lightType == LIGHT_TYPE_SPOT) {
light.zLight = dot(shadowUniforms.shadows[light.shadowIndex].lightFromWorldZ, vec4(worldPosition, 1.0));
}
}
if (light.lightType == LIGHT_TYPE_SPOT) {
light.attenuation *= getAngleAttenuation(-direction, light.l, scaleOffset);
}
}
#endif
return light;
@@ -207,8 +209,8 @@ void evaluatePunctualLights(const MaterialInputs material,
#endif
float visibility = 1.0;
#if defined(VARIANT_HAS_SHADOWING)
if (light.NoL > 0.0) {
#if defined(MATERIAL_HAS_LIGHTING)
if (CONFIG_HAS_SHADOWING && light.NoL > 0.0) {
if (light.castsShadows) {
int shadowIndex = light.shadowIndex;
if (light.lightType == LIGHT_TYPE_POINT) {

View File

@@ -92,8 +92,10 @@ void main() {
fragColor.rgb = fragColor.rgb * (1.0 - fogColor.a) + fogColor.rgb;
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
if (CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP) {
#if defined(MATERIAL_HAS_LIGHTING)
if (CONFIG_HAS_SHADOWING
&& CONFIG_HAS_DIRECTIONAL_LIGHTING
&& CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP) {
float a = fragColor.a;
highp vec4 p = getShadowPosition(getShadowCascade());
p.xy = p.xy * (1.0 / p.w);

View File

@@ -162,12 +162,14 @@ void main() {
vertex_worldNormal = material.worldNormal;
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
vertex_lightSpacePosition = computeLightSpacePosition(
vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection,
shadowUniforms.shadows[0].normalBias,
shadowUniforms.shadows[0].lightFromWorldMatrix);
#if defined(MATERIAL_HAS_LIGHTING)
if (CONFIG_HAS_SHADOWING && CONFIG_HAS_DIRECTIONAL_LIGHTING) {
vertex_lightSpacePosition = computeLightSpacePosition(
vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection,
shadowUniforms.shadows[0].normalBias,
shadowUniforms.shadows[0].lightFromWorldMatrix);
}
#endif
#endif // !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)

View File

@@ -289,12 +289,14 @@ vec4 evaluateLights(const MaterialInputs material) {
// it also saves 1 shader variant
evaluateIBL(material, pixel, color);
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
evaluateDirectionalLight(material, pixel, color);
#endif
#if defined(MATERIAL_HAS_LIGHTING)
if (CONFIG_HAS_DIRECTIONAL_LIGHTING) {
evaluateDirectionalLight(material, pixel, color);
}
#if defined(VARIANT_HAS_DYNAMIC_LIGHTING)
evaluatePunctualLights(material, pixel, color);
if (CONFIG_HAS_DYNAMIC_LIGHTING) {
evaluatePunctualLights(material, pixel, color);
}
#endif
#if defined(BLEND_MODE_FADE) && !defined(SHADING_MODEL_UNLIT)

View File

@@ -38,39 +38,43 @@ vec4 fixupAlpha(vec4 color) {
vec4 evaluateMaterial(const MaterialInputs material) {
vec4 color = material.baseColor;
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
#if defined(VARIANT_HAS_SHADOWING)
float visibility = 1.0;
int cascade = getShadowCascade();
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1 << cascade) << 8));
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1);
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
highp vec4 shadowPosition = getShadowPosition(cascade);
visibility = shadow(true, sampler0_shadowMap, cascade, shadowPosition, 0.0);
#if defined(MATERIAL_HAS_LIGHTING)
if (CONFIG_HAS_DIRECTIONAL_LIGHTING) {
if (CONFIG_HAS_SHADOWING) {
float visibility = 1.0;
int cascade = getShadowCascade();
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1 << cascade) << 8));
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1);
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
highp vec4 shadowPosition = getShadowPosition(cascade);
visibility = shadow(true, sampler0_shadowMap, cascade, shadowPosition, 0.0);
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
applyShadowStrength(visibility, material.shadowStrength);
#endif
#if defined (FILAMENT_SHADOW_FAR_ATTENUATION)
// shadow far attenuation
highp vec3 v = getWorldPosition() - getWorldCameraPosition();
// (viewFromWorld * v).z == dot(transpose(viewFromWorld), v)
highp float z = dot(transpose(getViewFromWorldMatrix())[2].xyz, v);
highp vec2 p = frameUniforms.shadowFarAttenuationParams;
visibility = 1.0 - ((1.0 - visibility) * saturate(p.x - z * z * p.y));
#endif
}
if ((frameUniforms.directionalShadows & 0x2) != 0 && visibility > 0.0) {
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0) {
visibility *= (1.0 - screenSpaceContactShadow(frameUniforms.lightDirection));
}
}
color *= 1.0 - visibility;
#else
color = vec4(0.0);
#endif
#elif defined(MATERIAL_HAS_SHADOW_MULTIPLIER)
color = vec4(0.0);
#endif
applyShadowStrength(visibility, material.shadowStrength);
#endif // MATERIAL_HAS_SHADOW_STRENGTH
#if defined(FILAMENT_SHADOW_FAR_ATTENUATION)
// shadow far attenuation
highp vec3 v = getWorldPosition() - getWorldCameraPosition();
// (viewFromWorld * v).z == dot(transpose(viewFromWorld), v)
highp float z = dot(transpose(getViewFromWorldMatrix())[2].xyz, v);
highp vec2 p = frameUniforms.shadowFarAttenuationParams;
visibility = 1.0 - ((1.0 - visibility) * saturate(p.x - z * z * p.y));
#endif // FILAMENT_SHADOW_FAR_ATTENUATION
}
if ((frameUniforms.directionalShadows & 0x2) != 0 && visibility > 0.0) {
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0) {
visibility *= (1.0 - screenSpaceContactShadow(frameUniforms.lightDirection));
}
}
color *= 1.0 - visibility;
} else {
color = vec4(0.0);
} // CONFIG_HAS_SHADOWING
} else {
#if defined(MATERIAL_HAS_SHADOW_MULTIPLIER)
color = vec4(0.0);
#endif // MATERIAL_HAS_SHADOW_MULTIPLIER
} // CONFIG_HAS_DIRECTIONAL_LIGHTING
#endif // MATERIAL_HAS_LIGHTING
addEmissive(material, color);

View File

@@ -504,14 +504,14 @@ float screenSpaceContactShadow(vec3 lightDirection) {
* the light intensity.
*/
#if defined(MATERIAL_HAS_LIGHTING)
// get texture coordinate for directional and spot shadow maps
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
// Depends on DIR
highp vec4 getShadowPosition(const int cascade) {
return getCascadeLightSpacePosition(cascade);
}
#endif
#if defined(VARIANT_HAS_DYNAMIC_LIGHTING)
// Depends on DYN
highp vec4 getShadowPosition(const int index, const highp vec3 dir, const highp float zLight) {
return getSpotLightSpacePosition(index, dir, zLight);
}

View File

@@ -2,13 +2,15 @@
// Shadowing
//------------------------------------------------------------------------------
#if defined(VARIANT_HAS_SHADOWING)
#if defined(MATERIAL_HAS_LIGHTING)
/**
* Computes the light space position of the specified world space point.
* The returned point may contain a bias to attempt to eliminate common
* shadowing artifacts such as "acne". To achieve this, the world space
* normal at the point must also be passed to this function.
* Normal bias is not used for VSM.
*
* Depends on SRE
*/
highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
@@ -62,4 +64,4 @@ highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
return mulMat4x4Float3(lightFromWorldMatrix, p);
}
#endif // VARIANT_HAS_SHADOWING
#endif // MATERIAL_HAS_LIGHTING

View File

@@ -1,6 +1,8 @@
#if defined(VARIANT_HAS_SHADOWING)
#if defined(MATERIAL_HAS_LIGHTING)
// Adreno drivers seem to ignore precision qualifiers in structs, unless they're used in
// UBOs, which is the case here.
//
// Depends on SRE
struct ShadowData {
highp mat4 lightFromWorldMatrix;
highp vec4 lightFromWorldZ;

View File

@@ -28,7 +28,8 @@ LAYOUT_LOCATION(10) flat VARYING highp int instance_index;
highp int logical_instance_index;
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
#if defined(MATERIAL_HAS_LIGHTING)
/* Depends on SRE | DIR */
LAYOUT_LOCATION(11) VARYING highp vec4 vertex_lightSpacePosition;
#endif