* Optimize Shader LineDictionary with Variable-Length 3-Streams
This commit completely reorganizes the string dictionary compression
pipeline used by compiled Material Text Chunks and improves matinfo
dictionary output.
1. Multi-Base Variable-Length Scaling:
We replaced the static 16-bit indices overhead with a
bounded payload. Now, indices scale dynamically:
- 0 to 239 evaluate in 1 byte.
- 240 to 3584 leverage 0xF0-0xFD escapes evaluating in 2 bytes.
- 3584+ are locked behind a 0xFF marker to 3 bytes.
This natively eradicated the massive monolithic lengths and zero-padding
issues previously dominating shader packages.
2. Variable-Length 3-Stream Decoding:
To solve the Zstandard/Zlib entropy fragmentation that conventionally
plagues interleaved variable byte lengths (which previously inflated
our `filament.aar` boundary constraint by +2KB), we segregated the encoded
payloads.
By grouping high-entropy string boundaries into a `Base Stream`
and isolating offset digits inside an `Extension Stream`,
predictive LZ77 ZIP sliding-windows perfectly map
over both arrays independently without disruption.
3. Optimize Numeric Stream using LEB128
Prior to this change, numerical suffixes split from shader
variables (e.g., `param_1024` -> `param_` + `1024`) were fed back
into the localized String Dictionary. Because high-frequency numbers were
assigned disjointed localized IDs per shader variant, LZ77
failed to cross-reference their repetitive structures across
shipped `.aar` archives, fracturing compression sequences.
This patch implements a unified 3-Stream topology. It
extracts numerical primitives (< 32768) away from the baseline
String Dictionary, writing them into an isolated, contiguous
LEB128 array.
By using a dedicated `[254]` Escape Token within the primary stream, numerical
variables maintain exact 1-byte (`< 128`) or 2-byte (`>= 128`)
geometric layouts across all permutations.
The resulting deterministic alignment guarantees that Zlib sliding windows
can deduplicate highly repetitive variables across the
entire application binary block.
4. We use the ShaderStage information to create distinct index ranges, which
further help use 1-byte indices.
Verification Metrics:
`filament-android.aar`: -7,938 B
`gltfio-android.aar`: -290,939 B
`libfilament.a`: -18,464 B
* Optimize shader dictionary by decoding '_' for numeric literals
Most numbers extracted from the shader text are preceded by an
underscore (e.g., from `_`, `hp_copy_`), which previously caused
standalone `_` strings to heavily pollute the LineDictionary.
This change removes the standalone `_` from the dictionary index:
- `MaterialChunk` rehydrates the `_` prefix when decoding these numeric
literals.
This frees up dictionary indices, yielding massive byte savings across
uncompressed binaries (e.g., -28.4 KB for volume_masked.filamat).
* Optimize ShaderMinifier to strip explicit spacing
Spirv-cross outputs GLSL with explicit spacing around generic
operators (e.g., ` = `, `, `, ` ) * `). This padding consumes a
significant amount of uncompressed bytes across large ubershaders.
By applying targeted string replacements at the end of the `ShaderMinifier`
pass, we strip this extraneous padding down to its raw tokens
(e.g., `a=b`, `a,b`, `a*b`).
This optimization preserves isolating spaces where valuable, ensuring
line-dictionary tokens (such as raw `=` or `,`) remain deduplicated
instead of fusing into unpredictable variables.
Impact:
This saves roughly ~9.1 KB in `libfilament.a` and ~3.2 KB in
`volume_masked.filamat` uncompressed, with proportional gains across
the downstream LZ4 compressed archives.
- Introduce `shiftRadius` to allow positional tolerances by searching
a local neighborhood, absorbing sub-pixel shifts and MSAA quirks.
- Introduce `blurRadius` to apply local area averaging, ignoring
high-frequency noise like hardware dithering.
- Enhance `ImageDiffResult` to include an `averageError` array and
a 10-bin `errorHistogram` for actionable failure debugging.
- Update Android JNI bindings (`ImageDiff.java` and `ImageDiff.cpp`)
to propagate the new error distribution statistics to Java callers.
- Update C++ unit tests to cover the new heuristic options.
- Document the new parameters and JSON result format in README.md.
- Add synthetic image generation tests in `tools/diffimg/tests/` to
validate the CLI tool's handling of spatial shifts and dithering.
On certain linux, macOS environment, there is already a system
getopt. This often creates conflict when compiling filament.
Here we alias utils::getopt to either the system getopt (if
present) or third_party/getopt.
Fixes#7551
This fixes#9701 by replacing shell execution (`shell=True` and
`os.system`) with direct subprocess calls using argument lists in
`tools/zbloat/zbloat.py`
Previously, the script used f-strings to pass paths directly into a
shell command, which created an unnecessary risk: if an external archive
contained files with shell metacharacters, it could lead to accidental
or malicious command execution during analysis.
By passing arguments as lists, the subprocess module maps them directly
to the executable, bypassing the system shell and eliminating the
vulnerability.
This tool uses existing libraries: image, imageio, imageio-lite,
imagediff to perform difference comparison for on-disk images.
We refactor renderdiff to use this tool instead of using
python dependencies.
Co-authored-by: Ben Doherty <bendoherty@google.com>
- Add documentation for specgen along with proper math rendering
- Adjust the heading size, capitalization of various READMEs.
- Add backend test README to the doc
- Rename the CI related tests to have prefix "CI:"
- spectral reconstructions with 4 samples
- unroll the whole dispersion computation to improve performance
(batch texture fetches and reuse common values)
- tool to generate the matrices for spectral integration
* Move the include resolution functionality to matp
- matp::MaterialParser now has a function resolveIncludes that returns a pair of status and resolve string.
- all the include resolution classes are moved to matp private src.
- matp::resolveIncludes is renamed to matp::resolveIncludesRecursively and only used internally.
- added insertLineDirectives and insertLineDirectiveChecks in Config; add those in the CommandlineConfig.
- moved output format to public use.
Note: MaterialParser::resolveIncludes could take a includer instead of the materialFilePath, but i decided
to go with the materialFilePath because the most common use case is resolving from the file's directory.
This allows the parser to just create the default DirIncluder internally, and we don't need to expose it publicly.
* add const to the buffer param
* Move resolving #includes from MaterialBuilder to MaterialCompiler, before parsing the material.
- resolving #includes was happening after parsing, now moving before parsing.
this is because we could offload this resolution at build time for RuntimeMaterialCompiler
- filamat::resolveIncludes used to have an assumption where the given text was already
a shader block. this assumption is now broken so it finds the line offset internally.
thus the line offset field is removed from IncludeResult.
* Move include related classes to matc
* Add missing includes.
* Add parenthesis around operators to suppress compiler warning.
* Remove duplicate definition of is_supported_aux_t.
* Explicitly create descriptions.
* Remove usage of anonymous struct with non-trivially constructible members.
This is an error on some compilers (e.g. gcc).
* Remove unnecessary rvalue-reference on pointer type.
* Explicitly construct SamplerParams to suppress compiler warnings.
* Place attribute specifier before declaration.
* Remove usage of anonymous struct with a non-trivially constructible member.
Replace the `array` union member with an `operator[]` to provide similar
functionality.
Some compilers (e.g. gcc) do not support this non-standard use-case.
* Use same warning settings as main filament project.
* Use utils::Status in MaterialParser
* Use utils::sstream instead of std::stringstream
* Remove remaining std::cerr and dep; update MaterialParser::reflectParameters
* make error message in utils::Status more generic
---------
Co-authored-by: Powei Feng <powei@google.com>
In the current implementation, the function std::cout is used inside a signal handler. This is problematic because std::cout is not
async-signal-safe. According to POSIX standards, only a small set of
functions are guaranteed to be safe when called from signal handlers,
and std::cout is not one of them. Using non-async-signal-safe functions
inside signal handlers leads to undefined behavior and can cause
crashes, deadlocks, or other unpredictable issues.
By making these changes, we avoid undefined behavior and ensure the
program can handle signals safely.
The workaround option is now set to none by default. Use
```
matc -Wall
```
if you want to restore the previous behavior.
Current material workarounds:
- remove MergeReturnPass (except on Metal)
- remove SimplificationPass (except on Metal)
There are reason to believe these workarounds are no longer needed,
due to changes in spirv-opt.
It's possible that older devices might still need them.
These workaround affect *all* devices, this is why it is important
to disable them if not needed.
The script uses os.system to execute external commands like nm and
objdump. Instead we use the subprocess module, which is the recommended
approach in modern Python.
Properly handle potential errors if nm or objdump fail.
Currently the only values possible are 'none' and 'all'. 'all' is the
default. This option will be used to control code generation
workarounds individually. Currently 'all' disable the
MergeReturn and Simplification passes, which have causes issues in
the past on some older Android devices.
This commit enhances the material compilation process by embedding the
`matc` command-line parameters directly into the compiled material file.
This feature is valuable for debugging, as it allows developers to
inspect the exact compilation settings used for a given material.
A key consideration is the potential for personally identifiable
information (PII) in the command-line arguments (e.g., file paths). To
address this, a `toPIISafeString` method has been implemented to filter
out PII-sensitive options before they are stored in the material.
With this change, the matc command below
/path/to/matc -a opengl --api vulkan -p desktop -g -o /path/to/my.filamat /path/to/my.mat
is stored to the package as below. (veryfied by running `matinfo my.filamat`)
Compilation Parameters: -a opengl --api vulkan -p desktop -g
This commit introduces a CRC32 checksum to material packages to ensure
data integrity.
When a material is loaded, this checksum is verified. If the check
fails, an error is logged, and the material fails to load. For older
material packages without a CRC32 checksum, a warning is logged and
proceed.
BUGS=[373396840]
- when generating the DFG LUT as a text file, make sure to faithfully
reproduce the command used in the comments
- add a "bin" format that will output a headerless raw binary of the
LUT data.
A test for how Gemini can clean-up code. Changes:
- Configuration Struct: Global variables have been grouped into an
AppConfig struct. This makes the program's configuration explicit and
avoids polluting the global namespace.
- Clearer Argument Parsing: The handleArguments function now returns an
AppConfig object, encapsulating all parsing logic and making the main
function cleaner.
- Helper Functions: Repetitive tasks like string replacement, file I/O,
and writing file entries have been extracted into small, well-defined
helper functions (replaceAll, openOutputFile, readFile, etc.).
- Simplified main Function: The main function is now a high-level
coordinator, delegating work to helper functions. This makes the overall
program flow much easier to follow.
- Modern C++ Idioms: The code now consistently uses C++ streams
(std::cout, std::ofstream) and std::string objects, which is more
idiomatic than a mix of C and C++ styles.
- added short comments
instead of using a single resource binary for all materials, each
postfx set of materials (e.g. dof, bloom) gets its own resource file
based on the directory it is in.
resgen now generates the resources size/offset directly as #define
instead of as an `extern` variable. The assembly file also
doesn't generate the corresponding global variables, saving some binary
space.
`unfiterable : true` indicates that the sampling of the texture
will not apply filtering.
This is mainly to satisfy the webgpu requirement for
bindGroupLayouts.
- Documentation has been updated for both `unfilterable` and
`multisample`.
- Internal materials have been updated where necessary (depth
samplers have to be marked as `unfilterable`.)
- webgpu code has been changed appropriately.
BUGS=420745987
* materials: introduce mutable spec constants
Rationale & design of this feature has been discussed internally.
The current implementation uses a `FixedCapacityVector` to store the new program
handles, but I wouldn't object to replacing it with a hasmap as discussed
offline.
I have compiled but not tested this yet on Android, so I'm not certain that the
API bindings are correctly wired up.
* materials: mutable spec constant feedback
* materials: address mutable spec constant comments
Shader model (desktop or mobile) wasn't really accounted for
in the UI. This means that we will get shaders that look like
duplicates (same variant). In this work, we pass the current
shader model from engine into the frontend and filter out
variants of a different shader model.
Moreover, for matinfo, we use a specific dbg shader model (matinfo)
to indicate it is in that mode. We add UI in matinfo to show the
shadermodel.
So UI updates as well.