Compare commits

..
18 Commits
Author SHA1 Message Date
Powei Feng cde3d5a2df Merge branch 'rc/1.71.1' into release 2026-04-21 12:19:51 -07:00
Ben Doherty 06793c4a80 Add support for custom outputs for unlit surface materials (#9912) 2026-04-20 11:47:51 -04:00
Eliza Velasquez 04684411b9 engine: override spec constants on instance-basis
# Conflicts:
#	filament/src/LocalProgramCache.cpp
#	filament/src/PostProcessManager.cpp
#	filament/src/PostProcessManager.h
#	filament/src/details/Material.cpp
2026-04-20 14:34:42 +08:00
Ben Doherty b52c25694e Metal: fix potential crash in when dynamic offsets are empty (#9908) 2026-04-16 14:05:32 -04:00
Mathias Agopian b85d9af9f9 FL0 materials are always compile via spirv-cross 2026-04-13 21:22:56 -07:00
Ben Doherty 5f878667a8 Fix null texture pointer crash in GLDescriptorSet (#9884) 2026-04-08 13:01:10 -07:00
Benjamin Doherty 7ec4faa324 Bump version to 1.71.1 2026-04-07 18:41:43 -07:00
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
65 changed files with 1082 additions and 413 deletions
+2
View File
@@ -6,3 +6,5 @@
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: add `MaterialInstance::setConstant()` and `MaterialInstance::getConstant()` methods. These allow for per-material instance specialization constant overrides.
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.71.0'
implementation 'com.google.android.filament:filament-android:1.71.1'
}
```
@@ -50,7 +50,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.71.0'
pod 'Filament', '~> 1.71.1'
```
## Documentation
+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**]
@@ -20,6 +20,8 @@
#include <filament/Texture.h>
#include <filament/TextureSampler.h>
#include "common/CallbackUtils.h"
#include <math/mat3.h>
#include <math/mat4.h>
#include <math/vec2.h>
@@ -246,6 +248,69 @@ Java_com_google_android_filament_MaterialInstance_nSetFloatParameterArray(JNIEnv
env->ReleaseStringUTFChars(name_, name);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nGetConstantBool(JNIEnv *env, jclass,
jlong nativeMaterialInstance, jstring name_) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
jboolean result = instance->getConstant<bool>(name);
env->ReleaseStringUTFChars(name_, name);
return result;
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_MaterialInstance_nGetConstantFloat(JNIEnv *env, jclass,
jlong nativeMaterialInstance, jstring name_) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
jfloat result = instance->getConstant<float>(name);
env->ReleaseStringUTFChars(name_, name);
return result;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_MaterialInstance_nGetConstantInt(JNIEnv *env, jclass,
jlong nativeMaterialInstance, jstring name_) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
jint result = instance->getConstant<int32_t>(name);
env->ReleaseStringUTFChars(name_, name);
return result;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetConstantBool(JNIEnv *env, jclass,
jlong nativeMaterialInstance, jstring name_, jboolean x) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
instance->setConstant<bool>(name, x);
env->ReleaseStringUTFChars(name_, name);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetConstantFloat(JNIEnv *env, jclass,
jlong nativeMaterialInstance, jstring name_, jfloat x) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
instance->setConstant<float>(name, x);
env->ReleaseStringUTFChars(name_, name);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetConstantInt(JNIEnv *env, jclass,
jlong nativeMaterialInstance, jstring name_, jint x) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
instance->setConstant<int32_t>(name, x);
env->ReleaseStringUTFChars(name_, name);
}
// defined in TextureSampler.cpp
namespace filament::JniUtils {
TextureSampler from_long(jlong params) noexcept;
@@ -580,3 +645,17 @@ Java_com_google_android_filament_MaterialInstance_nGetTransparencyMode(JNIEnv*,
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
return (jint) instance->getTransparencyMode();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nCompile(JNIEnv *env, jclass clazz,
jlong nativeMaterialInstance, jint priority, jint variants, jobject handler, jobject runnable) {
MaterialInstance* materialInstance = (MaterialInstance*) nativeMaterialInstance;
JniCallback* jniCallback = JniCallback::make(env, handler, runnable);
materialInstance->compile(
(MaterialInstance::CompilerPriorityQueue) priority,
(UserVariantFilterBit) variants,
jniCallback->getHandler(), [jniCallback](MaterialInstance*){
JniCallback::postToJavaAndDestroy(jniCallback);
});
}
@@ -522,11 +522,19 @@ public class Material {
* for stereoscopic rendering. If an application is not planning to render in stereo, this bit
* should be turned off to avoid unnecessary material compilations.
*</p>
*<p>
* Note that it is possible to override specialization constants on a per-MaterialInstance basis
* (see {@link MaterialInstance#setConstant}). In that case, the programs compiled by a call to
* Material::compile() may not be reusable by that MaterialInstance. It's better to call
* MaterialInstance::compile() in cases where you intend to override specialization constants.
*</p>
* @param priority Which priority queue to use, LOW or HIGH.
* @param variants Variants to include to the compile command.
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
* @param callback callback called on the main thread when the compilation is done on
* by backend.
*
* @see MaterialInstance#compile
*/
public void compile(@NonNull CompilerPriorityQueue priority,
int variants,
@@ -18,6 +18,7 @@ package com.google.android.filament;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Size;
import com.google.android.filament.proguard.UsedByNative;
@@ -142,6 +143,28 @@ public class MaterialInstance {
return mMaterial;
}
/**
* Asynchronously ensures that a subset of this MaterialInstance's variants are compiled.
*
* <p>This function behaves identically to {@link Material#compile}, but takes into account
* the specific constants overridden by {@link #setConstant}.</p>
*
* @param priority Priority of the compile command.
* @param variants Variants to include to the compile command.
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
* @param callback callback called on the main thread when the compilation is done on
* by backend.
*
* @see Material#compile
* @see #setConstant
*/
public void compile(@NonNull Material.CompilerPriorityQueue priority,
int variants,
@Nullable Object handler,
@Nullable Runnable callback) {
nCompile(getNativeObject(), priority.ordinal(), variants, handler, callback);
}
/** @return the name associated with this instance */
@NonNull
public String getName() {
@@ -402,6 +425,69 @@ public class MaterialInstance {
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
}
/**
* Overrides a specialization constant of this material instance.
*
* @param name The name of the constant as defined in the material.
* @param value The value of the constant.
* @see Material.Builder#constant
*/
public void setConstant(@NonNull String name, boolean value) {
nSetConstantBool(getNativeObject(), name, value);
}
/**
* Overrides a specialization constant of this material instance.
*
* @param name The name of the constant as defined in the material.
* @param value The value of the constant.
* @see Material.Builder#constant
*/
public void setConstant(@NonNull String name, float value) {
nSetConstantFloat(getNativeObject(), name, value);
}
/**
* Overrides a specialization constant of this material instance.
*
* @param name The name of the constant as defined in the material.
* @param value The value of the constant.
* @see Material.Builder#constant
*/
public void setConstant(@NonNull String name, int value) {
nSetConstantInt(getNativeObject(), name, value);
}
/**
* Gets the value of a specialization constant by name.
*
* @param name The name of the constant as defined in the material.
* @return The value of the constant.
*/
public boolean getConstantBoolean(@NonNull String name) {
return nGetConstantBool(getNativeObject(), name);
}
/**
* Gets the value of a specialization constant by name.
*
* @param name The name of the constant as defined in the material.
* @return The value of the constant.
*/
public float getConstantFloat(@NonNull String name) {
return nGetConstantFloat(getNativeObject(), name);
}
/**
* Gets the value of a specialization constant by name.
*
* @param name The name of the constant as defined in the material.
* @return The value of the constant.
*/
public int getConstantInt(@NonNull String name) {
return nGetConstantInt(getNativeObject(), name);
}
/**
* Set-up a custom scissor rectangle; by default it is disabled.
*
@@ -937,6 +1023,17 @@ public class MaterialInstance {
@NonNull String name, int element, @NonNull @Size(min = 1) float[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count);
private static native boolean nGetConstantBool(long nativeMaterialInstance, @NonNull String name);
private static native float nGetConstantFloat(long nativeMaterialInstance, @NonNull String name);
private static native int nGetConstantInt(long nativeMaterialInstance, @NonNull String name);
private static native void nSetConstantBool(long nativeMaterialInstance,
@NonNull String name, boolean x);
private static native void nSetConstantFloat(long nativeMaterialInstance,
@NonNull String name, float x);
private static native void nSetConstantInt(long nativeMaterialInstance,
@NonNull String name, int x);
private static native void nSetParameterTexture(long nativeMaterialInstance,
@NonNull String name, long nativeTexture, long sampler);
@@ -1000,4 +1097,5 @@ public class MaterialInstance {
private static native int nGetDepthFunc(long nativeMaterialInstance);
private static native void nSetTransparencyMode(long nativeMaterialInstance, int mode);
private static native int nGetTransparencyMode(long nativeMaterialInstance);
private static native void nCompile(long nativeMaterialInstance, int priority, int variants, Object handler, Runnable callback);
}
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.71.0
VERSION_NAME=1.71.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+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.
+8 -6
View File
@@ -2298,12 +2298,14 @@ void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t inst
// Bind the offset data.
if (mContext->dynamicOffsets.isDirty()) {
const auto [size, data] = mContext->dynamicOffsets.getOffsets();
[mContext->currentRenderPassEncoder setFragmentBytes:data
length:size * sizeof(uint32_t)
atIndex:DYNAMIC_OFFSET_BINDING];
[mContext->currentRenderPassEncoder setVertexBytes:data
length:size * sizeof(uint32_t)
atIndex:DYNAMIC_OFFSET_BINDING];
if (size > 0) {
[mContext->currentRenderPassEncoder setFragmentBytes:data
length:size * sizeof(uint32_t)
atIndex:DYNAMIC_OFFSET_BINDING];
[mContext->currentRenderPassEncoder setVertexBytes:data
length:size * sizeof(uint32_t)
atIndex:DYNAMIC_OFFSET_BINDING];
}
mContext->dynamicOffsets.setDirty(false);
}
@@ -185,11 +185,13 @@ void GLDescriptorSet::update(OpenGLState& gl, HandleAllocatorGL& handleAllocator
}
// GLES3.x specification forbids depth textures to be filtered.
bool const depthTextureNoPcf = isDepthFormat(t->format) &&
bool const isDepthFmt = t && isDepthFormat(t->format);
bool const depthTextureNoPcf = isDepthFmt &&
params.compareMode == SamplerCompareMode::NONE;
// GLES3.x may not support filtering of fp32 textures
bool const fp32TextureNotFilterable = isFp32ColorFormat(t->format) &&
bool const isFp32Fmt = t && isFp32ColorFormat(t->format);
bool const fp32TextureNotFilterable = isFp32Fmt &&
!gl.context().ext.OES_texture_float_linear;
if (t && (depthTextureNoPcf || fp32TextureNotFilterable)) {
+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;
};
+9 -4
View File
@@ -139,10 +139,8 @@ public:
Builder& package(const void* UTILS_NONNULL payload, size_t size);
template<typename T>
using is_supported_constant_parameter_t = std::enable_if_t<
std::is_same_v<int32_t, T> ||
std::is_same_v<float, T> ||
std::is_same_v<bool, T>>;
using is_supported_constant_parameter_t =
MaterialInstance::is_supported_constant_parameter_t<T>;
/**
* Specialize a constant parameter specified in the material definition with a concrete
@@ -243,11 +241,18 @@ public:
* for stereoscopic rendering. If an application is not planning to render in stereo, this bit
* should be turned off to avoid unnecessary material compilations.
*
* Note that it is possible to override specialization constants on a per-MaterialInstance basis
* (@see MaterialInstance::setConstant). In that case, the programs compiled by a call to
* Material::compile() may not be reusable by that MaterialInstance. It's better to call
* MaterialInstance::compile() in cases where you intend to override specialization constants.
*
* @param priority Which priority queue to use, LOW or HIGH.
* @param variants Variants to include to the compile command.
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback callback called on the main thread when the compilation is done on
* by backend.
*
* @see Material::compile
*/
void compile(CompilerPriorityQueue priority,
UserVariantFilterMask variants,
@@ -94,6 +94,12 @@ public:
std::is_same_v<math::mat3f, T>
>;
template<typename T>
using is_supported_constant_parameter_t = std::enable_if_t<
std::is_same_v<int32_t, T> ||
std::is_same_v<float, T> ||
std::is_same_v<bool, T>>;
/**
* Creates a new MaterialInstance using another MaterialInstance as a template for initialization.
* The new MaterialInstance is an instance of the same Material of the template instance and
@@ -276,6 +282,91 @@ public:
return getParameter<T>(name, strlen(name));
}
/**
* Overrides a specialization constant of this material instance.
*
* @tparam T The type of the constant. Must be int32_t, float, or bool.
* @param name The name of the constant as defined in the material. Cannot be nullptr.
* @param nameLength Length in `char` of the name parameter.
* @param value The value of the constant.
*
* @see Material::Builder::constant
*/
template<typename T, typename = is_supported_constant_parameter_t<T>>
void setConstant(const char* UTILS_NONNULL name, size_t nameLength, T value);
/** inline helper to provide the name as a null-terminated string literal */
template<typename T, typename = is_supported_constant_parameter_t<T>>
void setConstant(StringLiteral const name, T value) {
setConstant<T>(name.data, name.size, value);
}
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
void setConstant(const char* UTILS_NONNULL name, T value) {
setConstant<T>(name, strlen(name), value);
}
/**
* Gets the value of a specialization constant by name.
*
* @tparam T The type of the constant. Must be int32_t, float, or bool.
* @param name The name of the constant as defined in the material. Cannot be nullptr.
* @param nameLength Length in `char` of the name parameter.
* @return The value of the constant.
*/
template<typename T, typename = is_supported_constant_parameter_t<T>>
T getConstant(const char* UTILS_NONNULL name, size_t nameLength) const;
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
T getConstant(StringLiteral const name) const {
return getConstant<T>(name.data, name.size);
}
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
T getConstant(const char* UTILS_NONNULL name) const {
return getConstant<T>(name, strlen(name));
}
using CompilerPriorityQueue = backend::CompilerPriorityQueue;
/**
* Asynchronously ensures that a subset of this MaterialInstance's variants are compiled.
*
* This function behaves identically to Material::compile(), but takes into account the
* specific constants overridden by setConstant().
*
* @param priority Which priority queue to use, LOW or HIGH.
* @param variants Variants to include to the compile command.
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback callback called on the main thread when the compilation is done on
* by backend.
*
* @see Material::compile
* @see setConstant
*/
void compile(CompilerPriorityQueue priority,
UserVariantFilterMask variants,
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>&& callback = {}) noexcept;
inline void compile(CompilerPriorityQueue priority,
UserVariantFilterBit variants,
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>&& callback = {}) noexcept {
compile(priority, UserVariantFilterMask(variants), handler,
std::forward<utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>>(callback));
}
inline void compile(CompilerPriorityQueue priority,
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>&& callback = {}) noexcept {
compile(priority, UserVariantFilterBit::ALL, handler,
std::forward<utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>>(callback));
}
/**
* Set-up a custom scissor rectangle; by default it is disabled.
*
+11 -8
View File
@@ -182,15 +182,11 @@ Program::SpecializationConstant LocalProgramCache::getConstantImpl(
std::string_view name) const noexcept {
assert_invariant(mMaterial != nullptr);
MaterialDefinition const& definition = mMaterial->getDefinition();
auto it = definition.specializationConstantsNameToIndex.find(name);
if (it != definition.specializationConstantsNameToIndex.cend()) {
return getConstantImpl(it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
}
auto const& constants = mMaterial->getDefinition().specializationConstantsNameToIndex;
auto it = constants.find(name);
FILAMENT_CHECK_PRECONDITION(it != constants.end()) << "Constant " << name << " does not exist";
CString name_cstring(name);
PANIC_PRECONDITION("No such constant exists: %s", name_cstring.c_str());
return {};
return getConstantImpl(it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
}
void LocalProgramCache::setConstants(
@@ -240,6 +236,13 @@ void LocalProgramCache::setConstants(
}
}
void LocalProgramCache::setConstants(
FixedCapacityVector<Program::SpecializationConstant> constants) noexcept {
assert_invariant(mMaterial != nullptr);
setConstantsImpl(std::move(constants));
}
void LocalProgramCache::setConstantsImpl(
FixedCapacityVector<Program::SpecializationConstant> constants) noexcept {
FEngine& engine = mMaterial->getEngine();
+7
View File
@@ -119,7 +119,14 @@ public:
std::pair<std::string_view, backend::Program::SpecializationConstant>>
constants) noexcept;
// Set constants list directly.
void setConstants(utils::FixedCapacityVector<backend::Program::SpecializationConstant>
constants) noexcept;
private:
// Apply any pending specialization constants. Invalidates programs as necessary.
void flushConstants() const;
backend::Handle<backend::HwProgram> prepareProgramSlow(backend::DriverApi& driver,
Variant const variant,
backend::CompilerPriorityQueue const priorityQueue) const noexcept;
+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
+26
View File
@@ -233,6 +233,26 @@ template UTILS_PUBLIC mat3f MaterialInstance::getParameter<mat3f> (const ch
// ------------------------------------------------------------------------------------------------
template<typename T, typename>
void MaterialInstance::setConstant(const char* name, size_t nameLength, T value) {
downcast(this)->setConstantImpl(std::string_view{name, nameLength}, value);
}
template UTILS_PUBLIC void MaterialInstance::setConstant<int32_t>(const char* name, size_t nameLength, int32_t value);
template UTILS_PUBLIC void MaterialInstance::setConstant<float>(const char* name, size_t nameLength, float value);
template UTILS_PUBLIC void MaterialInstance::setConstant<bool>(const char* name, size_t nameLength, bool value);
template<typename T, typename>
T MaterialInstance::getConstant(const char* name, size_t nameLength) const {
return downcast(this)->getConstantImpl<T>(std::string_view{name, nameLength});
}
template UTILS_PUBLIC int32_t MaterialInstance::getConstant<int32_t>(const char* name, size_t nameLength) const;
template UTILS_PUBLIC float MaterialInstance::getConstant<float>(const char* name, size_t nameLength) const;
template UTILS_PUBLIC bool MaterialInstance::getConstant<bool>(const char* name, size_t nameLength) const;
// ------------------------------------------------------------------------------------------------
Material const* MaterialInstance::getMaterial() const noexcept {
return downcast(this)->getMaterial();
}
@@ -403,4 +423,10 @@ void MaterialInstance::commit(Engine& engine) const {
downcast(this)->commit(downcast(engine));
}
void MaterialInstance::compile(CompilerPriorityQueue const priority,
UserVariantFilterMask const variants, CallbackHandler* handler,
utils::Invocable<void(MaterialInstance*)>&& callback) noexcept {
downcast(this)->compile(priority, variants, handler, std::move(callback));
}
} // namespace filament
+74 -59
View File
@@ -186,12 +186,10 @@ void PostProcessManager::PostProcessMaterial::loadMaterial(FEngine& engine) cons
}
UTILS_NOINLINE
FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine,
DriverApi& driver, Variant::type_t const variant) const noexcept {
FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine) const noexcept {
if (UTILS_UNLIKELY(mSize)) {
loadMaterial(engine);
}
mMaterial->prepareProgram(driver, Variant{ variant }, CompilerPriorityQueue::CRITICAL);
return mMaterial;
}
@@ -249,6 +247,20 @@ void PostProcessManager::bindPerRenderableDescriptorSet(DriverApi& driver) const
{ { 0, 0 }, driver });
}
FMaterialInstance* PostProcessManager::getMaterialInstance(backend::DriverApi& driver,
FMaterial const* ma, Variant::type_t variant) const {
FMaterialInstance* mi = mMaterialInstanceManager.getMaterialInstance(ma);
mi->prepareProgram(driver, Variant{ variant }, backend::CompilerPriorityQueue::CRITICAL);
return mi;
}
FMaterialInstance* PostProcessManager::getMaterialInstanceWithTag(backend::DriverApi& driver,
FMaterial const* ma, uint32_t tag, Variant::type_t variant) const {
FMaterialInstance* mi = mMaterialInstanceManager.getMaterialInstance(ma, tag);
mi->prepareProgram(driver, Variant { variant }, backend::CompilerPriorityQueue::CRITICAL);
return mi;
}
UboManager* PostProcessManager::getUboManager() const noexcept {
return mEngine.getUboManager();
}
@@ -468,9 +480,10 @@ void PostProcessManager::unbindAllDescriptorSets(DriverApi& driver) noexcept {
UTILS_NOINLINE
PipelineState PostProcessManager::getPipelineState(
FMaterial const* const ma, Variant::type_t const variant) const noexcept {
FMaterialInstance const* const mi, Variant::type_t const variant) const noexcept {
FMaterial const* const ma = mi->getMaterial();
return {
.program = ma->getProgram(Variant{ variant }),
.program = mi->getProgram(Variant{ variant }),
.vertexBufferInfo = mFullScreenQuadVbih,
.pipelineLayout = {
.setLayout = {
@@ -478,7 +491,7 @@ PipelineState PostProcessManager::getPipelineState(
mPerRenderableDslh,
ma->getDescriptorSetLayout().getHandle()
}},
.rasterState = ma->getRasterState()
.rasterState = mi->getRasterState()
};
}
@@ -518,8 +531,7 @@ void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver,
PostProcessVariant const variant) const noexcept {
mi->commit(driver, getUboManager());
mi->use(driver);
FMaterial const* const ma = mi->getMaterial();
PipelineState const pipeline = getPipelineState(ma, variant);
PipelineState const pipeline = getPipelineState(mi, variant);
assert_invariant(
((out.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH)
@@ -636,12 +648,12 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
auto in = resources.getTexture(data.depth);
auto& material = getPostProcessMaterial("mipmapDepth");
FMaterial const* const ma = material.getMaterial(mEngine, driver);
FMaterialInstance* const mi = getMaterialInstance(ma);
FMaterial const* const ma = material.getMaterial(mEngine);
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
// Only the depth texture is changing in the material instance (no UBO updates),
// we do not move getMaterialInstance() inside the loop.
auto pipeline = getPipelineState(ma);
auto pipeline = getPipelineState(mi);
// The first mip already exists, so we process n-1 lods
for (size_t level = 0; level < levelCount - 1; level++) {
@@ -1001,14 +1013,13 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
#endif
auto& material = getPostProcessMaterial(materialName);
FMaterial* ma = material.getMaterial(mEngine, driver);
FMaterial* ma = material.getMaterial(mEngine);
ma->getPrograms().setConstants({
{ "useVisibilityBitmasks", options.gtao.useVisibilityBitmasks },
{ "linearThickness", options.gtao.linearThickness },
});
ma = material.getMaterial(mEngine, driver);
FMaterialInstance* const mi = getMaterialInstance(ma);
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
// Set AO type specific material parameters
switch (aoType) {
@@ -1084,7 +1095,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
mi->commit(driver, getUboManager());
mi->use(driver);
auto pipeline = getPipelineState(ma);
auto pipeline = getPipelineState(mi);
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
assert_invariant(ssao.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH);
renderFullScreenQuad(ssao, pipeline, driver);
@@ -1194,8 +1205,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
auto& material = config.bentNormals ?
getPostProcessMaterial("bilateralBlurBentNormals") :
getPostProcessMaterial("bilateralBlur");
FMaterial const* const ma = material.getMaterial(mEngine, driver);
FMaterialInstance* const mi = getMaterialInstance(ma);
FMaterial const* const ma = material.getMaterial(mEngine);
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
mi->setParameter("ssao", ssao, { /* only reads level 0 */ });
mi->setParameter("axis", axis / float2{desc.width, desc.height});
mi->setParameter("kernel", kGaussianSamples, kGaussianCount);
@@ -1205,7 +1216,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
mi->commit(driver, getUboManager());
mi->use(driver);
auto pipeline = getPipelineState(ma);
auto pipeline = getPipelineState(mi);
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
renderFullScreenQuad(blurred, pipeline, driver);
unbindAllDescriptorSets(driver);
@@ -1364,7 +1375,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
"separableGaussianBlur4L"sv : "separableGaussianBlur4"sv; break;
}
auto const& separableGaussianBlur = getPostProcessMaterial(materialName);
auto ma = separableGaussianBlur.getMaterial(mEngine, driver);
auto ma = separableGaussianBlur.getMaterial(mEngine);
const size_t kernelStorageSize = ma->reflect("kernel")->size;
float2 kernel[64];
@@ -1887,9 +1898,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
auto inOutCoc = resources.getTexture(data.inOutCoc);
auto const& material = getPostProcessMaterial("dofMipmap");
FMaterial const* const ma = material.getMaterial(mEngine, driver);
FMaterial const* const ma = material.getMaterial(mEngine);
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
auto const pipeline = getPipelineState(ma, variant);
auto const pipeline = getPipelineState(mi, variant);
for (size_t level = 0 ; level < mipmapCount - 1u ; level++) {
const float w = FTexture::valueForLevel(level, desc.width);
@@ -1897,7 +1909,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
auto const& out = resources.getRenderPassInfo(data.rp[level]);
auto inColor = driver.createTextureView(inOutColor, level, 1);
auto inCoc = driver.createTextureView(inOutCoc, level, 1);
FMaterialInstance* const mi = getMaterialInstance(ma);
// FIXME: is this necessary?
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
mi->setParameter("color", inColor, SamplerParams{
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
@@ -2426,9 +2439,9 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
auto const& outDesc = resources.getDescriptor(data.out);
auto const& material = getPostProcessMaterial("bloomUpsample");
FMaterial const* const ma = material.getMaterial(mEngine, driver);
FMaterial const* const ma = material.getMaterial(mEngine);
auto pipeline = getPipelineState(ma);
auto pipeline = getPipelineState(getMaterialInstance(driver, ma));
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
@@ -2436,7 +2449,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
// Note that we wouldn't want to use the same instance for each pass since that
// would imply using the same UBOs, which implies synchronization across the
// passes.
FMaterialInstance* mi = getMaterialInstance(ma);
FMaterialInstance* mi = getMaterialInstance(driver, ma);
auto hwDstRT = resources.getRenderPassInfo(data.outRT[i - 1]);
hwDstRT.params.flags.discardStart = TargetBufferFlags::NONE; // b/c we'll blend
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
@@ -2569,11 +2582,12 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver,
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
auto const& material = getPostProcessMaterial("colorGradingAsSubpass");
FMaterial const* const ma = material.getMaterial(mEngine, driver, variant);
FMaterial const* const ma = material.getMaterial(mEngine);
// the UBO has been set and committed in colorGradingPrepareSubpass()
FMaterialInstance const* mi = mMaterialInstanceManager.getMaterialInstance(ma, colorGradingConfig.translucent);
FMaterialInstance const* mi =
getMaterialInstanceWithTag(driver, ma, colorGradingConfig.translucent, variant);
mi->use(driver);
auto const pipeline = getPipelineState(ma, variant);
auto const pipeline = getPipelineState(mi, variant);
driver.nextSubpass();
driver.scissor(mi->getScissor());
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
@@ -2581,8 +2595,8 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver,
void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomResolveOp const op) noexcept {
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
auto const ma = material.getMaterial(mEngine, driver, PostProcessVariant::OPAQUE);
auto* const mi = mMaterialInstanceManager.getMaterialInstance(ma, 0);
auto const ma = material.getMaterial(mEngine);
auto* const mi = getMaterialInstance(driver, ma, 0);
mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f),
mi->commit(driver, getUboManager());
}
@@ -2592,12 +2606,12 @@ void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
bindPerRenderableDescriptorSet(driver);
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
FMaterial const* const ma = material.getMaterial(mEngine, driver);
FMaterial const* const ma = material.getMaterial(mEngine);
// the UBO has been set and committed in customResolvePrepareSubpass()
FMaterialInstance const* mi = mMaterialInstanceManager.getMaterialInstance(ma, 0);
FMaterialInstance const* mi = getMaterialInstance(driver, ma, 0);
mi->use(driver);
auto const pipeline = getPipelineState(ma);
auto const pipeline = getPipelineState(mi);
driver.nextSubpass();
driver.scissor(mi->getScissor());
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
@@ -2633,8 +2647,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::customResolveUncompressPass(
void PostProcessManager::clearAncillaryBuffersPrepare(DriverApi& driver,
Variant::type_t variant) noexcept {
auto const& material = getPostProcessMaterial("clearDepth");
auto const ma = material.getMaterial(mEngine, driver, variant);
auto const mi = mMaterialInstanceManager.getMaterialInstance(ma, 0);
auto const ma = material.getMaterial(mEngine);
auto const mi = getMaterialInstanceWithTag(driver, ma, 0, variant);
mi->commit(driver, getUboManager());
}
@@ -2649,13 +2663,13 @@ void PostProcessManager::clearAncillaryBuffers(DriverApi& driver,
bindPerRenderableDescriptorSet(driver);
auto const& material = getPostProcessMaterial("clearDepth");
FMaterial const* const ma = material.getMaterial(mEngine, driver, variant);
FMaterial const* const ma = material.getMaterial(mEngine);
// the UBO has been set and committed in clearAncillaryBuffersPrepare()
FMaterialInstance const* const mi = mMaterialInstanceManager.getMaterialInstance(ma, 0);
FMaterialInstance const* const mi = getMaterialInstanceWithTag(driver, ma, 0, variant);
mi->use(driver);
auto pipeline = getPipelineState(ma, variant);
auto pipeline = getPipelineState(mi, variant);
pipeline.rasterState.depthFunc = RasterState::DepthFunc::A;
driver.scissor(mi->getScissor());
@@ -2665,7 +2679,7 @@ void PostProcessManager::clearAncillaryBuffers(DriverApi& driver,
void PostProcessManager::fogPrepare(DriverApi& driver) noexcept {
// ensures the material is loaded and material instance created
auto const& material = getPostProcessMaterial("fog");
FMaterial const* const ma = material.getMaterial(mEngine, driver, PostProcessVariant::OPAQUE);
FMaterial const* const ma = material.getMaterial(mEngine);
FMaterialInstance const* mi = ma->getDefaultInstance();
mi->commit(driver, getUboManager());
}
@@ -2676,11 +2690,11 @@ void PostProcessManager::fog(DriverApi& driver) noexcept {
bindPerRenderableDescriptorSet(driver);
auto const& material = getPostProcessMaterial("fog");
FMaterial const* const ma = material.getMaterial(mEngine, driver);
FMaterial const* const ma = material.getMaterial(mEngine);
FMaterialInstance const* mi = ma->getDefaultInstance();
mi->use(driver);
auto pipeline = getPipelineState(ma, Variant::NO_VARIANT);
auto pipeline = getPipelineState(mi, Variant::NO_VARIANT);
driver.scissor(mi->getScissor());
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
}
@@ -2937,7 +2951,7 @@ void PostProcessManager::TaaJitterCamera(
void PostProcessManager::configureTemporalAntiAliasingMaterial(backend::DriverApi& driver,
TemporalAntiAliasingOptions const& taaOptions) noexcept {
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine, driver);
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine);
ma->getPrograms().setConstants({
{ "upscaling", taaOptions.upscaling > 1.0f },
{ "historyReprojection", taaOptions.historyReprojection },
@@ -2956,7 +2970,7 @@ FMaterialInstance* PostProcessManager::configureColorGradingMaterial(backend::Dr
PostProcessMaterial const& material, FColorGrading const* colorGrading,
ColorGradingConfig const& colorGradingConfig, VignetteOptions const& vignetteOptions,
uint32_t const width, uint32_t const height) noexcept {
FMaterial* ma = material.getMaterial(mEngine, driver);
FMaterial* ma = material.getMaterial(mEngine);
ma->getPrograms().setConstants({
{ "isOneDimensional", colorGrading->isOneDimensional() },
{ "isLDR", colorGrading->isLDR() },
@@ -2965,8 +2979,9 @@ FMaterialInstance* PostProcessManager::configureColorGradingMaterial(backend::Dr
PostProcessVariant const variant = colorGradingConfig.translucent
? PostProcessVariant::TRANSLUCENT
: PostProcessVariant::OPAQUE;
ma = material.getMaterial(mEngine, driver, variant);
FMaterialInstance* mi = mMaterialInstanceManager.getMaterialInstance(ma, colorGradingConfig.translucent);
ma = material.getMaterial(mEngine);
FMaterialInstance* mi =
getMaterialInstanceWithTag(driver, ma, colorGradingConfig.translucent, variant);
const SamplerParams params = SamplerParams{
.filterMag = SamplerMagFilter::LINEAR,
@@ -3078,9 +3093,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
PostProcessVariant const variant = colorGradingConfig.translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
FMaterial const* const ma = material.getMaterial(mEngine, driver, variant);
FMaterial const* const ma = material.getMaterial(mEngine);
FMaterialInstance* mi = getMaterialInstance(ma);
FMaterialInstance* mi = getMaterialInstance(driver, ma);
mi->setParameter("color", color, SamplerParams{}); // nearest
mi->setParameter("depth", depth, SamplerParams{}); // nearest
mi->setParameter("history", history, SamplerParams{
@@ -3120,7 +3135,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
if (colorGradingConfig.asSubpass) {
out.params.subpassMask = 1;
}
auto const pipeline = getPipelineState(ma, variant);
auto const pipeline = getPipelineState(mi, variant);
driver.beginRenderPass(out.target, out.params);
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
@@ -3202,7 +3217,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
mi->commit(driver, getUboManager());
mi->use(driver);
auto pipeline = getPipelineState(material.getMaterial(mEngine, driver), variant);
auto pipeline = getPipelineState(mi, variant);
if (mode == RcasMode::BLENDED) {
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
@@ -3295,7 +3310,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
auto out = resources.getRenderPassInfo();
auto pipeline = getPipelineState(material.getMaterial(mEngine, driver));
auto pipeline = getPipelineState(mi);
if (blended) {
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
@@ -3509,15 +3524,15 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
auto out = resources.getRenderPassInfo();
if (UTILS_UNLIKELY(twoPassesEASU)) {
auto pipeline0 = getPipelineState(splitEasuMaterial->getMaterial(mEngine, driver));
auto pipeline1 = getPipelineState(easuMaterial->getMaterial(mEngine, driver));
auto pipeline0 = getPipelineState(getMaterialInstance(mEngine, driver, *splitEasuMaterial));
auto pipeline1 = getPipelineState(getMaterialInstance(mEngine, driver, *easuMaterial));
pipeline1.rasterState.depthFunc = SamplerCompareFunc::NE;
driver.beginRenderPass(out.target, out.params);
driver.draw(pipeline0, mFullScreenQuadRph, 0, 3, 1);
driver.draw(pipeline1, mFullScreenQuadRph, 0, 3, 1);
driver.endRenderPass();
} else {
auto pipeline = getPipelineState(easuMaterial->getMaterial(mEngine, driver));
auto pipeline = getPipelineState(getMaterialInstance(mEngine, driver, *easuMaterial));
renderFullScreenQuad(out, pipeline, driver);
}
unbindAllDescriptorSets(driver);
@@ -3569,8 +3584,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
PostProcessMaterial const& material =
getPostProcessMaterial(layer ? "blitArray" : "blitLow");
FMaterial const* const ma = material.getMaterial(mEngine, driver);
auto* mi = getMaterialInstance(ma);
FMaterial const* const ma = material.getMaterial(mEngine);
auto* mi = getMaterialInstance(driver, ma);
mi->setParameter("color", color, SamplerParams{
.filterMag = filterMag,
.filterMin = filterMin
@@ -3588,7 +3603,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
mi->commit(driver, getUboManager());
mi->use(driver);
auto pipeline = getPipelineState(ma);
auto pipeline = getPipelineState(mi);
if (translucent) {
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
@@ -3883,10 +3898,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
// set uniforms
PostProcessMaterial const& material = getPostProcessMaterial("blitArray");
FMaterial const* const ma = material.getMaterial(mEngine, driver);
FMaterial const* const ma = material.getMaterial(mEngine);
// It should be ok to not move this getMaterialInstance to inside the loop, since
// this is a pass meant for debug.
auto* mi = getMaterialInstance(ma);
auto* mi = getMaterialInstance(driver, ma);
mi->setParameter("color", color, SamplerParams{
.filterMag = filterMag,
.filterMin = filterMin
@@ -3900,7 +3915,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
mi->commit(driver, getUboManager());
mi->use(driver);
auto pipeline = getPipelineState(ma);
auto pipeline = getPipelineState(mi);
if (translucent) {
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
+40 -25
View File
@@ -356,13 +356,7 @@ public:
void terminate(FEngine& engine) noexcept;
FMaterial* getMaterial(FEngine& engine, backend::DriverApi& driver,
Variant::type_t variant) const noexcept;
FMaterial* getMaterial(FEngine& engine, backend::DriverApi& driver,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept {
return getMaterial(engine, driver, Variant::type_t(variant));
}
FMaterial* getMaterial(FEngine& engine) const noexcept;
private:
void loadMaterial(FEngine& engine) const noexcept;
@@ -389,11 +383,12 @@ public:
void bindPerRenderableDescriptorSet(backend::DriverApi& driver) const noexcept;
backend::PipelineState getPipelineState(FMaterial const* ma, Variant::type_t variant) const noexcept;
backend::PipelineState getPipelineState(FMaterialInstance const* mi,
Variant::type_t variant) const noexcept;
backend::PipelineState getPipelineState(FMaterial const* ma,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept {
return getPipelineState(ma, Variant::type_t(variant));
backend::PipelineState getPipelineState(FMaterialInstance const* mi,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept {
return getPipelineState(mi, Variant::type_t(variant));
}
void renderFullScreenQuad(FrameGraphResources::RenderPassInfo const& out,
@@ -423,27 +418,47 @@ public:
void resetForRender();
MaterialInstanceManager& getMaterialInstanceManager() noexcept {
return mMaterialInstanceManager;
}
// Helper to get a MaterialInstance from a FMaterial
// This currently just call FMaterial::getDefaultInstance().
FMaterialInstance* getMaterialInstance(FMaterial const* ma) {
return mMaterialInstanceManager.getMaterialInstance(ma);
}
// Helper to get a MaterialInstance from a PostProcessMaterial.
FMaterialInstance* getMaterialInstance(FEngine& engine, backend::DriverApi& driver,
PostProcessMaterial const& material, PostProcessVariant variant = PostProcessVariant::OPAQUE) {
FMaterial const* ma = material.getMaterial(engine, driver, variant);
return getMaterialInstance(ma);
}
static void unbindAllDescriptorSets(backend::DriverApi& driver) noexcept;
private:
// Helpers to get MaterialInstances.
//
// These funcions additionally ensure that the necessary shader programs are compiled via
// prepareProgram().
FMaterialInstance* getMaterialInstance(backend::DriverApi& driver, FMaterial const* ma,
Variant::type_t variant) const;
FMaterialInstance* getMaterialInstance(backend::DriverApi& driver, FMaterial const* ma,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const {
return getMaterialInstance(driver, ma, Variant::type_t(variant));
}
FMaterialInstance* getMaterialInstance(FEngine& engine, backend::DriverApi& driver,
PostProcessMaterial const& material,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const {
return getMaterialInstance(driver, material.getMaterial(engine), Variant::type_t(variant));
}
FMaterialInstance* getMaterialInstanceWithTag(backend::DriverApi& driver, FMaterial const* ma,
uint32_t tag, Variant::type_t variant) const;
FMaterialInstance* getMaterialInstanceWithTag(backend::DriverApi& driver, FMaterial const* ma,
uint32_t tag, PostProcessVariant variant = PostProcessVariant::OPAQUE) const {
return getMaterialInstanceWithTag(driver, ma, tag, Variant::type_t(variant));
}
FMaterialInstance* getMaterialInstanceWithTag(FEngine& engine, backend::DriverApi& driver,
PostProcessMaterial const& material, uint32_t tag,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const {
return getMaterialInstanceWithTag(driver, material.getMaterial(engine), tag,
Variant::type_t(variant));
}
UboManager* getUboManager() const noexcept;
backend::RenderPrimitiveHandle mFullScreenQuadRph;
+7 -10
View File
@@ -243,8 +243,8 @@ void RenderPass::appendCommands(FEngine const& engine, backend::DriverApi& drive
// This must be done from the main thread.
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
auto ma = first->info.mi->getMaterial();
ma->prepareProgram(driver, first->info.materialVariant, CompilerPriorityQueue::CRITICAL);
first->info.mi->prepareProgram(driver, first->info.materialVariant,
CompilerPriorityQueue::CRITICAL);
}
}
}
@@ -426,16 +426,18 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
bool const isBlendingCommand = !hasScreenSpaceRefraction &&
(blendingMode != BlendingMode::OPAQUE && blendingMode != BlendingMode::MASKED);
RasterState rasterState = mi->getRasterState();
uint64_t keyDraw = cmdDraw.key;
keyDraw &= ~(PASS_MASK | BLENDING_MASK | MATERIAL_MASK);
keyDraw |= uint64_t(hasScreenSpaceRefraction ? Pass::REFRACT : Pass::COLOR);
keyDraw |= uint64_t(CustomCommand::PASS);
keyDraw |= mi->getSortingKey(); // already all set-up for direct or'ing
keyDraw |= makeField(variant.key, MATERIAL_VARIANT_KEY_MASK, MATERIAL_VARIANT_KEY_SHIFT);
keyDraw |= makeField(ma->getRasterState().alphaToCoverage, BLENDING_MASK, BLENDING_SHIFT);
keyDraw |= makeField(rasterState.alphaToCoverage, BLENDING_MASK, BLENDING_SHIFT);
cmdDraw.key = isBlendingCommand ? keyBlending : keyDraw;
cmdDraw.info.rasterState = ma->getRasterState();
cmdDraw.info.rasterState = rasterState;
// for SSR pass, the blending mode of opaques (including MASKED) must be off
// see Material.cpp.
@@ -446,10 +448,6 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
BlendFunction::ZERO : cmdDraw.info.rasterState.blendFunctionDstAlpha;
cmdDraw.info.rasterState.inverseFrontFaces = inverseFrontFaces;
cmdDraw.info.rasterState.culling = mi->getCullingMode();
cmdDraw.info.rasterState.colorWrite = mi->isColorWriteEnabled();
cmdDraw.info.rasterState.depthWrite = mi->isDepthWriteEnabled();
cmdDraw.info.rasterState.depthFunc = mi->getDepthFunc();
cmdDraw.info.rasterState.depthClamp = hasDepthClamp;
cmdDraw.info.materialVariant = variant;
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
@@ -1090,8 +1088,7 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
mi->use(driver, info.materialVariant);
}
assert_invariant(ma);
pipeline.program = ma->getProgram(info.materialVariant);
pipeline.program = mi->getProgram(info.materialVariant);
if (UTILS_UNLIKELY(memcmp(&pipeline, &currentPipeline, sizeof(PipelineState)) != 0)) {
currentPipeline = pipeline;
+5 -5
View File
@@ -636,7 +636,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::gaussianBlurSeparatedPass(
// get the material
auto const& separableGaussianBlur = ppm.getPostProcessMaterial("gaussian");
auto const ma = separableGaussianBlur.getMaterial(engine, driver);
auto const ma = separableGaussianBlur.getMaterial(engine);
// Generates half of a Gaussian kernel (center + one side)
auto generateGaussianWeights = [](std::array<float, 32>& weights,
@@ -685,7 +685,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::gaussianBlurSeparatedPass(
for (auto const mi : miList) {
mi->use(driver);
driver.scissor(mi->getScissor());
driver.draw(ppm.getPipelineState(ma), engine.getFullScreenRenderPrimitive(), 0, 3, 1);
driver.draw(ppm.getPipelineState(mi), engine.getFullScreenRenderPrimitive(), 0, 3, 1);
}
driver.endRenderPass();
@@ -733,12 +733,12 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::vsmMipmapPass(
ppm.bindPerRenderableDescriptorSet(driver);
auto& material = ppm.getPostProcessMaterial("vsmMipmap");
FMaterial const* const ma = material.getMaterial(engine, driver);
FMaterial const* const ma = material.getMaterial(engine);
auto const mi = ppm.getMaterialInstanceManager().getMaterialInstance(ma);
auto const pipeline = ppm.getPipelineState(ma);
auto const pipeline = ppm.getPipelineState(mi);
backend::Viewport const scissor = { 0, 0, dim, dim };
auto const mi = ppm.getMaterialInstanceManager().getMaterialInstance(ma);
mi->setParameter("color", in, SamplerParams{
.filterMag = SamplerMagFilter::NEAREST,
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST
+2 -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,
@@ -1755,7 +1756,7 @@ void FEngine::compile(
CallbackHandler* handler,
Invocable<void(Material*)>&& callback) {
auto const variants = getMaterialCompileVariants(view, shadowReceiver, skinning);
material->compile(priority, variants, handler, std::move(callback));
const_cast<FMaterial*>(material)->compile(priority, variants, handler, std::move(callback));
}
// ------------------------------------------------------------------------------------------------
+15 -28
View File
@@ -170,8 +170,6 @@ FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition
DriverApi& driver = engine.getDriverApi();
mIsStereoSupported = driver.isStereoSupported();
mIsParallelShaderCompileSupported = driver.isParallelShaderCompileSupported();
mDepthPrecacheDisabled =
driver.isWorkaroundNeeded(Workaround::DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL);
mDefaultMaterial = engine.getDefaultMaterial();
@@ -240,44 +238,33 @@ filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
void FMaterial::compile(CompilerPriorityQueue const priority,
UserVariantFilterMask variantSpec,
CallbackHandler* handler,
Invocable<void(Material*)>&& callback) const noexcept {
DriverApi& driver = mEngine.getDriverApi();
// Turn off the STE variant if stereo is not supported.
if (!mIsStereoSupported) {
variantSpec &= ~UserVariantFilterMask(UserVariantFilterBit::STE);
Invocable<void(Material*)>&& callback) noexcept {
FMaterialInstance* mi = getDefaultInstance();
if (callback) {
mi->compile(priority, variantSpec, handler,
[this, callback = std::move(callback)](MaterialInstance*) {
callback(this);
});
} else {
mi->compile(priority, variantSpec, handler, {});
}
UserVariantFilterMask const variantFilter = ~variantSpec & UserVariantFilterMask(UserVariantFilterBit::ALL);
ShaderModel const shaderModel = mEngine.getShaderModel();
bool const isStereoSupported = mEngine.getDriverApi().isStereoSupported();
if (UTILS_LIKELY(mIsParallelShaderCompileSupported)) {
for (auto const variant: mDefinition.getVariants()) {
if (!variantFilter || variant == Variant::filterUserVariant(variant, variantFilter)) {
if (mDefinition.hasVariant(variant, shaderModel, isStereoSupported)) {
prepareProgram(driver, variant, priority);
}
}
}
}
compileAllPrograms(priority, handler, std::move(callback));
}
void FMaterial::compile(CompilerPriorityQueue const priority,
FixedCapacityVector<Variant> const& variants,
CallbackHandler* handler,
Invocable<void(Material*)>&& callback) const noexcept {
Invocable<void(Material*)>&& callback) noexcept {
DriverApi& driver = mEngine.getDriverApi();
FMaterialInstance* mi = getDefaultInstance();
ShaderModel const shaderModel = mEngine.getShaderModel();
bool const isStereoSupported = driver.isStereoSupported();
bool const isParallelShaderCompileSupported = driver.isParallelShaderCompileSupported();
if (UTILS_LIKELY(mIsParallelShaderCompileSupported)) {
if (UTILS_LIKELY(isParallelShaderCompileSupported)) {
for (auto const variant : variants) {
if (mDefinition.hasVariant(variant, shaderModel, isStereoSupported)) {
prepareProgram(driver, variant, priority);
mi->prepareProgram(driver, variant, priority);
}
}
}
@@ -287,7 +274,7 @@ void FMaterial::compile(CompilerPriorityQueue const priority,
void FMaterial::compileAllPrograms(CompilerPriorityQueue const priority,
CallbackHandler* handler,
Invocable<void(Material*)>&& callback) const noexcept {
Invocable<void(Material*)>&& callback) noexcept {
DriverApi& driver = mEngine.getDriverApi();
if (callback) {
+6 -41
View File
@@ -106,12 +106,12 @@ public:
void compile(CompilerPriorityQueue priority,
UserVariantFilterMask variantSpec,
backend::CallbackHandler* handler,
utils::Invocable<void(Material*)>&& callback) const noexcept;
utils::Invocable<void(Material*)>&& callback) noexcept;
void compile(CompilerPriorityQueue priority,
utils::FixedCapacityVector<Variant> const& variants,
backend::CallbackHandler* handler,
utils::Invocable<void(Material*)>&& callback) const noexcept;
utils::Invocable<void(Material*)>&& callback) noexcept;
// Creates an instance of this material, specifying the batching mode.
FMaterialInstance* createInstance(const char* name) const noexcept;
@@ -130,40 +130,6 @@ public:
FEngine& getEngine() const noexcept { return mEngine; }
// prepareProgram creates the program for the material's given variant at the backend level.
// Must be called outside of backend render pass.
// Must be called before getProgram() below.
backend::Handle<backend::HwProgram> prepareProgram(backend::DriverApi& driver,
Variant const variant,
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
return mPrograms.prepareProgram(driver, variant, priorityQueue);
}
// getProgram returns the backend program for the material's given variant.
// Must be called after prepareProgram().
[[nodiscard]]
backend::Handle<backend::HwProgram> getProgram(Variant variant) const noexcept {
if (UTILS_UNLIKELY(mEngine.features.material.enable_fog_as_postprocess)) {
// if the fog as post-process feature is enabled, we need to proceed "as-if" the material
// didn't have the FOG variant bit.
if (getMaterialDomain() == MaterialDomain::SURFACE) {
BlendingMode const blendingMode = getBlendingMode();
bool const hasScreenSpaceRefraction = getRefractionMode() == RefractionMode::SCREEN_SPACE;
bool const isBlendingCommand = !hasScreenSpaceRefraction &&
(blendingMode != BlendingMode::OPAQUE && blendingMode != BlendingMode::MASKED);
if (!isBlendingCommand) {
variant.setFog(false);
}
}
}
#if FILAMENT_ENABLE_MATDBG
updateActiveProgramsForMatdbg(variant);
#endif
return mPrograms.getProgram(variant);
}
// MaterialInstance::use() binds descriptor sets before drawing. For shared variants,
// however, the material instance will call useShared() to bind the default material's sets
// instead.
@@ -310,6 +276,9 @@ public:
}
/** @}*/
// Called by getProgram() to update active program list for matdbg UI.
void updateActiveProgramsForMatdbg(Variant const variant) const noexcept;
#endif
private:
@@ -318,15 +287,13 @@ private:
void compileAllPrograms(CompilerPriorityQueue priority,
backend::CallbackHandler* handler,
utils::Invocable<void(Material*)>&& callback) const noexcept;
utils::Invocable<void(Material*)>&& callback) noexcept;
MaterialDefinition const& mDefinition;
bool mIsDefaultMaterial = false;
bool mUseUboBatching = false;
bool mIsStereoSupported = false;
bool mIsParallelShaderCompileSupported = false;
bool mDepthPrecacheDisabled = false;
FMaterial const* mDefaultMaterial = nullptr;
@@ -341,8 +308,6 @@ private:
mutable utils::Mutex mPendingEditsLock;
std::unique_ptr<MaterialParser> mPendingEdits;
std::unique_ptr<MaterialParser> mEditedMaterialParser;
// Called by getProgram() to update active program list for matdbg UI.
void updateActiveProgramsForMatdbg(Variant const variant) const noexcept;
void setPendingEdits(std::unique_ptr<MaterialParser> pendingEdits) noexcept;
bool hasPendingEdits() const noexcept;
void latchPendingEdits() noexcept;
+125
View File
@@ -17,6 +17,7 @@
#include <filament/MaterialInstance.h>
#include "RenderPass.h"
#include "MaterialParser.h"
#include "ds/DescriptorSetLayout.h"
@@ -131,6 +132,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mTextureParameters(other->mTextureParameters),
mDescriptorSet(other->mDescriptorSet.duplicate(
"MaterialInstance", mMaterial->getDescriptorSetLayout())),
mPrograms(other->mPrograms),
mPolygonOffset(other->mPolygonOffset),
mStencilState(other->mStencilState),
mMaskThreshold(other->mMaskThreshold),
@@ -207,6 +209,10 @@ void FMaterialInstance::terminate(FEngine& engine) {
if (ubHandle){
driver.destroyBufferObject(*ubHandle);
}
if (mPrograms.isInitialized()) {
mPrograms.terminate(engine);
}
}
void FMaterialInstance::commit(FEngine& engine) const {
@@ -259,6 +265,39 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
// ------------------------------------------------------------------------------------------------
template<typename T>
void FMaterialInstance::setConstantImpl(std::string_view name, T value) {
auto const& constants = mMaterial->getDefinition().specializationConstantsNameToIndex;
auto it = constants.find(name);
FILAMENT_CHECK_PRECONDITION(it != constants.end()) << "Constant " << name << " does not exist";
if (UTILS_UNLIKELY(mPendingSpecializationConstants.empty())) {
mPendingSpecializationConstants =
FixedCapacityVector<backend::Program::SpecializationConstant>(
getPrograms().getSpecializationConstants());
}
uint32_t id = it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
mPendingSpecializationConstants[id] = value;
}
template<typename T>
T FMaterialInstance::getConstantImpl(std::string_view name) const {
auto const& constants = mMaterial->getDefinition().specializationConstantsNameToIndex;
auto it = constants.find(name);
FILAMENT_CHECK_PRECONDITION(it != constants.end()) << "Constant " << name << " does not exist";
uint32_t id = it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
if (UTILS_UNLIKELY(!mPendingSpecializationConstants.empty())) {
return std::get<T>(mPendingSpecializationConstants[id]);
}
return getPrograms().getConstant<T>(id);
}
// ------------------------------------------------------------------------------------------------
void FMaterialInstance::setParameter(std::string_view const name,
Handle<HwTexture> texture, SamplerParams const params) {
auto const binding = mMaterial->getSamplerBinding(name);
@@ -378,6 +417,15 @@ void FMaterialInstance::setTransparencyMode(TransparencyMode const mode) noexcep
mTransparencyMode = mode;
}
RasterState FMaterialInstance::getRasterState() const noexcept {
RasterState rs = mMaterial->getRasterState();
rs.culling = mCulling;
rs.depthWrite = mDepthWrite;
rs.depthFunc = mDepthFunc;
rs.colorWrite = mColorWrite;
return rs;
}
void FMaterialInstance::setDepthCulling(bool const enable) noexcept {
mDepthFunc = enable ? RasterState::DepthFunc::GE : RasterState::DepthFunc::A;
}
@@ -398,6 +446,51 @@ const char* FMaterialInstance::getName() const noexcept {
// ------------------------------------------------------------------------------------------------
void FMaterialInstance::compile(CompilerPriorityQueue const priority,
UserVariantFilterMask variantSpec, CallbackHandler* handler,
Invocable<void(MaterialInstance*)>&& callback) noexcept {
FEngine& engine = mMaterial->getEngine();
DriverApi& driver = engine.getDriverApi();
MaterialDefinition const& definition = mMaterial->getDefinition();
bool const isStereoSupported = driver.isStereoSupported();
// Turn off the STE variant if stereo is not supported.
if (UTILS_LIKELY(!isStereoSupported)) {
variantSpec &= ~UserVariantFilterMask(UserVariantFilterBit::STE);
}
UserVariantFilterMask const variantFilter =
~variantSpec & UserVariantFilterMask(UserVariantFilterBit::ALL);
ShaderModel const shaderModel = engine.getShaderModel();
if (UTILS_LIKELY(driver.isParallelShaderCompileSupported())) {
for (auto const variant: definition.getVariants()) {
if (!variantFilter || variant == Variant::filterUserVariant(variant, variantFilter)) {
if (definition.hasVariant(variant, shaderModel, isStereoSupported)) {
prepareProgram(driver, variant, priority);
}
}
}
}
if (callback) {
struct Callback {
Invocable<void(MaterialInstance*)> f;
MaterialInstance* m;
static void func(void* user) {
auto* const c = static_cast<Callback*>(user);
c->f(c->m);
delete c;
}
};
auto* const user = new (std::nothrow) Callback{ std::move(callback), this };
driver.compilePrograms(priority, handler, &Callback::func, user);
} else {
driver.compilePrograms(priority, nullptr, nullptr, nullptr);
}
}
void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
assert_invariant(mDescriptorSet.getHandle());
assert_invariant(!isUsingUboBatching() || BufferAllocator::isValid(getAllocationId()));
@@ -502,4 +595,36 @@ void FMaterialInstance::fixMissingSamplers() const {
}
}
LocalProgramCache const& FMaterialInstance::getPrograms() const noexcept {
return mPrograms.isInitialized() ? mPrograms : mMaterial->getPrograms();
}
void FMaterialInstance::flushSpecializationConstants() const noexcept {
if (mPendingSpecializationConstants.empty()) {
return;
}
if (!mPrograms.isInitialized()) {
mPrograms.initializeForMaterialInstance(mMaterial->getEngine(), *mMaterial);
}
mPrograms.setConstants(std::move(mPendingSpecializationConstants));
mPendingSpecializationConstants.clear();
}
#if FILAMENT_ENABLE_MATDBG
void FMaterialInstance::updateActiveProgramsForMatdbg(Variant const variant) const noexcept {
mMaterial->updateActiveProgramsForMatdbg(variant);
}
#endif // FILAMENT_ENABLE_MATDBG
template void FMaterialInstance::setConstantImpl<int32_t>(std::string_view name, int32_t value);
template void FMaterialInstance::setConstantImpl<float>(std::string_view name, float value);
template void FMaterialInstance::setConstantImpl<bool>(std::string_view name, bool value);
template int32_t FMaterialInstance::getConstantImpl<int32_t>(std::string_view name) const;
template float FMaterialInstance::getConstantImpl<float>(std::string_view name) const;
template bool FMaterialInstance::getConstantImpl<bool>(std::string_view name) const;
} // namespace filament
+52 -4
View File
@@ -18,7 +18,7 @@
#define TNT_FILAMENT_DETAILS_MATERIALINSTANCE_H
#include "downcast.h"
#include "LocalProgramCache.h"
#include "UniformBuffer.h"
#include "ds/DescriptorSet.h"
@@ -26,8 +26,6 @@
#include "details/BufferAllocator.h"
#include "details/Engine.h"
#include "private/backend/DriverApi.h"
#include <filament/MaterialInstance.h>
#include <private/filament/Variant.h>
@@ -67,7 +65,7 @@ public:
~FMaterialInstance() noexcept;
void terminate(FEngine& engine);
void commit(FEngine& engine) const;
void commit(FEngine::DriverApi& driver, UboManager* uboManager) const;
@@ -86,6 +84,32 @@ public:
UniformBuffer const& getUniformBuffer() const noexcept { return mUniforms; }
void compile(backend::CompilerPriorityQueue priority, UserVariantFilterMask variantSpec,
backend::CallbackHandler* handler,
utils::Invocable<void(MaterialInstance*)>&& callback) noexcept;
// prepareProgram creates the program for the material's given variant at the backend level.
// Must be called outside of backend render pass.
// Must be called before getProgram() below.
backend::Handle<backend::HwProgram> prepareProgram(backend::DriverApi& driver,
Variant const variant,
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
flushSpecializationConstants();
return getPrograms().prepareProgram(driver, variant, priorityQueue);
}
// getProgram returns the backend program for the material's given variant.
// Must be called after prepareProgram().
//
// See also Material::getProgram().
[[nodiscard]]
backend::Handle<backend::HwProgram> getProgram(Variant const variant) const noexcept {
#if FILAMENT_ENABLE_MATDBG
updateActiveProgramsForMatdbg(variant);
#endif
return getPrograms().getProgram(variant);
}
void setScissor(uint32_t const left, uint32_t const bottom, uint32_t const width, uint32_t const height) noexcept {
constexpr uint32_t maxvalu = std::numeric_limits<int32_t>::max();
mScissorRect = { int32_t(left), int32_t(bottom),
@@ -107,6 +131,8 @@ public:
bool hasScissor() const noexcept { return mHasScissor; }
backend::RasterState getRasterState() const noexcept;
backend::CullingMode getCullingMode() const noexcept { return mCulling; }
backend::CullingMode getShadowCullingMode() const noexcept { return mShadowCulling; }
@@ -254,11 +280,15 @@ public:
backend::Handle<backend::HwTexture> texture, backend::SamplerParams params);
using MaterialInstance::setParameter;
using MaterialInstance::setConstant;
private:
friend class FMaterial;
friend class MaterialInstance;
// Cannot inline since it inspects the FMaterial class.
LocalProgramCache const& getPrograms() const noexcept;
template<size_t Size>
void setParameterUntypedImpl(std::string_view name, const void* value);
@@ -277,6 +307,19 @@ private:
template<typename T>
T getParameterImpl(std::string_view name) const;
template<typename T>
void setConstantImpl(std::string_view name, T value);
template<typename T>
T getConstantImpl(std::string_view name) const;
void flushSpecializationConstants() const noexcept;
#if FILAMENT_ENABLE_MATDBG
// Called by getProgram() to update active program list for matdbg UI.
void updateActiveProgramsForMatdbg(Variant const variant) const noexcept;
#endif
// keep these grouped, they're accessed together in the render-loop
FMaterial const* mMaterial = nullptr;
@@ -291,6 +334,11 @@ private:
mutable DescriptorSet mDescriptorSet;
UniformBuffer mUniforms;
// HACK: Mutable so that prepareProgram() can update specialization constants.
mutable LocalProgramCache mPrograms;
mutable utils::FixedCapacityVector<backend::Program::SpecializationConstant>
mPendingSpecializationConstants;
backend::PolygonOffset mPolygonOffset{};
backend::StencilState mStencilState{};
+2 -2
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
+1 -1
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;
+50
View File
@@ -19,6 +19,7 @@
#include <filament/Engine.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include "filament_test_resources.h"
@@ -167,3 +168,52 @@ TEST(Material, MaterialSettingInvalidApiLevelReturnsAnInvalidPackage) {
Engine::destroy(engine);
}
TEST(MaterialInstanceTest, SetConstant) {
Engine* engine = Engine::create(Engine::Backend::NOOP);
std::string shaderCode(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = vec4(1.0);
}
)");
filamat::MaterialBuilder builder;
builder.init();
builder.name("MaterialInstanceTest");
builder.material(shaderCode.c_str());
builder.constant("myFloat", filamat::MaterialBuilder::ConstantType::FLOAT, 1.0f);
builder.constant("myInt", filamat::MaterialBuilder::ConstantType::INT, 2);
builder.constant("myBool", filamat::MaterialBuilder::ConstantType::BOOL, false);
filamat::Package result = builder.build(engine->getJobSystem());
ASSERT_TRUE(result.isValid());
Material* material = Material::Builder()
.package(result.getData(), result.getSize())
.build(*engine);
ASSERT_NE(material, nullptr);
MaterialInstance* instance = material->createInstance();
ASSERT_NE(instance, nullptr);
// Verify default values
EXPECT_EQ(instance->getConstant<float>("myFloat"), 1.0f);
EXPECT_EQ(instance->getConstant<int32_t>("myInt"), 2);
EXPECT_EQ(instance->getConstant<bool>("myBool"), false);
// Set new values
instance->setConstant("myFloat", 3.0f);
instance->setConstant("myInt", 4);
instance->setConstant("myBool", true);
// Verify new values
EXPECT_EQ(instance->getConstant<float>("myFloat"), 3.0f);
EXPECT_EQ(instance->getConstant<int32_t>("myInt"), 4);
EXPECT_EQ(instance->getConstant<bool>("myBool"), true);
engine->destroy(instance);
engine->destroy(material);
Engine::destroy(engine);
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.71.0"
spec.version = "1.71.1"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.71.0/filament-v1.71.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.71.1/filament-v1.71.1-ios.tgz" }
spec.libraries = 'c++'
+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);
+1 -1
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;
}
@@ -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;
}
@@ -278,7 +278,15 @@ public:
FLOAT,
FLOAT2,
FLOAT3,
FLOAT4
FLOAT4,
INT,
INT2,
INT3,
INT4,
UINT,
UINT2,
UINT3,
UINT4
};
struct PreprocessorDefine {
+8
View File
@@ -148,6 +148,14 @@ std::unordered_map<std::string_view, OutputType> Enums::mStringToOutputType = {
{ "float2", OutputType::FLOAT2 },
{ "float3", OutputType::FLOAT3 },
{ "float4", OutputType::FLOAT4 },
{ "int", OutputType::INT },
{ "int2", OutputType::INT2 },
{ "int3", OutputType::INT3 },
{ "int4", OutputType::INT4 },
{ "uint", OutputType::UINT },
{ "uint2", OutputType::UINT2 },
{ "uint3", OutputType::UINT3 },
{ "uint4", OutputType::UINT4 }
};
template <>
+32
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;
}
+31 -4
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;
@@ -1273,6 +1282,25 @@ error:
mShading = Shading::UNLIT;
}
if (mMaterialDomain == MaterialDomain::SURFACE && !mOutputs.empty()) {
if (mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) {
LOG(ERROR) << "Error: feature level 0 does not support custom outputs.";
goto error;
}
if (mOutputs.size() > 1) {
LOG(ERROR) << "Error: surface materials only support a maximum of 1 custom output.";
goto error;
}
if (mShading != Shading::UNLIT) {
LOG(ERROR) << "Error: custom outputs are only supported for unlit surface materials.";
goto error;
}
if (mBlendingMode != BlendingMode::OPAQUE) {
LOG(ERROR) << "Error: surface materials with custom outputs must use opaque blending.";
goto error;
}
}
// Add a default color output.
if (mMaterialDomain == MaterialDomain::POST_PROCESS && mOutputs.empty()) {
output(VariableQualifier::OUT,
@@ -1319,8 +1347,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,
};
+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() {
+13 -66
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);
@@ -1303,6 +1242,14 @@ char const* CodeGenerator::getOutputTypeName(MaterialBuilder::OutputType type) n
case MaterialBuilder::OutputType::FLOAT2: return "vec2";
case MaterialBuilder::OutputType::FLOAT3: return "vec3";
case MaterialBuilder::OutputType::FLOAT4: return "vec4";
case MaterialBuilder::OutputType::INT: return "int";
case MaterialBuilder::OutputType::INT2: return "ivec2";
case MaterialBuilder::OutputType::INT3: return "ivec3";
case MaterialBuilder::OutputType::INT4: return "ivec4";
case MaterialBuilder::OutputType::UINT: return "uint";
case MaterialBuilder::OutputType::UINT2: return "uvec2";
case MaterialBuilder::OutputType::UINT3: return "uvec3";
case MaterialBuilder::OutputType::UINT4: return "uvec4";
}
}
+31 -11
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);
@@ -605,6 +607,16 @@ std::string ShaderGenerator::createSurfaceFragmentProgram(ShaderModel const shad
+PerMaterialBindingPoints::MATERIAL_PARAMS,
material.uib);
if (!filament::Variant::isValidDepthVariant(variant)) {
if (!mOutputs.empty()) {
CodeGenerator::generateDefine(fs, "HAS_CUSTOM_OUTPUT", 1u);
for (const auto& output: mOutputs) {
cg.generateOutput(fs, ShaderStage::FRAGMENT, output.name, output.location,
output.qualifier, output.precision, output.type);
}
}
}
CodeGenerator::generateSeparator(fs);
if (featureLevel >= FeatureLevel::FEATURE_LEVEL_1) {
@@ -687,6 +699,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,
@@ -737,7 +751,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 +780,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;
@@ -801,13 +818,16 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel const
CodeGenerator::generatePostProcessGetters(fs, ShaderStage::FRAGMENT);
// Generate post-process outputs.
for (const auto& output : mOutputs) {
if (output.target == MaterialBuilder::OutputTarget::COLOR) {
cg.generateOutput(fs, ShaderStage::FRAGMENT, output.name, output.location,
output.qualifier, output.precision, output.type);
}
if (output.target == MaterialBuilder::OutputTarget::DEPTH) {
CodeGenerator::generateDefine(fs, "FRAG_OUTPUT_DEPTH", 1u);
if (!mOutputs.empty()) {
CodeGenerator::generateDefine(fs, "HAS_CUSTOM_OUTPUT", 1u);
for (const auto& output: mOutputs) {
if (output.target == MaterialBuilder::OutputTarget::COLOR) {
cg.generateOutput(fs, ShaderStage::FRAGMENT, output.name, output.location,
output.qualifier, output.precision, output.type);
}
if (output.target == MaterialBuilder::OutputTarget::DEPTH) {
CodeGenerator::generateDefine(fs, "FRAG_OUTPUT_DEPTH", 1u);
}
}
}
+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;
+82 -3
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;
@@ -1061,6 +1061,85 @@ TEST_F(MaterialCompiler, ClientApiLevelReleasedUseOfUnstableApiFails) {
EXPECT_FALSE(result.isValid());
}
TEST_F(MaterialCompiler, CustomOutputSuccess) {
std::string shaderCode(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.customOutput = uvec4(1, 2, 3, 4);
}
)");
filamat::MaterialBuilder builder;
builder.material(shaderCode.c_str());
builder.shading(filament::Shading::UNLIT);
builder.blending(filament::BlendingMode::OPAQUE);
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_1);
builder.output(filamat::MaterialBuilder::VariableQualifier::OUT,
filamat::MaterialBuilder::OutputTarget::COLOR,
filamat::MaterialBuilder::Precision::DEFAULT,
filamat::MaterialBuilder::OutputType::UINT4,
"customOutput");
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());
}
TEST_F(MaterialCompiler, CustomOutputFeatureLevel0Fails) {
filamat::MaterialBuilder builder;
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_0);
builder.output(filamat::MaterialBuilder::VariableQualifier::OUT,
filamat::MaterialBuilder::OutputTarget::COLOR,
filamat::MaterialBuilder::Precision::DEFAULT,
filamat::MaterialBuilder::OutputType::UINT4,
"customOutput");
filamat::Package result = builder.build(*jobSystem);
EXPECT_FALSE(result.isValid());
}
TEST_F(MaterialCompiler, CustomOutputMoreThanOneFails) {
filamat::MaterialBuilder builder;
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_1);
builder.output(filamat::MaterialBuilder::VariableQualifier::OUT,
filamat::MaterialBuilder::OutputTarget::COLOR,
filamat::MaterialBuilder::Precision::DEFAULT,
filamat::MaterialBuilder::OutputType::UINT4,
"customOutput");
builder.output(filamat::MaterialBuilder::VariableQualifier::OUT,
filamat::MaterialBuilder::OutputTarget::COLOR,
filamat::MaterialBuilder::Precision::DEFAULT,
filamat::MaterialBuilder::OutputType::UINT4,
"customOutput1");
filamat::Package result = builder.build(*jobSystem);
EXPECT_FALSE(result.isValid());
}
TEST_F(MaterialCompiler, CustomOutputLitFails) {
filamat::MaterialBuilder builder;
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_1);
builder.shading(filament::Shading::LIT);
builder.output(filamat::MaterialBuilder::VariableQualifier::OUT,
filamat::MaterialBuilder::OutputTarget::COLOR,
filamat::MaterialBuilder::Precision::DEFAULT,
filamat::MaterialBuilder::OutputType::UINT4,
"customOutput");
filamat::Package result = builder.build(*jobSystem);
EXPECT_FALSE(result.isValid());
}
TEST_F(MaterialCompiler, CustomOutputTransparentFails) {
filamat::MaterialBuilder builder;
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_1);
builder.shading(filament::Shading::UNLIT);
builder.blending(filament::BlendingMode::TRANSPARENT);
builder.output(filamat::MaterialBuilder::VariableQualifier::OUT,
filamat::MaterialBuilder::OutputTarget::COLOR,
filamat::MaterialBuilder::Precision::DEFAULT,
filamat::MaterialBuilder::OutputType::UINT4,
"customOutput");
filamat::Package result = builder.build(*jobSystem);
EXPECT_FALSE(result.isValid());
}
#if FILAMENT_SUPPORTS_WEBGPU
TEST_F(MaterialCompiler, WgslConversionBakedColor) {
std::string bakedColorCodeFrag(R"(
+1 -1
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()];
+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
+4 -10
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
+1 -1
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)
+9 -8
View File
@@ -1,7 +1,9 @@
#if __VERSION__ == 100
vec4 fragColor;
#else
#if !defined(HAS_CUSTOM_OUTPUT)
layout(location = 0) out vec4 fragColor;
#else
// Define fragColor even with custom outputs enabled to satisfy usages. It will be removed
// during dead-code elimination.
vec4 fragColor;
#endif
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
@@ -114,14 +116,13 @@ 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
#if __VERSION__ == 100
gl_FragData[0] = fragColor;
#if defined(HAS_CUSTOM_OUTPUT)
FRAG_OUTPUT_AT0 = inputs.FRAG_OUTPUT0;
#endif
}
+8
View File
@@ -105,6 +105,10 @@ struct MaterialInputs {
float shadowStrength;
#endif
#if defined(FRAG_OUTPUT0)
FRAG_OUTPUT_MATERIAL_TYPE0 FRAG_OUTPUT0;
#endif
};
void initMaterial(out MaterialInputs material) {
@@ -208,6 +212,10 @@ void initMaterial(out MaterialInputs material) {
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
material.shadowStrength = 0.0;
#endif
#if defined(FRAG_OUTPUT0)
material.FRAG_OUTPUT0 = FRAG_OUTPUT_MATERIAL_TYPE0(0.0);
#endif
}
#if defined(MATERIAL_HAS_CUSTOM_SURFACE_SHADING)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.71.0",
"version": "1.71.1",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",