Compare commits

..
11 Commits
Author SHA1 Message Date
Benjamin Doherty be3239810a Release Filament 1.71.0 2026-04-07 18:41:31 -07:00
Mathias Agopian e8b268bce4 replace AutoresolveDifferingSampleCounts test
It's not clear what this test was trying to test and its golden was
wrong. 

Removed this test, and replaced it with a simple "autoresolve" test.
2026-04-03 17:02:18 -07:00
Mathias Agopian 88ef82a611 [backend/test] Fix MSAA resolves of depth/stencil attachments
- the first problem was that we don't need (or can) resolve RenderBuffer
  attachments, it's meaningless.

- the bigger problem was that we were marking all attachments of a FBO
  for potential resolve (at FBO creation).  Later, during endRenderPass, 
  the resolve pass itself would be triggered if the RT had a sidecar
  RT. But that's wrong because attachments that match the RT sample
  count don't need to be resolved.
  In turn, this was causing these attachments to be *discarded* 
  because, in beginRenderPass, we were detecting a case where it 
  looked like a MS attachment was used with non a MS rendertarget
  (and filament disallows that, the attachment must be discarded).
  We fix this by making sure attachments that don't need to be
  resolved are not marked as such.


FIXES=[453757787]
2026-04-03 17:02:18 -07:00
Mathias Agopian b4256c1c15 [backend/test] Fix LoadImageTest.UpdateImageSRGB precision mismatch on OpenGL
Relaxes exact pixel matching for `UpdateImageSRGB` test on software/MESA OpenGL drivers. Linear filtering on sRGB textures produces slightly different fractional weights for edge pixels compared to hardware texturing units.

FIXES=[453756688]
2026-04-03 17:02:18 -07:00
Mathias Agopian b49b61e131 [backend/test] Fix BlitTest scaling failures on MESA OpenGL
Relaxes exact pixel matching for `ColorMagnify` and `ColorMinify` tests on software/MESA OpenGL drivers. Software blit interpolation (glBlitFramebuffer) produces slightly different fractional weights for edge pixels compared to discrete hardware texturing units. 

