Compare commits

...

6 Commits

Author SHA1 Message Date
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
41 changed files with 948 additions and 302 deletions

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.

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

View File

@@ -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);
});
}

View File

@@ -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,

View File

@@ -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);
}

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.

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);
}

View File

@@ -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)) {

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,

View File

@@ -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.
*

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();

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;

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

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;

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;

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;

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

View File

@@ -1756,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));
}
// ------------------------------------------------------------------------------------------------

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) {

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;

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

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{};

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

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;

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);
}

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++'

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;
}

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;
}

View File

@@ -278,7 +278,15 @@ public:
FLOAT,
FLOAT2,
FLOAT3,
FLOAT4
FLOAT4,
INT,
INT2,
INT3,
INT4,
UINT,
UINT2,
UINT3,
UINT4
};
struct PreprocessorDefine {

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 <>

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;
}

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,
};

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";
}
}

View File

@@ -607,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) {
@@ -689,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,
@@ -806,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);
}
}
}

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"(

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()];

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

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)

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)
@@ -119,7 +121,8 @@ void main() {
}
#endif
#if __VERSION__ == 100
gl_FragData[0] = fragColor;
#if defined(HAS_CUSTOM_OUTPUT)
FRAG_OUTPUT_AT0 = inputs.FRAG_OUTPUT0;
#endif
}

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)

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",