Implemented the custom 3D LUT feature in the ColorGrading API, allowing
users to specify a custom LUT for final color mapping.
To test this feature, added a "Custom LUT" option to the color grading
settings in the gltf_viewer sample. This includes several procedurally
generated LUTs for testing purposes:
- Negative
- Grayscale
- Sepia
- Teal and Orange
Updated settings serialization (Settings.cpp) and the viewer UI
(ViewerGui.cpp) to support these options.
FIXES=[362596936]
- Added a bounds check in the DICTIONARY_NUMERIC_FLAG case to prevent a
heap buffer overflow when writing decoded numeric literals.
- Tightened the existing bounds check for dictionary strings to use `>=`
instead of `>`, preventing a potential off-by-one overflow when
writing the null terminator.
- Fixed the `MaterialChunkHeapOverflow` and `MaterialChunkOOBReadText`
unit tests in `test_filaflat.cpp` by adding missing header fields to
the mock payloads so they correctly pass initialization.
Fixes#9871
* a new tribool type in libutils (header only)
* Support `Engine::compile` overload to generate view-dependent variants
Implemented an `Engine::compile()` method to infer and generate shader
permutations directly based on feature states from a provided `View`
alongside `tribool` settings (skinning, shadowReceiver).
- Added a new `Material::compile()` overload that directly accepts
a precalculated `FixedCapacityVector<Variant>`, decoupling the
Material compilation process from specific feature flag checks.
FIXES=[468058969]
- Move the renderer options to be applied before others before view
and camera options.
- Add "render" field to the output of the json description of
the settings (this was missing).
- This fixes the Scene->Background Color setting for
Android's gltf_viewer
- Fix constant wrap-around: Define `BIT_COUNT`, `BITS_PER_WORD`, and
`WORD_COUNT` (fixed typo from `WORLD_COUNT`) as `size_t` rather than
`T` to prevent silent overflows on large bitsets using small integer
types.
- Fix NEON `all()` truncation: Remove the generic `T` cast to correctly
evaluate bounds across the full 64-bit vector lanes.
- Fix non-NEON `count()` overflow: Use a `size_t` accumulator rather
than `T` to prevent overflow when counting >255 bits on `uint8_t`
parameters.
- Fix NEON strict alignment UB: Enforce unconditional `alignas(16)` on
`storage` when `BIT_COUNT` satisfies 128-bit vector multiples, adding
a compile-time `static_assert` to ensure ABI expectations match NEON
load intrinsics.
- Remove dead masking code inside `firstSetBit()`.
Missing the castShadows=true caused the shadows to be missing
on mobile (using the remote settings page).
Update README.md (this will trigger an update to the remote
page's filament.js)
* 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.
* Fix out-of-bounds string read by verifying null-terminator existence
during extraction.
* Fix heap buffer overflow by validating dictionary string lengths
against target shader buffers before copying.
* Fix out-of-bounds array reads by validating chunk-provided lookup
indices against parsed dictionary sizes.
* Fix integer wrapping exploits by replacing pointer addition with
offset subtraction during chunk size verifications.
Add unit tests for these vulerabilities.
- 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.
libfilament (including libutils and libmath) are 100% std::string
free.
std::string is pulled in the .so (on android) through libc++ for
exception handling, even if we're not using them. There is not much
we can do here, but at least, it's not because of us!
utils::ostream still references it but only as an inline function,
so if the inline is not called, std::string won't be pulled in.
It's also referenced from Path.cpp, but that's not included in
libfilament.
This made it nearly impossible to find the actual error. Now, we
output only the error from the compiler + the material name, variant
and shader stage all in one line.
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 commit addresses a critical security vulnerability (OOB write) and
several stability issues in the Radiance HDR parser.
Primary Fix:
* Fixed a heap buffer overflow in the RLE decoding loop (Issue #9748).
The decoder previously failed to verify if a run-length chunk exceeded
the remaining space in the scanline buffer. Added strict bounds checking
(`num_bytes + run_length > width`) before executing `memset` or
`mStream.read` to prevent arbitrary memory corruption.
Additional Security & Stability Improvements:
* Prevented an infinite loop (DoS) in header parsing. Replaced the
`do { ... } while(true);` loop with proper stream state checking
(`while (mStream.getline(...))`) to handle unexpected EOFs gracefully.
* Mitigated integer overflow and Out-Of-Memory (OOM) vulnerabilities by
enforcing maximum sane dimensions (`MAX_IMAGE_DIMENSION` and
`MAX_IMAGE_PIXELS`). This prevents catastrophic memory allocations
triggered by maliciously crafted width/height values.
* Initialized local variables and buffers (`buf`, `gamma`, `exposure`) to
prevent undefined behavior and parsing of stack garbage upon stream read
failures.
Fixes#9748
* utils: add LRU cache to RefCountedMap
This change introduces a new data structure LruCache and uses it in
RefCountedMap to keep a fixed number of cache entries alive after their
reference count has dropped to zero in the main map.
* utils: address LRU cache comments
- refactoring/clecanup to make some changes easier
- VSM mipmap generation was mistakenly disable when blur radius was 0
- analytic variance was disabled because the math only worked for VSM. Fixed the math.
- better handling of large blurs when using fp32
- implement EVSM equivalent of receiver plane normal bias
- use correct EVSM clearing color
- mipmapping with point lights works much better (no seams)
- min variance is computed automatically
- custom high precision mipmaping shader for VSM
The VSM variant bit was overloaded, it meant two different things
depending on the DEP bit (depth).
For standard variants (DEP = 0), it decides the type of the shadow
sampler used (PCF or 2D).
For depth variants (DEP = 1), it decides what is written during the
shadow pass (nothing, i.e. depth only, or EVSM depth moments).
We now clearly separate the two bits throughout the code.
This change should be purely source-cosmetic, there shouldn't be any
behavior changes.
Co-authored-by: Powei Feng <powei@google.com>
The previous code used the max of the texel's width or height footprint
in world space to compute the offset; this could both overestimate or
underestimate the bias causing some peter panning or acne.
The new code replaces a sqrt with a dot, but is otherwise similar.
* fixbug:"debug error, reason: The material 'Material_MR' has not been compiled to include the required GLSL or SPIR-V chunks for the vertex shader (variant=5, filtered=5)",Because the member variable mVariantFilter is not initialized, random values will appear on Windows 10 or others platform, which eventually causes some variants to be filtered out by this mVariantFilter.
* fix:Fix morph target loading for accessors without buffer_view
Morph targets were not working because ResourceLoader skipped all
accessors without buffer_view. For morph targets, the data can be
accessed directly via cgltf_accessor_unpack_floats().
This fix properly unpacks and uploads morph target vertex data to the
GPU, enabling blendshapes and facial deformation to work correctly.
Steps to Reproduce
1、In Unity (2022.3.11): Create a Prefab with Blend Shapes (Morph Targets) and an Animator to control them (e.g., an animation clip that makes the eyes squint).
2、Export: Use the UnityGLTF tool to export the model as a .glb file (including the Animator and Morph Target tracks).
3、In Filament: Load and play the animation.
4、Result: The skeletal animation (bone-based) may play, but the Morph Target effect (squinting) is missing or static.