FIXES=[453757697, 453758045, 453776623]
2026-04-03 17:02:18 -07:00
Mathias Agopian 92dc063a8a filaflat: Fix missing bounds checks in MaterialChunk::getTextShader (#9873)
- 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
2026-04-03 16:02:56 -07:00
Powei Feng e8fe85ee89 filamat: make DryRunner in Flattener not static (#9872)
This enables multiple thread to use MaterialBuilder/Flattener at
the cost of allocating, deallocating a flattener.
2026-04-03 18:41:50 +00:00
Doris Wu 0b63969aab Make the LruCache size of material/program cache configurable (#9865) 2026-04-03 03:10:14 +00:00
dependabot[bot]andPowei Feng af71d773ae build(deps): bump the npm_and_yarn group across 1 directory with 2 updates (#9870)
Bumps the npm_and_yarn group with 2 updates in the /build/common/upload-release-assets directory: [brace-expansion](https://github.com/juliangruber/brace-expansion) and [minimatch](https://github.com/isaacs/minimatch).


Updates `brace-expansion` from 1.1.12 to 1.1.13
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.13)

Updates `minimatch` from 3.1.3 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.3...v3.1.5)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.13
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Powei Feng <powei@google.com>
2026-04-02 17:06:54 +00:00
Mathias Agopian 57faae4645 postfx materials now support sRGB emulation in FL0 (#9866)
FIXES=[498317018]
2026-04-02 09:08:50 -07:00
Filament Bot e6cf5fa815 [automated] Updating /docs due to commit 426db0a
Full commit hash is 426db0a01d

DOCS_ALLOW_DIRECT_EDITS
2026-04-02 03:37:21 +00:00
27 changed files with 134 additions and 111 deletions
+3
View File
@@ -7,6 +7,9 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.71.1
## v1.71.0
- matc: better material compression using a Multi-Base Variable-Length Interleaved Token Stream [⚠️ **New Material Version**]
+12 -13
View File
@@ -204,10 +204,9 @@
"license": "Apache-2.0"
},
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"license": "MIT",
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -235,9 +234,9 @@
"license": "MIT"
},
"node_modules/minimatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -410,9 +409,9 @@
"integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A=="
},
"brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -429,9 +428,9 @@
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="
},
"minimatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"requires": {
"brace-expansion": "^1.1.7"
}
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.70.1'
implementation 'com.google.android.filament:filament-android:1.70.2'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -195,7 +195,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.70.1'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.70.2'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
+29 -18
View File
@@ -1740,6 +1740,10 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
assert_invariant(target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
GL_RENDERBUFFER, t->gl.id);
// Clear the resolve bit for this particular attachment. Note that other attachment(s)
// might be sampleable, so this does not necessarily prevent the resolve from occurring.
resolveFlags = TargetBufferFlags::NONE;
}
break;
case GL_TEXTURE_3D:
@@ -1879,6 +1883,11 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
CHECK_GL_ERROR()
}
// We don't need to resolve attachments that match the rendertarget's sample count.
if (rt->gl.samples == t->samples) {
resolveFlags = TargetBufferFlags::NONE;
}
rt->gl.resolve |= resolveFlags;
CHECK_GL_ERROR()
@@ -3768,20 +3777,6 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
// each render-pass starts with a disabled scissor
gl.disable(GL_SCISSOR_TEST);
if (gl.ext.EXT_discard_framebuffer
&& !gl.bugs.disable_invalidate_framebuffer) {
AttachmentArray attachments; // NOLINT
if (GLsizei const attachmentCount = getAttachments(attachments, discardFlags, !fbo)) {
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
CHECK_GL_ERROR()
} else {
// It's important to clear the framebuffer before drawing, as it resets
// the fb to a known state (resets fb compression and possibly other things).
// So we use glClear instead of glInvalidateFramebuffer
clearWithRasterPipe(discardFlags & ~clearFlags, { 0.0f }, 0.0f, 0);
}
if (rt->gl.fbo_read) {
// we have a multi-sample RenderTarget with non multi-sample attachments (i.e. this is the
// EXT_multisampled_render_to_texture emulation).
@@ -3793,9 +3788,26 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
discardFlags |= rt->gl.resolve;
}
// we don't discard attachments that are cleared (because clear is an implicit discard)
TargetBufferFlags const discardOnlyFlags = discardFlags & ~clearFlags;
if (any(discardOnlyFlags)) {
if (gl.ext.EXT_discard_framebuffer && !gl.bugs.disable_invalidate_framebuffer) {
AttachmentArray attachments; // NOLINT
if (GLsizei const attachmentCount = getAttachments(attachments, discardOnlyFlags, !fbo)) {
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
CHECK_GL_ERROR()
} else {
// It's important to clear the framebuffer before drawing, as it resets
// the fb to a known state (resets fb compression and possibly other things).
// So we use glClear instead of glInvalidateFramebuffer
clearWithRasterPipe(discardOnlyFlags, { 0.0f }, 0.0f, 0);
}
}
if (any(clearFlags)) {
clearWithRasterPipe(clearFlags,
params.clearColor, GLfloat(params.clearDepth), GLint(params.clearStencil));
clearWithRasterPipe(clearFlags, params.clearColor, GLfloat(params.clearDepth), GLint(params.clearStencil));
}
// we need to reset those after we call clearWithRasterPipe()
@@ -3812,8 +3824,7 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
#ifndef NDEBUG
// clear the discarded (but not the cleared ones) buffers in debug builds
clearWithRasterPipe(discardFlags & ~clearFlags,
{ 1, 0, 0, 1 }, 1.0, 0);
clearWithRasterPipe(discardOnlyFlags, { 1, 0, 0, 1 }, 1.0, 0);
#endif
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

+6 -36
View File
@@ -31,10 +31,7 @@ using namespace filament;
using namespace filament::backend;
using namespace filament::math;
TEST_F(BackendTest, AutoresolveDifferingSampleCounts) {
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::OPENGL), "see b/486954356");
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::VULKAN), "see b/486954356");
TEST_F(BackendTest, Autoresolve) {
auto& api = getDriverApi();
constexpr int kRenderTargetSize = 512;
@@ -61,33 +58,7 @@ TEST_F(BackendTest, AutoresolveDifferingSampleCounts) {
TextureFormat::RGBA8, 1, kRenderTargetSize, kRenderTargetSize, 1,
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
// First render pass: render a red triangle to a RenderTarget with 8 samples.
{
Handle<HwRenderTarget> renderTarget =
addCleanup(api.createRenderTarget(TargetBufferFlags::COLOR, kRenderTargetSize,
kRenderTargetSize, 8, 1, { { texture } }, {}, {}));
shader.uploadUniform(api, ubuffer,
SimpleMaterialParams{
.color = float4(1, 0, 0, 1),
});
RenderPassParams params = getClearColorRenderPass(float4(0));
params.viewport = { 0, 0, kRenderTargetSize, kRenderTargetSize };
RenderFrame frame(api);
api.beginRenderPass(renderTarget, params);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.commit(swapChain);
}
// Second render pass: render a green triangle to a RenderTarget with 4 samples, attached to
// the same texture.
// render a red triangle to a RenderTarget with 4 samples.
{
Handle<HwRenderTarget> renderTarget =
addCleanup(api.createRenderTarget(TargetBufferFlags::COLOR, kRenderTargetSize,
@@ -95,10 +66,10 @@ TEST_F(BackendTest, AutoresolveDifferingSampleCounts) {
shader.uploadUniform(api, ubuffer,
SimpleMaterialParams{
.color = float4(0, 1, 0, 1),
.color = float4(1, 0, 0, 1),
});
RenderPassParams params = getClearColorRenderPass(float4(0));
RenderPassParams params = getClearColorRenderPass(float4(1));
params.viewport = { 0, 0, kRenderTargetSize, kRenderTargetSize };
RenderFrame frame(api);
@@ -109,11 +80,10 @@ TEST_F(BackendTest, AutoresolveDifferingSampleCounts) {
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.commit(swapChain);
EXPECT_IMAGE(renderTarget, ScreenshotParams(kRenderTargetSize, kRenderTargetSize,
"AutoresolveDifferingSampleCounts", 1048576));
api.commit(swapChain);
"Autoresolve", 545665143));
}
}
+4 -6
View File
@@ -145,7 +145,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
}
TEST_F(BlitTest, ColorMagnify) {
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::OPENGL), "b/453757697");
// b/453757697: MESA glBlitFramebuffer linear interpolation fallback precision slightly mismatches.
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::VULKAN), "b/453777397");
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
@@ -206,15 +206,13 @@ TEST_F(BlitTest, ColorMagnify) {
{
RenderFrame frame(api);
EXPECT_IMAGE(dstRenderTargets[0],
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMagnify", 0x410bdd31));
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMagnify", 0x410bdd31, false, 8000, 5));
api.commit(swapChain);
}
}
TEST_F(BlitTest, ColorMinify) {
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::OPENGL), "b/453758045");
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::VULKAN), "b/453776623");
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
@@ -268,7 +266,7 @@ TEST_F(BlitTest, ColorMinify) {
SamplerMagFilter::LINEAR);
EXPECT_IMAGE(dstRenderTargets[0],
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMinify", 0xf3d9c53f));
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMinify", 0xf3d9c53f, false, 8000, 5));
}
TEST_F(BlitTest, ColorResolve) {
@@ -355,7 +353,7 @@ TEST_F(BlitTest, ColorResolve) {
SamplerMagFilter::NEAREST);
EXPECT_IMAGE(dstRenderTarget,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 531759687));
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 531759687, false, 8000, 5));
}
TEST_F(BlitTest, Blit2DTextureArray) {
+1 -2
View File
@@ -391,7 +391,6 @@ TEST_F(LoadImageTest, UpdateImage2D) {
}
TEST_F(LoadImageTest, UpdateImageSRGB) {
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::OPENGL), "see b/453756688");
SKIP_IF(Backend::VULKAN, "b/454040142");
auto& api = getDriverApi();
@@ -468,7 +467,7 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
api.endRenderPass();
EXPECT_IMAGE(defaultRenderTarget,
ScreenshotParams(kTexSize, kTexSize, "UpdateImageSRGB", 3300305265));
ScreenshotParams(kTexSize, kTexSize, "UpdateImageSRGB", 3300305265, false, 8000, 5));
api.commit(swapChain);
api.endFrame(0);
+1 -3
View File
@@ -177,9 +177,7 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL), "Stencil isn't applied");
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::OPENGL), "see b/453757787");
SKIP_IF(Backend::VULKAN, "b/453777217");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN), "Stencil not preserved");
auto& api = getDriverApi();
+18
View File
@@ -442,6 +442,24 @@ public:
* invoking asynchronous methods.
*/
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
/**
* Capacity of the LRU cache for material definitions.
*
* A value of 0 indicates that definitions will be destroyed immediately when they are no
* longer referenced by any material instances or scenes. A value greater than 0 defines
* the maximum number of unreferenced definitions to keep alive to avoid re-compilation.
*/
uint32_t materialCacheCapacity = 0;
/**
* Capacity of the LRU cache for program specializations.
*
* Similar to materialCacheCapacity, but applies to the underlying shader programs generated
* for materials. A value of 0 means immediate destruction of unreferenced programs. A
* positive value caches up to that number of programs.
*/
uint32_t programCacheCapacity = 0;
};
+3 -3
View File
@@ -39,9 +39,9 @@ bool MaterialCache::MaterialKey::operator==(MaterialKey const& rhs) const noexce
return parser == rhs.parser;
}
MaterialCache::MaterialCache()
: mDefinitions("MaterialCache::mDefinitions", 0),
mPrograms("MaterialCache::mPrograms", 0) {}
MaterialCache::MaterialCache(uint32_t materialCapacity, uint32_t programCapacity)
: mDefinitions("MaterialCache::mDefinitions", materialCapacity),
mPrograms("MaterialCache::mPrograms", programCapacity) {}
MaterialCache::~MaterialCache() {
assert_invariant(mDefinitions.empty());
+1 -1
View File
@@ -56,7 +56,7 @@ public:
using ProgramCache =
utils::RefCountedMap<ProgramSpecialization, backend::Handle<backend::HwProgram>>;
MaterialCache();
MaterialCache(uint32_t materialCapacity, uint32_t programCapacity);
~MaterialCache();
// All reference-counted resources should be freed by the time MaterialCache is destructed, but
+1
View File
@@ -271,6 +271,7 @@ FEngine::FEngine(Builder const& builder) :
mRenderableManager(*this),
mLightManager(*this),
mCameraManager(*this),
mMaterialCache(builder->mConfig.materialCacheCapacity, builder->mConfig.programCacheCapacity),
mCommandBufferQueue(
builder->mConfig.minCommandBufferSizeMB * MiB,
builder->mConfig.commandBufferSizeMB * MiB,
+4 -1
View File
@@ -244,6 +244,9 @@ bool MaterialChunk::getTextShader(Unflattener unflattener,
buf[0] = '_';
auto const [ptr, ec] = std::to_chars(buf + 1, buf + 16, numericLiteral);
size_t const len = ptr - buf;
if (UTILS_UNLIKELY(cursor + len >= shaderSize)) {
return false;
}
memcpy(&shaderContent[cursor], buf, len);
cursor += len;
continue;
@@ -263,7 +266,7 @@ bool MaterialChunk::getTextShader(Unflattener unflattener,
const auto& content = dictionary[globalIndex];
// Ensure string is correctly formed and doesn't exceed reserved shader space.
if (UTILS_UNLIKELY(content.size() == 0 || cursor + content.size() - 1 > shaderSize)) {
if (UTILS_UNLIKELY(content.size() == 0 || cursor + content.size() - 1 >= shaderSize)) {
return false;
}
+18 -4
View File
@@ -90,16 +90,23 @@ TEST_F(FilaflatSecurityTest, DictionaryTextOOBRead) {
// 2. Heap Buffer Overflow writing dictionary arrays to undersized output buffers
TEST_F(FilaflatSecurityTest, MaterialChunkHeapOverflow) {
std::vector<uint8_t> payload;
write16(payload, 0); // mSharedStrings
write16(payload, 0); // mVertexStrings
write16(payload, 0); // mFragmentStrings
write16(payload, 0); // mComputeStrings
write64(payload, 1); // numShaders
payload.push_back(1); // model
payload.push_back(0); // variant
payload.push_back(0); // stage
write32(payload, 15); // offset (8 + 1 + 1 + 1 + 4 = 15)
write32(payload, 23); // offset (4*2 + 8 + 1 + 1 + 1 + 4 = 23)
// Shader content layout
write32(payload, 4); // shaderSize (vulnerable tiny size)
write32(payload, 1); // lineCount = 1
write16(payload, 0); // lineIndex = 0
write32(payload, 0); // extLength
write32(payload, 0); // baseLength
write32(payload, 0); // numericLength
payload.push_back(0); // lineIndex = 0
std::vector<uint8_t> fileData;
write64(fileData, (uint64_t)filamat::ChunkType::MaterialGlsl);
@@ -132,14 +139,21 @@ TEST_F(FilaflatSecurityTest, MaterialChunkHeapOverflow) {
// 3. Out of Bounds Read via index mapping evasion (Text mode)
TEST_F(FilaflatSecurityTest, MaterialChunkOOBReadText) {
std::vector<uint8_t> payload;
write16(payload, 0); // mSharedStrings
write16(payload, 0); // mVertexStrings
write16(payload, 0); // mFragmentStrings
write16(payload, 0); // mComputeStrings
write64(payload, 1); // numShaders
payload.push_back(1); // model
payload.push_back(0); // variant
payload.push_back(0); // stage
write32(payload, 15); // offset (8 + 1 + 1 + 1 + 4 = 15)
write32(payload, 23); // offset
write32(payload, 100); // shaderSize
write32(payload, 1); // lineCount = 1
write16(payload, 9999); // lineIndex = 9999 (VULNERABLE ACCESS)
write32(payload, 0); // extLength
write32(payload, 0); // baseLength
write32(payload, 0); // numericLength
payload.push_back(1); // lineIndex = 1 (OOB for empty dict)
std::vector<uint8_t> fileData;
write64(fileData, (uint64_t)filamat::ChunkType::MaterialGlsl);
+2 -1
View File
@@ -24,7 +24,8 @@ namespace filamat {
// when the container is about to be flattened.
size_t ChunkContainer::getSize() const {
return flatten(Flattener::getDryRunner());
auto flattener = Flattener::getDryRunner();
return flatten(flattener);
}
size_t ChunkContainer::flatten(Flattener& f) const {
+2 -8
View File
@@ -48,14 +48,8 @@ public:
// flattening. If we set mStart = nullptr and mEnd=nullptr, we would hit an error about
// offsetting on null when ubsan is enabled. Instead we point mStart to a fake address, and
// mCursor is offset from that.
static Flattener& getDryRunner() {
static Flattener dryRunner = Flattener(nullptr);
dryRunner.mStart = (uint8_t*) FAKE_DRY_RUNNER_START_ADDR;
dryRunner.mCursor = (uint8_t*) FAKE_DRY_RUNNER_START_ADDR;
dryRunner.mOffsetPlaceholders.clear();
dryRunner.mSizePlaceholders.clear();
dryRunner.mValuePlaceholders.clear();
return dryRunner;
static Flattener getDryRunner() {
return Flattener((uint8_t*) FAKE_DRY_RUNNER_START_ADDR);
}
bool isDryRunner() {
+9 -4
View File
@@ -250,7 +250,9 @@ void ShaderGenerator::generateVertexDomainDefines(io::sstream& out, VertexDomain
}
void ShaderGenerator::generatePostProcessMaterialVariantDefines(io::sstream& out,
PostProcessVariant const variant) noexcept {
ShaderStage const, MaterialBuilder::FeatureLevel const featureLevel,
MaterialInfo const&, PostProcessVariant const variant) noexcept {
CodeGenerator::generateDefine(out, "MATERIAL_FEATURE_LEVEL", uint32_t(featureLevel));
switch (variant) {
case PostProcessVariant::OPAQUE:
CodeGenerator::generateDefine(out, "POST_PROCESS_OPAQUE", 1u);
@@ -737,7 +739,8 @@ std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel const sm
}
CodeGenerator::generatePostProcessInputs(vs, ShaderStage::VERTEX);
generatePostProcessMaterialVariantDefines(vs, PostProcessVariant(variantKey));
generatePostProcessMaterialVariantDefines(vs, ShaderStage::VERTEX,
featureLevel, material, PostProcessVariant(variantKey));
cg.generateUniforms(vs, ShaderStage::VERTEX,
DescriptorSetBindingPoints::PER_VIEW,
@@ -765,14 +768,16 @@ std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel const sm
std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel const sm,
MaterialBuilder::TargetApi const targetApi, MaterialBuilder::TargetLanguage const targetLanguage,
MaterialBuilder::FeatureLevel const featureLevel,
MaterialInfo const& material, uint8_t variant, uint32_t apiLevel) const noexcept {
MaterialInfo const& material, filament::Variant::type_t variantKey,
uint32_t apiLevel) const noexcept {
const CodeGenerator cg(sm, targetApi, targetLanguage, featureLevel);
io::sstream fs;
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, {}, apiLevel);
generateUserSpecConstants(cg, fs, mConstants);
generatePostProcessMaterialVariantDefines(fs, PostProcessVariant(variant));
generatePostProcessMaterialVariantDefines(fs, ShaderStage::FRAGMENT,
featureLevel, material, PostProcessVariant(variantKey));
// custom material variables
size_t variableIndex = 0;
+5 -2
View File
@@ -102,7 +102,9 @@ private:
MaterialInfo const& material, filament::Variant variant) noexcept;
static void generatePostProcessMaterialVariantDefines(utils::io::sstream& out,
filament::PostProcessVariant variant) noexcept;
filament::backend::ShaderStage stage,
MaterialBuilder::FeatureLevel featureLevel,
MaterialInfo const& material, filament::PostProcessVariant variant) noexcept;
static void generateUserSpecConstants(
const CodeGenerator& cg, utils::io::sstream& fs,
@@ -117,7 +119,8 @@ private:
std::string createPostProcessFragmentProgram(filament::backend::ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialBuilder::FeatureLevel featureLevel,
MaterialInfo const& material, uint8_t variant, uint32_t apiLevel) const noexcept;
MaterialInfo const& material, filament::Variant::type_t variantKey,
uint32_t apiLevel) const noexcept;
static void appendShader(utils::io::sstream& ss,
const utils::CString& shader, size_t lineOffset) noexcept;
+8
View File
@@ -4,6 +4,14 @@ void main() {
// Invoke user code
postProcess(inputs);
#if MATERIAL_FEATURE_LEVEL == 0
if (CONFIG_SRGB_SWAPCHAIN_EMULATION) {
if (frameUniforms.rec709 != 0) {
inputs.FRAG_OUTPUT0.rgb = pow(inputs.FRAG_OUTPUT0.rgb, vec3(0.45454));
}
}
#endif
#if defined(FRAG_OUTPUT0)
FRAG_OUTPUT_AT0 FRAG_OUTPUT_SWIZZLE0 = inputs.FRAG_OUTPUT0;
#endif
+1 -3
View File
@@ -114,9 +114,7 @@ void main() {
#if MATERIAL_FEATURE_LEVEL == 0
if (CONFIG_SRGB_SWAPCHAIN_EMULATION) {
if (frameUniforms.rec709 != 0) {
fragColor.r = pow(fragColor.r, 0.45454);
fragColor.g = pow(fragColor.g, 0.45454);
fragColor.b = pow(fragColor.b, 0.45454);
fragColor.rgb = pow(fragColor.rgb, vec3(0.45454));
}
}
#endif