Compare commits
2 Commits
v1.71.2
...
exv/mutabl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84cecd334b | ||
|
|
bb6ebd3aea |
@@ -7,3 +7,6 @@ We are chaning the way Vulkan buffers are handled. We need to switch over to a m
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- Rename `sampler` parameter `unfilterable` to `filterable` [⚠️ **New Material Version**]
|
||||
- materials: new mutable specialization constants feature. See the [materials documentation](https://google.github.io/filament/Materials.html) for details. [⚠️ **New Material Version**]
|
||||
|
||||
@@ -60,6 +60,14 @@ static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void setConstant(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
instance->setConstant(name, v);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetParameterBool(JNIEnv *env, jclass,
|
||||
@@ -264,6 +272,13 @@ Java_com_google_android_filament_MaterialInstance_nSetParameterTexture(
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetConstantBool(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_, jboolean x) {
|
||||
setConstant(env, nativeMaterialInstance, name_, bool(x));
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetScissor(
|
||||
|
||||
@@ -402,6 +402,16 @@ public class MaterialInstance {
|
||||
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a bool constant.
|
||||
*
|
||||
* @param name the name of the material constant
|
||||
* @param x the value of the material constant
|
||||
*/
|
||||
public void setConstant(@NonNull String name, boolean x) {
|
||||
nSetConstantBool(getNativeObject(), name, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
@@ -921,6 +931,9 @@ 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 void nSetConstantBool(long nativeMaterialInstance,
|
||||
@NonNull String name, boolean x);
|
||||
|
||||
private static native void nSetParameterTexture(long nativeMaterialInstance,
|
||||
@NonNull String name, long nativeTexture, long sampler);
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ when using textures.
|
||||
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
|
||||
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
|
||||
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
|
||||
(reflected light takes on the color of the surfaced as defined by `baseColor`).
|
||||
(reflected light takes on the color of the surfaced as defined by `baseColor`).
|
||||
|
||||
The effect of `metallic` is shown in figure [metallicProperty] (click on the image to see a
|
||||
larger version).
|
||||
@@ -248,7 +248,7 @@ The effect of `roughness` on metallic surfaces is shown in figure [roughnessCond
|
||||
When refraction through an object is enabled (using a `refractonType` of `thin` or `solid`), the
|
||||
`roughness` property will also affect the refractions, as shown in figure
|
||||
[roughnessRefractionProperty] (click on the image to see a larger version).
|
||||
|
||||
|
||||
![Figure [roughnessRefractionProperty]: Refractive sphere with `roughness` varying from 0.0
|
||||
(left) to 1.0 (right)](images/materials/refraction_roughness.png)
|
||||
|
||||
@@ -307,7 +307,7 @@ The sheen color controls the color appearance and strength of an optional sheen
|
||||
base layer described by the properties above. The sheen layer always sits below the clear coat layer
|
||||
if such a layer is present.
|
||||
|
||||
The sheen layer can be used to represent cloth and fabric materials. Please refer to
|
||||
The sheen layer can be used to represent cloth and fabric materials. Please refer to
|
||||
section [Cloth model] for more information about cloth and fabric materials.
|
||||
|
||||
The effect of `sheenColor` is shown in figure [materialSheenColor]
|
||||
@@ -520,14 +520,14 @@ light to bend further away from the initial path.
|
||||
|
||||
Table [commonMatIOR] describes acceptable refractive indices for various types of materials.
|
||||
|
||||
Material | IOR
|
||||
Material | IOR
|
||||
--------------------------:|:-----------------
|
||||
Air | 1.0
|
||||
Water | 1.33
|
||||
Common liquids | 1.33 to 1.5
|
||||
Common gemstones | 1.58 to 2.33
|
||||
Plastics, glass | 1.5 to 1.58
|
||||
Other dielectric materials | 1.33 to 1.58
|
||||
Water | 1.33
|
||||
Common liquids | 1.33 to 1.5
|
||||
Common gemstones | 1.58 to 2.33
|
||||
Plastics, glass | 1.5 to 1.58
|
||||
Other dielectric materials | 1.33 to 1.58
|
||||
[Table [commonMatIOR]: Index of refraction of common materials]
|
||||
|
||||
The appearance of a refractive material will greatly depend on the `refractionType` and
|
||||
@@ -1096,30 +1096,37 @@ Value
|
||||
`bool` or `number`, depending on the `type` of the constant. The type must be one of the types
|
||||
described in table [materialConstantsTypes].
|
||||
|
||||
Type | Description | Default
|
||||
:----------------------|:-----------------------------------------|:------------------
|
||||
int | A signed, 32 bit GLSL int | 0
|
||||
float | A single-precision GLSL float | 0.0
|
||||
bool | A GLSL bool | false
|
||||
Constants may also be specified as mutable by setting the `mutable` property to `true`. Only
|
||||
`bool` spec constants may be specified as mutable.
|
||||
|
||||
| Type | Description | Default | May be mutable? |
|
||||
|:------|:------------------------------|:--------|-----------------|
|
||||
| int | A signed, 32 bit GLSL int | | no |
|
||||
| float | A single-precision GLSL float | 0.0 | no |
|
||||
| bool | A GLSL bool | false | yes |
|
||||
[Table [materialConstantsTypes]: Material constants types]
|
||||
|
||||
Description
|
||||
: Lists the constant parameters accepted by your material. These constants can be set, or
|
||||
"specialized", at runtime when loading a material package. Multiple materials can be loaded from
|
||||
the same material package with differing constant parameter specializations. Once a material is
|
||||
loaded from a material package, its constant parameters cannot be changed. Compared to regular
|
||||
parameters, constant parameters allow the compiler to generate more efficient code. Access
|
||||
constant parameters from the shader by prefixing the name with `materialConstant_`. For example,
|
||||
a constant parameter named `myConstant` is accessed in the shader as
|
||||
`materialConstant_myConstant`. If a constant parameter is not set at runtime, the default is
|
||||
used.
|
||||
the same material package with differing constant parameter specializations. If a constant
|
||||
parameter is specialized as mutable, it may be changed at any time on a per-instance basis via
|
||||
`MaterialInstance::setConstant(name, value)`. Otherwise, once a material is loaded from a
|
||||
material package, its constant parameters cannot be changed.
|
||||
|
||||
Compared to regular parameters, constant parameters allow the compiler to generate more
|
||||
efficient code. Access constant parameters from the shader by prefixing the name with
|
||||
`materialConstant_`. For example, a constant parameter named `myConstant` is accessed in the
|
||||
shader as `materialConstant_myConstant`. If a constant parameter is not set at runtime, the
|
||||
default is used.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
constants : [
|
||||
{
|
||||
name : overrideAlpha,
|
||||
type : bool
|
||||
type : bool,
|
||||
mutable : true,
|
||||
},
|
||||
{
|
||||
name : customAlpha,
|
||||
@@ -2639,7 +2646,7 @@ standard skybox material. It produces a list of 2 parameters, named `showSun` an
|
||||
respectively a boolean and a cubemap texture.
|
||||
|
||||
```text
|
||||
$ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
$ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
{
|
||||
"parameters": [
|
||||
{
|
||||
@@ -2655,7 +2662,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### --variant-filter
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -66,6 +67,7 @@ public:
|
||||
using DescriptorBindingsInfo = utils::FixedCapacityVector<Descriptor>;
|
||||
using DescriptorSetInfo = std::array<DescriptorBindingsInfo, MAX_DESCRIPTOR_SET_COUNT>;
|
||||
using SpecializationConstantsInfo = utils::FixedCapacityVector<SpecializationConstant>;
|
||||
using MutableSpecConstantsInfo = utils::bitset8;
|
||||
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
|
||||
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
|
||||
|
||||
@@ -103,7 +105,8 @@ public:
|
||||
Program& descriptorBindings(backend::descriptor_set_t set,
|
||||
DescriptorBindingsInfo descriptorBindings) noexcept;
|
||||
|
||||
Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept;
|
||||
Program& specializationConstants(SpecializationConstantsInfo specConstants,
|
||||
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept;
|
||||
|
||||
struct PushConstant {
|
||||
utils::CString name;
|
||||
|
||||
@@ -82,8 +82,19 @@ Program& Program::attributes(AttributesInfo attributes) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants) noexcept {
|
||||
mSpecializationConstants = std::move(specConstants);
|
||||
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants,
|
||||
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept {
|
||||
// String the two lists together.
|
||||
mSpecializationConstants = SpecializationConstantsInfo(
|
||||
specConstants.size() + mutableSpecConstants.size());
|
||||
std::uninitialized_move(specConstants.begin(), specConstants.end(),
|
||||
mSpecializationConstants.begin());
|
||||
for (uint32_t i = 0; i < mutableSpecConstants.size(); i++) {
|
||||
mSpecializationConstants[i + specConstants.size()] = SpecializationConstant {
|
||||
.id = i + firstMutableId,
|
||||
.value = mutableSpecConstants[i],
|
||||
};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,9 @@ 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<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
|
||||
@@ -238,13 +241,13 @@ public:
|
||||
|
||||
/**
|
||||
* Gets the value of a parameter by name.
|
||||
*
|
||||
*
|
||||
* Note: Only supports non-texture parameters such as numeric and math types.
|
||||
*
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*
|
||||
*
|
||||
* @see Material::hasParameter
|
||||
*/
|
||||
template<typename T>
|
||||
@@ -262,6 +265,53 @@ public:
|
||||
return getParameter<T>(name, strlen(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a mutable constant parameter by name.
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @param value Value of the parameter to set.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*/
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(const char* UTILS_NONNULL name, size_t nameLength, T const& 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 const& 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 const& value) {
|
||||
setConstant<T>(name, strlen(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a mutable constant parameter by name.
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*
|
||||
* @see Material::hasConstant
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
|
||||
@@ -233,6 +233,46 @@ template UTILS_PUBLIC mat3f MaterialInstance::getParameter<mat3f> (const ch
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<>
|
||||
inline void FMaterialInstance::setConstantImpl<bool>(std::string_view const name, bool const& value) {
|
||||
std::optional<uint32_t> id = mMaterial->getMutableConstantId(name);
|
||||
FILAMENT_CHECK_PRECONDITION(id.has_value()) << "No mutable constant with name " << name;
|
||||
mConstants.set(*id, value);
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
void MaterialInstance::setConstant(const char* name, size_t nameLength, T const& value) {
|
||||
downcast(this)->setConstantImpl({ name, nameLength }, value);
|
||||
}
|
||||
|
||||
// Explicit template instantiation of our supported types.
|
||||
//
|
||||
// Mutable spec constants will probably only ever allow bools, but it's nice to keep the API
|
||||
// forwards-compatible.
|
||||
template UTILS_PUBLIC void MaterialInstance::setConstant<bool>(const char* name, size_t nameLength, bool const& v);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<>
|
||||
inline bool FMaterialInstance::getConstantImpl<bool>(std::string_view const name) const {
|
||||
std::optional<uint32_t> id = mMaterial->getMutableConstantId(name);
|
||||
FILAMENT_CHECK_PRECONDITION(id.has_value()) << "No mutable constant with name " << name;
|
||||
return mConstants[*id];
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
T MaterialInstance::getConstant(const char* name, size_t nameLength) const {
|
||||
return downcast(this)->getConstantImpl<T>({ name, nameLength });
|
||||
}
|
||||
|
||||
// Explicit template instantiation of our supported types.
|
||||
//
|
||||
// Mutable spec constants will probably only ever allow bools, but it's nice to keep the API
|
||||
// forwards-compatible.
|
||||
template UTILS_PUBLIC bool MaterialInstance::getConstant<bool>(const char* name, size_t nameLength) const;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
Material const* MaterialInstance::getMaterial() const noexcept {
|
||||
return downcast(this)->getMaterial();
|
||||
}
|
||||
|
||||
@@ -229,6 +229,11 @@ bool MaterialParser::getConstants(FixedCapacityVector<MaterialConstant>* contain
|
||||
return get<ChunkMaterialConstants>(container);
|
||||
}
|
||||
|
||||
bool MaterialParser::getMutableConstants(
|
||||
FixedCapacityVector<MaterialMutableConstant>* container) const noexcept {
|
||||
return get<ChunkMaterialMutableConstants>(container);
|
||||
}
|
||||
|
||||
bool MaterialParser::getPushConstants(CString* structVarName,
|
||||
FixedCapacityVector<MaterialPushConstant>* value) const noexcept {
|
||||
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialPushConstants);
|
||||
@@ -761,6 +766,38 @@ bool ChunkMaterialConstants::unflatten(Unflattener& unflattener,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChunkMaterialMutableConstants::unflatten(Unflattener& unflattener,
|
||||
FixedCapacityVector<MaterialMutableConstant>* materialConstants) {
|
||||
assert_invariant(materialConstants);
|
||||
|
||||
// Read number of constants.
|
||||
uint64_t numConstants = 0;
|
||||
if (!unflattener.read(&numConstants)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
materialConstants->reserve(numConstants);
|
||||
materialConstants->resize(numConstants);
|
||||
|
||||
for (uint64_t i = 0; i < numConstants; i++) {
|
||||
CString constantName;
|
||||
bool defaultValue;
|
||||
|
||||
if (!unflattener.read(&constantName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!unflattener.read(&defaultValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(*materialConstants)[i].name = std::move(constantName);
|
||||
(*materialConstants)[i].defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChunkMaterialPushConstants::unflatten(Unflattener& unflattener,
|
||||
CString* structVarName,
|
||||
FixedCapacityVector<MaterialPushConstant>* materialPushConstants) {
|
||||
|
||||
@@ -49,6 +49,7 @@ class BufferInterfaceBlock;
|
||||
class SamplerInterfaceBlock;
|
||||
struct SubpassInfo;
|
||||
struct MaterialConstant;
|
||||
struct MaterialMutableConstant;
|
||||
struct MaterialPushConstant;
|
||||
|
||||
class MaterialParser {
|
||||
@@ -79,6 +80,8 @@ public:
|
||||
bool getShaderModels(uint32_t* value) const noexcept;
|
||||
bool getMaterialProperties(uint64_t* value) const noexcept;
|
||||
bool getConstants(utils::FixedCapacityVector<MaterialConstant>* value) const noexcept;
|
||||
bool getMutableConstants(utils::FixedCapacityVector<MaterialMutableConstant>* value)
|
||||
const noexcept;
|
||||
bool getPushConstants(utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* value) const noexcept;
|
||||
|
||||
@@ -238,6 +241,13 @@ struct ChunkMaterialConstants {
|
||||
static filamat::ChunkType const tag = filamat::MaterialConstants;
|
||||
};
|
||||
|
||||
struct ChunkMaterialMutableConstants {
|
||||
static bool unflatten(filaflat::Unflattener& unflattener,
|
||||
utils::FixedCapacityVector<MaterialMutableConstant>* materialConstants);
|
||||
using Container = utils::FixedCapacityVector<MaterialMutableConstant>;
|
||||
static filamat::ChunkType const tag = filamat::MaterialMutableConstants;
|
||||
};
|
||||
|
||||
struct ChunkMaterialPushConstants {
|
||||
static bool unflatten(filaflat::Unflattener& unflattener, utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* materialPushConstants);
|
||||
|
||||
@@ -199,6 +199,8 @@ FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine,
|
||||
loadMaterial(engine);
|
||||
}
|
||||
mMaterial->prepareProgram(Variant{ Variant::type_t(variant) },
|
||||
// TODO(exv): allow post process materials to use mutable spec constants?
|
||||
Program::MutableSpecConstantsInfo(0),
|
||||
CompilerPriorityQueue::CRITICAL);
|
||||
return mMaterial;
|
||||
}
|
||||
@@ -465,7 +467,10 @@ UTILS_NOINLINE
|
||||
PipelineState PostProcessManager::getPipelineState(
|
||||
FMaterial const* const ma, PostProcessVariant variant) const noexcept {
|
||||
return {
|
||||
.program = ma->getProgram(Variant{ Variant::type_t(variant) }),
|
||||
.program = ma->getProgram(Variant{ Variant::type_t(variant) },
|
||||
// TODO(exv): allow post process materials to use mutable spec constants?
|
||||
Program::MutableSpecConstantsInfo(0)
|
||||
),
|
||||
.vertexBufferInfo = mFullScreenQuadVbih,
|
||||
.pipelineLayout = {
|
||||
.setLayout = {
|
||||
|
||||
@@ -243,7 +243,9 @@ void RenderPass::appendCommands(FEngine const& engine,
|
||||
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(first->info.materialVariant, CompilerPriorityQueue::CRITICAL);
|
||||
ma->prepareProgram(first->info.materialVariant,
|
||||
first->info.mi->getMutableSpecConstants(),
|
||||
CompilerPriorityQueue::CRITICAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1096,7 +1098,8 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
}
|
||||
|
||||
assert_invariant(ma);
|
||||
pipeline.program = ma->getProgram(info.materialVariant);
|
||||
pipeline.program = ma->getProgram(info.materialVariant,
|
||||
info.mi->getMutableSpecConstants());
|
||||
|
||||
if (UTILS_UNLIKELY(memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) {
|
||||
currentPipeline = pipeline;
|
||||
|
||||
@@ -364,8 +364,24 @@ FMaterial::FMaterial(FEngine& engine, const Builder& builder,
|
||||
|
||||
processBlendingMode(parser);
|
||||
processSpecializationConstants(engine, builder, parser);
|
||||
processMutableSpecializationConstants(engine, builder, parser);
|
||||
processPushConstants(engine, parser);
|
||||
processDescriptorSets(engine, parser);
|
||||
|
||||
switch (mMaterialDomain) {
|
||||
case filament::MaterialDomain::SURFACE:
|
||||
mVariantBits = VARIANT_BITS;
|
||||
break;
|
||||
case filament::MaterialDomain::POST_PROCESS:
|
||||
mVariantBits = POST_PROCESS_VARIANT_BITS;
|
||||
break;
|
||||
case filament::MaterialDomain::COMPUTE:
|
||||
mVariantBits = 0;
|
||||
break;
|
||||
}
|
||||
mCachedPrograms = FixedCapacityVector<backend::Handle<backend::HwProgram>>(
|
||||
1 << (mVariantBits + mMaterialMutableConstants.size()));
|
||||
|
||||
precacheDepthVariants(engine);
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
@@ -466,13 +482,17 @@ void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
UserVariantFilterMask const variantFilter =
|
||||
~variantSpec & UserVariantFilterMask(UserVariantFilterBit::ALL);
|
||||
|
||||
int mutableConstantVariations = 1 << mMaterialMutableConstants.size();
|
||||
|
||||
if (UTILS_LIKELY(mEngine.getDriverApi().isParallelShaderCompileSupported())) {
|
||||
auto const& variants = isVariantLit() ?
|
||||
VariantUtils::getLitVariants() : VariantUtils::getUnlitVariants();
|
||||
for (auto const variant: variants) {
|
||||
if (!variantFilter || variant == Variant::filterUserVariant(variant, variantFilter)) {
|
||||
if (hasVariant(variant)) {
|
||||
prepareProgram(variant, priority);
|
||||
for (int spec = 0; spec < mutableConstantVariations; spec++) {
|
||||
prepareProgram(variant, Program::MutableSpecConstantsInfo(spec), priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -553,14 +573,15 @@ bool FMaterial::hasVariant(Variant const variant) const noexcept {
|
||||
}
|
||||
|
||||
void FMaterial::prepareProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
assert_invariant(mEngine.hasFeatureLevel(mFeatureLevel));
|
||||
switch (getMaterialDomain()) {
|
||||
case MaterialDomain::SURFACE:
|
||||
getSurfaceProgramSlow(variant, priorityQueue);
|
||||
getSurfaceProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
break;
|
||||
case MaterialDomain::POST_PROCESS:
|
||||
getPostProcessProgramSlow(variant, priorityQueue);
|
||||
getPostProcessProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
break;
|
||||
case MaterialDomain::COMPUTE:
|
||||
// TODO: implement MaterialDomain::COMPUTE
|
||||
@@ -569,6 +590,7 @@ void FMaterial::prepareProgramSlow(Variant const variant,
|
||||
}
|
||||
|
||||
void FMaterial::getSurfaceProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
// filterVariant() has already been applied in generateCommands(), shouldn't be needed here
|
||||
// if we're unlit, we don't have any bits that correspond to lit materials
|
||||
@@ -579,25 +601,25 @@ void FMaterial::getSurfaceProgramSlow(Variant const variant,
|
||||
Variant const vertexVariant = Variant::filterVariantVertex(variant);
|
||||
Variant const fragmentVariant = Variant::filterVariantFragment(variant);
|
||||
|
||||
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant) };
|
||||
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant, mutableSpecConstants) };
|
||||
pb.priorityQueue(priorityQueue);
|
||||
pb.multiview(
|
||||
mEngine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW &&
|
||||
Variant::isStereoVariant(variant));
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
createAndCacheProgram(std::move(pb), variant, mutableSpecConstants);
|
||||
}
|
||||
|
||||
void FMaterial::getPostProcessProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
Program pb{ getProgramWithVariants(variant, variant, variant) };
|
||||
Program pb{ getProgramWithVariants(variant, variant, variant, mutableSpecConstants) };
|
||||
pb.priorityQueue(priorityQueue);
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
createAndCacheProgram(std::move(pb), variant, mutableSpecConstants);
|
||||
}
|
||||
|
||||
Program FMaterial::getProgramWithVariants(
|
||||
Variant variant,
|
||||
Variant vertexVariant,
|
||||
Variant fragmentVariant) const {
|
||||
Program FMaterial::getProgramWithVariants(Variant variant,
|
||||
Variant vertexVariant, Variant fragmentVariant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const {
|
||||
FEngine const& engine = mEngine;
|
||||
const ShaderModel sm = engine.getShaderModel();
|
||||
const bool isNoop = engine.getBackend() == Backend::NOOP;
|
||||
@@ -658,29 +680,34 @@ Program FMaterial::getProgramWithVariants(
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_RENDERABLE]);
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_MATERIAL,
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_MATERIAL]);
|
||||
program.specializationConstants(mSpecializationConstants);
|
||||
program.specializationConstants(mSpecializationConstants,
|
||||
CONFIG_MAX_RESERVED_SPEC_CONSTANTS + mMaterialConstants.size(), mutableSpecConstants);
|
||||
|
||||
program.pushConstants(ShaderStage::VERTEX, mPushConstants[uint8_t(ShaderStage::VERTEX)]);
|
||||
program.pushConstants(ShaderStage::FRAGMENT, mPushConstants[uint8_t(ShaderStage::FRAGMENT)]);
|
||||
|
||||
program.cacheId(hash::combine(size_t(mCacheId), variant.key));
|
||||
program.cacheId(
|
||||
hash::combine(size_t(mCacheId),
|
||||
hash::combine(variant.key, mutableSpecConstants.getValue())));
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const noexcept {
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant const variant,
|
||||
Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
FEngine const& engine = mEngine;
|
||||
DriverApi& driverApi = mEngine.getDriverApi();
|
||||
|
||||
bool const isShared = isSharedVariant(variant);
|
||||
size_t const cacheIndex = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
|
||||
// Check if the default material has this program cached
|
||||
if (isShared) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
if (pDefaultMaterial) {
|
||||
auto const program = pDefaultMaterial->mCachedPrograms[variant.key];
|
||||
auto const program = pDefaultMaterial->mCachedPrograms[cacheIndex];
|
||||
if (program) {
|
||||
mCachedPrograms[variant.key] = program;
|
||||
mCachedPrograms[cacheIndex] = program;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -689,17 +716,17 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const
|
||||
auto const program = driverApi.createProgram(std::move(p));
|
||||
driverApi.setDebugTag(program.getId(), mName);
|
||||
assert_invariant(program);
|
||||
mCachedPrograms[variant.key] = program;
|
||||
mCachedPrograms[cacheIndex] = program;
|
||||
|
||||
// If the default material doesn't already have this program cached, and all caching conditions
|
||||
// are met (Surface Domain and no custom depth shader), cache it now.
|
||||
// New Materials will inherit these program automatically.
|
||||
if (isShared) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[variant.key]) {
|
||||
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[cacheIndex]) {
|
||||
// set the tag to the default material name
|
||||
driverApi.setDebugTag(program.getId(), mName);
|
||||
pDefaultMaterial->mCachedPrograms[variant.key] = program;
|
||||
pDefaultMaterial->mCachedPrograms[cacheIndex] = program;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -747,6 +774,14 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
|
||||
return count;
|
||||
}
|
||||
|
||||
std::optional<uint32_t> FMaterial::getMutableConstantId(std::string_view name) const noexcept {
|
||||
auto const pos = mMutableSpecializationConstantsNameToIndex.find(name);
|
||||
if (pos != mMutableSpecializationConstantsNameToIndex.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
|
||||
// Swaps in an edited version of the original package that was used to create the material. The
|
||||
@@ -805,7 +840,9 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
|
||||
|
||||
#endif // FILAMENT_ENABLE_MATDBG
|
||||
|
||||
[[nodiscard]] Handle<HwProgram> FMaterial::getProgramWithMATDBG(Variant const variant) const noexcept {
|
||||
[[nodiscard]] Handle<HwProgram> FMaterial::getProgramWithMATDBG(Variant const variant,
|
||||
Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
size_t cacheIndex = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
assert_invariant((size_t)variant.key < VARIANT_COUNT);
|
||||
std::unique_lock lock(mActiveProgramsLock);
|
||||
@@ -820,13 +857,13 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
|
||||
lock.unlock();
|
||||
if (isSharedVariant(variant)) {
|
||||
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
|
||||
if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[variant.key]) {
|
||||
return pDefaultMaterial->getProgram(variant);
|
||||
if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[cacheIndex]) {
|
||||
return pDefaultMaterial->getProgram(variant, mutableSpecConstants);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
assert_invariant(mCachedPrograms[variant.key]);
|
||||
return mCachedPrograms[variant.key];
|
||||
assert_invariant(mCachedPrograms[cacheIndex]);
|
||||
return mCachedPrograms[cacheIndex];
|
||||
}
|
||||
|
||||
void FMaterial::destroyPrograms(FEngine& engine,
|
||||
@@ -839,7 +876,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
case MaterialDomain::SURFACE: {
|
||||
if (mIsDefaultMaterial || mHasCustomDepthShader) {
|
||||
// default material, or we have custom depth shaders, we destroy all variants
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variants, and this generates traffic in the command queue.
|
||||
@@ -857,7 +894,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
UTILS_UNUSED_IN_RELEASE
|
||||
auto const UTILS_NULLABLE pDefaultMaterial = engine.getDefaultMaterial();
|
||||
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
@@ -869,7 +906,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
// During Engine::shutdown, auto-cleanup destroys the
|
||||
// default material first
|
||||
assert_invariant(!pDefaultMaterial ||
|
||||
pDefaultMaterial->mCachedPrograms[k]);
|
||||
pDefaultMaterial->mCachedPrograms[k & (VARIANT_COUNT - 1)]);
|
||||
// we don't own this variant, skip, but clear the entry.
|
||||
cachedPrograms[k].clear();
|
||||
continue;
|
||||
@@ -883,7 +920,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
break;
|
||||
}
|
||||
case MaterialDomain::POST_PROCESS: {
|
||||
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
@@ -895,8 +932,13 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
break;
|
||||
}
|
||||
case MaterialDomain::COMPUTE: {
|
||||
// Compute programs don't have variants
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[0]));
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
if (cachedPrograms[k]) {
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[k]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1131,6 +1173,19 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processMutableSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* const parser) {
|
||||
// Older materials won't have a mutable constants chunk, but that's okay.
|
||||
parser->getMutableConstants(&mMaterialMutableConstants);
|
||||
for (size_t i = 0, c = mMaterialMutableConstants.size(); i < c; i++) {
|
||||
auto& item = mMaterialMutableConstants[i];
|
||||
// the key can be a string_view because mMaterialMutableConstants owns the CString
|
||||
std::string_view const key{ item.name.data(), item.name.size() };
|
||||
mMutableSpecializationConstantsNameToIndex[key] = i;
|
||||
mDefaultMutableSpecializationConstants.set(i, item.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processPushConstants(FEngine&, MaterialParser const* parser) {
|
||||
FixedCapacityVector<Program::PushConstant>& vertexConstants =
|
||||
mPushConstants[uint8_t(ShaderStage::VERTEX)];
|
||||
@@ -1186,7 +1241,9 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
}
|
||||
assert_invariant(Variant::isValidDepthVariant(variant));
|
||||
if (hasVariant(variant)) {
|
||||
prepareProgram(variant, CompilerPriorityQueue::HIGH);
|
||||
// The default material shouldn't ever have mutable spec constants.
|
||||
prepareProgram(variant, Program::MutableSpecConstantsInfo(0),
|
||||
CompilerPriorityQueue::HIGH);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -1199,9 +1256,16 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
assert_invariant(pDefaultMaterial);
|
||||
auto const allDepthVariants = VariantUtils::getDepthVariants();
|
||||
int mutableConstantVariations = 1 << mMaterialMutableConstants.size();
|
||||
for (auto const variant: allDepthVariants) {
|
||||
assert_invariant(Variant::isValidDepthVariant(variant));
|
||||
mCachedPrograms[variant.key] = pDefaultMaterial->mCachedPrograms[variant.key];
|
||||
size_t defaultCacheIndex = pDefaultMaterial->getCachedProgramIndex(variant,
|
||||
Program::MutableSpecConstantsInfo(0));
|
||||
for (int spec = 0; spec < mutableConstantVariations; spec++) {
|
||||
size_t cacheIndex = getCachedProgramIndex(variant,
|
||||
Program::MutableSpecConstantsInfo(spec));
|
||||
mCachedPrograms[cacheIndex] = pDefaultMaterial->mCachedPrograms[defaultCacheIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +132,9 @@ public:
|
||||
|
||||
FEngine& getEngine() const noexcept { return mEngine; }
|
||||
|
||||
bool isCached(Variant const variant) const noexcept {
|
||||
return bool(mCachedPrograms[variant.key]);
|
||||
bool isCached(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept {
|
||||
return bool(mCachedPrograms[getCachedProgramIndex(variant, mutableSpecConstants)]);
|
||||
}
|
||||
|
||||
void invalidate(Variant::type_t variantMask = 0, Variant::type_t variantValue = 0) noexcept;
|
||||
@@ -142,22 +143,26 @@ public:
|
||||
// Must be called outside of backend render pass.
|
||||
// Must be called before getProgram() below.
|
||||
void prepareProgram(Variant const variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
// prepareProgram() is called for each RenderPrimitive in the scene, so it must be efficient.
|
||||
if (UTILS_UNLIKELY(!isCached(variant))) {
|
||||
prepareProgramSlow(variant, priorityQueue);
|
||||
if (UTILS_UNLIKELY(!isCached(variant, mutableSpecConstants))) {
|
||||
prepareProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
}
|
||||
}
|
||||
|
||||
// getProgram returns the backend program for the material's given variant.
|
||||
// getProgram returns the backend program for the material's given variant and set of mutable
|
||||
// spec constants.
|
||||
// Must be called after prepareProgram().
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgram(Variant const variant) const noexcept {
|
||||
backend::Handle<backend::HwProgram> getProgram(Variant const variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
return getProgramWithMATDBG(variant);
|
||||
return getProgramWithMATDBG(variant, mutableSpecConstants);
|
||||
#endif
|
||||
assert_invariant(mCachedPrograms[variant.key]);
|
||||
return mCachedPrograms[variant.key];
|
||||
size_t index = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
assert_invariant(mCachedPrograms[index]);
|
||||
return mCachedPrograms[index];
|
||||
}
|
||||
|
||||
// MaterialInstance::use() binds descriptor sets before drawing. For shared variants,
|
||||
@@ -178,7 +183,8 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant) const noexcept;
|
||||
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept;
|
||||
|
||||
bool isVariantLit() const noexcept { return mIsVariantLit; }
|
||||
|
||||
@@ -235,6 +241,12 @@ public:
|
||||
}
|
||||
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
|
||||
|
||||
uint32_t getMutableConstantCount() const noexcept { return mMaterialMutableConstants.size(); }
|
||||
std::optional<uint32_t> getMutableConstantId(std::string_view name) const noexcept;
|
||||
backend::Program::MutableSpecConstantsInfo getDefaultMutableConstants() const noexcept {
|
||||
return mDefaultMutableSpecializationConstants;
|
||||
}
|
||||
|
||||
uint32_t generateMaterialInstanceId() const noexcept { return mMaterialInstanceId++; }
|
||||
|
||||
void destroyPrograms(FEngine& engine,
|
||||
@@ -288,34 +300,48 @@ public:
|
||||
private:
|
||||
bool hasVariant(Variant variant) const noexcept;
|
||||
void prepareProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
void getSurfaceProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
void getPostProcessProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
backend::Program getProgramWithVariants(Variant variant,
|
||||
Variant vertexVariant, Variant fragmentVariant) const;
|
||||
Variant vertexVariant, Variant fragmentVariant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const;
|
||||
|
||||
void processBlendingMode(MaterialParser const* parser);
|
||||
|
||||
void processSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* parser);
|
||||
|
||||
void processMutableSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* parser);
|
||||
|
||||
void processPushConstants(FEngine& engine, MaterialParser const* parser);
|
||||
|
||||
void precacheDepthVariants(FEngine& engine);
|
||||
|
||||
void processDescriptorSets(FEngine& engine, MaterialParser const* parser);
|
||||
|
||||
void createAndCacheProgram(backend::Program&& p, Variant variant) const noexcept;
|
||||
void createAndCacheProgram(backend::Program&& p, Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept;
|
||||
|
||||
inline bool isSharedVariant(Variant const variant) const {
|
||||
return (mMaterialDomain == MaterialDomain::SURFACE) && !mIsDefaultMaterial &&
|
||||
!mHasCustomDepthShader && Variant::isValidDepthVariant(variant);
|
||||
}
|
||||
|
||||
inline size_t getCachedProgramIndex(const Variant variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept {
|
||||
return static_cast<size_t>(variant.key) |
|
||||
(static_cast<size_t>(mutableSpecConstants.getValue()) << mVariantBits);
|
||||
}
|
||||
|
||||
// try to order by frequency of use
|
||||
mutable std::array<backend::Handle<backend::HwProgram>, VARIANT_COUNT> mCachedPrograms;
|
||||
mutable utils::FixedCapacityVector<backend::Handle<backend::HwProgram>> mCachedPrograms;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayout;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayoutVsm;
|
||||
DescriptorSetLayout mDescriptorSetLayout;
|
||||
@@ -351,6 +377,7 @@ private:
|
||||
bool mHasCustomDepthShader = false;
|
||||
bool mIsDefaultMaterial = false;
|
||||
bool mSpecularAntiAliasing = false;
|
||||
uint8_t mVariantBits;
|
||||
|
||||
// reserve some space to construct the default material instance
|
||||
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
|
||||
@@ -375,6 +402,13 @@ private:
|
||||
// current specialization constants for the HwProgram
|
||||
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mSpecializationConstants;
|
||||
|
||||
// Mutable constants defined by this Material.
|
||||
utils::FixedCapacityVector<MaterialMutableConstant> mMaterialMutableConstants;
|
||||
// A map from the Constant name to the mMutableMaterialConstant index
|
||||
std::unordered_map<std::string_view, uint32_t> mMutableSpecializationConstantsNameToIndex;
|
||||
// Default values for the mutable constants.
|
||||
backend::Program::MutableSpecConstantsInfo mDefaultMutableSpecializationConstants;
|
||||
|
||||
// current push constants for the HwProgram
|
||||
std::array<utils::FixedCapacityVector<backend::Program::PushConstant>,
|
||||
backend::Program::SHADER_TYPE_COUNT>
|
||||
|
||||
@@ -63,6 +63,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
const char* name) noexcept
|
||||
: mMaterial(material),
|
||||
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
|
||||
mConstants(material->getDefaultMutableConstants()),
|
||||
mCulling(CullingMode::BACK),
|
||||
mShadowCulling(CullingMode::BACK),
|
||||
mDepthFunc(RasterState::DepthFunc::LE),
|
||||
@@ -128,6 +129,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
mTextureParameters(other->mTextureParameters),
|
||||
mDescriptorSet(other->mDescriptorSet.duplicate(
|
||||
"MaterialInstance", mMaterial->getDescriptorSetLayout())),
|
||||
mConstants(other->mConstants),
|
||||
mPolygonOffset(other->mPolygonOffset),
|
||||
mStencilState(other->mStencilState),
|
||||
mMaskThreshold(other->mMaskThreshold),
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/bitset.h>
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
void commitStreamUniformAssociations(FEngine::DriverApi& driver);
|
||||
|
||||
|
||||
void commit(FEngine& engine) const;
|
||||
|
||||
void commit(FEngine::DriverApi& driver) const;
|
||||
@@ -244,6 +245,10 @@ public:
|
||||
|
||||
using MaterialInstance::setParameter;
|
||||
|
||||
const backend::Program::MutableSpecConstantsInfo getMutableSpecConstants() const noexcept {
|
||||
return mConstants;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class FMaterial;
|
||||
friend class MaterialInstance;
|
||||
@@ -266,6 +271,12 @@ private:
|
||||
template<typename T>
|
||||
T getParameterImpl(std::string_view name) const;
|
||||
|
||||
template<typename T>
|
||||
void setConstantImpl(std::string_view name, T const& value);
|
||||
|
||||
template<typename T>
|
||||
T getConstantImpl(std::string_view name) const;
|
||||
|
||||
// keep these grouped, they're accessed together in the render-loop
|
||||
FMaterial const* mMaterial = nullptr;
|
||||
|
||||
@@ -279,6 +290,7 @@ private:
|
||||
mutable DescriptorSet mDescriptorSet;
|
||||
UniformBuffer mUniforms;
|
||||
bool mHasStreamUniformAssociations = false;
|
||||
backend::Program::MutableSpecConstantsInfo mConstants;
|
||||
|
||||
backend::PolygonOffset mPolygonOffset{};
|
||||
backend::StencilState mStencilState{};
|
||||
|
||||
@@ -54,6 +54,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
MaterialDescriptorSetLayoutInfo = charTo64bitNum("MAT_DSLI"),
|
||||
MaterialProperties = charTo64bitNum("MAT_PROP"),
|
||||
MaterialConstants = charTo64bitNum("MAT_CONS"),
|
||||
MaterialMutableConstants = charTo64bitNum("MAT_MCNS"),
|
||||
MaterialPushConstants = charTo64bitNum("MAT_PCON"),
|
||||
|
||||
MaterialName = charTo64bitNum("MAT_NAME"),
|
||||
|
||||
@@ -30,7 +30,19 @@ struct MaterialConstant {
|
||||
ConstantType type;
|
||||
|
||||
MaterialConstant() = default;
|
||||
MaterialConstant(const char* name, ConstantType type) : name(name), type(type) {}
|
||||
MaterialConstant(utils::CString name, ConstantType type) : name(std::move(name)), type(type) {}
|
||||
};
|
||||
|
||||
struct MaterialMutableConstant {
|
||||
utils::CString name;
|
||||
// The Filament engine stores mutable spec constants as a bitfield, meaning that must override
|
||||
// all of the default values built into in the shader souces. Therefore, unlike the immutable
|
||||
// constants above, we have to store the default values in the material metadata.
|
||||
bool defaultValue;
|
||||
|
||||
MaterialMutableConstant() = default;
|
||||
MaterialMutableConstant(utils::CString name, bool defaultValue)
|
||||
: name(std::move(name)), defaultValue(defaultValue) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -319,14 +319,15 @@ public:
|
||||
MaterialBuilder& parameter(const char* name, size_t size, UniformType type,
|
||||
ParameterPrecision precision = ParameterPrecision::DEFAULT);
|
||||
|
||||
//! Add a constant parameter to this material.
|
||||
//! Add a (mutable) constant parameter to this material.
|
||||
template<typename T>
|
||||
using is_supported_constant_parameter_t = typename std::enable_if<
|
||||
std::is_same<int32_t, T>::value ||
|
||||
std::is_same<float, T>::value ||
|
||||
std::is_same<bool, T>::value>::type;
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
MaterialBuilder& constant(const char *name, ConstantType type, T defaultValue = 0);
|
||||
MaterialBuilder& constant(const char* name, ConstantType type, bool isMutable,
|
||||
T defaultValue = 0);
|
||||
|
||||
/**
|
||||
* Add a sampler parameter to this material.
|
||||
@@ -927,6 +928,7 @@ private:
|
||||
PropertyList mProperties;
|
||||
ParameterList mParameters;
|
||||
ConstantList mConstants;
|
||||
ConstantList mMutableConstants;
|
||||
PushConstantList mPushConstants;
|
||||
SubpassList mSubpasses;
|
||||
VariableList mVariables;
|
||||
|
||||
@@ -322,12 +322,19 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType sample
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const type, T defaultValue) {
|
||||
auto result = std::find_if(mConstants.begin(), mConstants.end(), [name](const Constant& c) {
|
||||
return c.name == CString(name);
|
||||
});
|
||||
FILAMENT_CHECK_POSTCONDITION(result == mConstants.end())
|
||||
MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const type,
|
||||
bool isMutable, T defaultValue) {
|
||||
ConstantList& constants = isMutable ? mMutableConstants : mConstants;
|
||||
const ConstantList& otherConstants = isMutable ? mConstants : mMutableConstants;
|
||||
auto result = std::find_if(constants.begin(), constants.end(),
|
||||
[name](const Constant& c) { return c.name == CString(name); });
|
||||
FILAMENT_CHECK_POSTCONDITION(result == constants.end())
|
||||
<< "There is already a constant parameter present with the name " << name << ".";
|
||||
auto otherResult = std::find_if(otherConstants.begin(), otherConstants.end(),
|
||||
[name](const Constant& c) { return c.name == CString(name); });
|
||||
FILAMENT_CHECK_POSTCONDITION(otherResult == otherConstants.end())
|
||||
<< "There is already a constant parameter present with the name " << name << ".";
|
||||
|
||||
Constant constant {
|
||||
.name = CString(name),
|
||||
.type = type,
|
||||
@@ -359,15 +366,20 @@ MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const
|
||||
assert_invariant(false);
|
||||
}
|
||||
|
||||
mConstants.push_back(constant);
|
||||
if (isMutable) {
|
||||
FILAMENT_CHECK_POSTCONDITION(type == ConstantType::BOOL)
|
||||
<< "Mutable spec constants must be bool type.";
|
||||
}
|
||||
|
||||
constants.push_back(constant);
|
||||
return *this;
|
||||
}
|
||||
template MaterialBuilder& MaterialBuilder::constant<int32_t>(
|
||||
const char* name, ConstantType type, int32_t defaultValue);
|
||||
const char* name, ConstantType type, bool isMutable, int32_t defaultValue);
|
||||
template MaterialBuilder& MaterialBuilder::constant<float>(
|
||||
const char* name, ConstantType type, float defaultValue);
|
||||
const char* name, ConstantType type, bool isMutable, float defaultValue);
|
||||
template MaterialBuilder& MaterialBuilder::constant<bool>(
|
||||
const char* name, ConstantType type, bool defaultValue);
|
||||
const char* name, ConstantType type, bool isMutable, bool defaultValue);
|
||||
|
||||
MaterialBuilder& MaterialBuilder::buffer(BufferInterfaceBlock bib) {
|
||||
FILAMENT_CHECK_POSTCONDITION(mBuffers.size() < MAX_BUFFERS_COUNT) << "Too many buffers";
|
||||
@@ -946,7 +958,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
BlobDictionary spirvDictionary;
|
||||
// End: must be protected by lock
|
||||
|
||||
ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mConstants, mPushConstants,
|
||||
ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mConstants, mMutableConstants, mPushConstants,
|
||||
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
|
||||
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
|
||||
mMaterialDomain);
|
||||
@@ -1548,7 +1560,7 @@ bool MaterialBuilder::needsStandardDepthProgram() const noexcept {
|
||||
std::string MaterialBuilder::peek(backend::ShaderStage const stage,
|
||||
const CodeGenParams& params, const PropertyList& properties) noexcept {
|
||||
|
||||
ShaderGenerator const sg(properties, mVariables, mOutputs, mDefines, mConstants, mPushConstants,
|
||||
ShaderGenerator const sg(properties, mVariables, mOutputs, mDefines, mConstants, mMutableConstants, mPushConstants,
|
||||
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
|
||||
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
|
||||
mMaterialDomain);
|
||||
@@ -1676,9 +1688,14 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
|
||||
// User constant parameters
|
||||
FixedCapacityVector<MaterialConstant> constantsEntry(mConstants.size());
|
||||
std::transform(mConstants.begin(), mConstants.end(), constantsEntry.begin(),
|
||||
[](Constant const& c) { return MaterialConstant(c.name.c_str(), c.type); });
|
||||
[](Constant const& c) { return MaterialConstant(c.name, c.type); });
|
||||
container.push<MaterialConstantParametersChunk>(std::move(constantsEntry));
|
||||
|
||||
FixedCapacityVector<MaterialMutableConstant> mutableConstantsEntry(mMutableConstants.size());
|
||||
std::transform(mMutableConstants.begin(), mMutableConstants.end(), mutableConstantsEntry.begin(),
|
||||
[](Constant const& c) { return MaterialMutableConstant(c.name, c.defaultValue.b); });
|
||||
container.push<MaterialMutableConstantParametersChunk>(std::move(mutableConstantsEntry));
|
||||
|
||||
FixedCapacityVector<MaterialPushConstant> pushConstantsEntry(mPushConstants.size());
|
||||
std::transform(mPushConstants.begin(), mPushConstants.end(), pushConstantsEntry.begin(),
|
||||
[](PushConstant const& c) {
|
||||
|
||||
@@ -118,6 +118,20 @@ void MaterialConstantParametersChunk::flatten(Flattener& f) {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
MaterialMutableConstantParametersChunk::MaterialMutableConstantParametersChunk(
|
||||
FixedCapacityVector<MaterialMutableConstant> constants)
|
||||
: Chunk(MaterialMutableConstants), mConstants(std::move(constants)) {}
|
||||
|
||||
void MaterialMutableConstantParametersChunk::flatten(Flattener& f) {
|
||||
f.writeUint64(mConstants.size());
|
||||
for (const auto& constant : mConstants) {
|
||||
f.writeString(constant.name.c_str());
|
||||
f.writeBool(constant.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
MaterialPushConstantParametersChunk::MaterialPushConstantParametersChunk(
|
||||
CString const& structVarName, FixedCapacityVector<MaterialPushConstant> constants)
|
||||
: Chunk(MaterialPushConstants),
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMAT_MAT_INTEFFACE_BLOCK_CHUNK_H
|
||||
|
||||
#include "Chunk.h"
|
||||
#include "private/filament/ConstantInfo.h"
|
||||
|
||||
#include <backend/Program.h>
|
||||
|
||||
@@ -91,6 +92,20 @@ private:
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class MaterialMutableConstantParametersChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialMutableConstantParametersChunk(
|
||||
FixedCapacityVector<filament::MaterialMutableConstant> constants);
|
||||
~MaterialMutableConstantParametersChunk() override = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener&) override;
|
||||
|
||||
FixedCapacityVector<filament::MaterialMutableConstant> mConstants;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class MaterialPushConstantParametersChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialPushConstantParametersChunk(CString const& structVarName,
|
||||
|
||||
@@ -285,10 +285,17 @@ void ShaderGenerator::appendShader(io::sstream& ss,
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGenerator::generateUserSpecConstants(
|
||||
const CodeGenerator& cg, io::sstream& fs, MaterialBuilder::ConstantList const& constants) {
|
||||
void ShaderGenerator::generateAllUserSpecConstants(const CodeGenerator& cg, io::sstream& fs,
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
MaterialBuilder::ConstantList const& mutableConstants) {
|
||||
generateUserSpecConstants(cg, fs, constants, 0);
|
||||
generateUserSpecConstants(cg, fs, mutableConstants, constants.size());
|
||||
}
|
||||
|
||||
void ShaderGenerator::generateUserSpecConstants(const CodeGenerator& cg, io::sstream& fs,
|
||||
MaterialBuilder::ConstantList const& constants, size_t offset) {
|
||||
// Constants 0 to CONFIG_MAX_RESERVED_SPEC_CONSTANTS - 1 are reserved by Filament.
|
||||
size_t index = CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
||||
size_t index = CONFIG_MAX_RESERVED_SPEC_CONSTANTS + offset;
|
||||
for (const auto& constant : constants) {
|
||||
std::string const fullName = std::string("materialConstants_") + constant.name.c_str();
|
||||
switch (constant.type) {
|
||||
@@ -316,6 +323,7 @@ ShaderGenerator::ShaderGenerator(
|
||||
MaterialBuilder::OutputList const& outputs,
|
||||
MaterialBuilder::PreprocessorDefineList const& defines,
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
MaterialBuilder::ConstantList const& mutableConstants,
|
||||
MaterialBuilder::PushConstantList const& pushConstants,
|
||||
CString const& materialCode, size_t const lineOffset,
|
||||
CString const& materialVertexCode, size_t const vertexLineOffset,
|
||||
@@ -338,6 +346,7 @@ ShaderGenerator::ShaderGenerator(
|
||||
mMaterialDomain = materialDomain;
|
||||
mDefines = defines;
|
||||
mConstants = constants;
|
||||
mMutableConstants = mutableConstants;
|
||||
mPushConstants = pushConstants;
|
||||
|
||||
if (mMaterialFragmentCode.empty()) {
|
||||
@@ -394,7 +403,7 @@ std::string ShaderGenerator::createSurfaceVertexProgram(ShaderModel const shader
|
||||
|
||||
cg.generateCommonProlog(vs, ShaderStage::VERTEX, material, variant);
|
||||
|
||||
generateUserSpecConstants(cg, vs, mConstants);
|
||||
generateAllUserSpecConstants(cg, vs, mConstants, mMutableConstants);
|
||||
|
||||
// note: even if the user vertex shader is empty, we can't use the "optimized" version if
|
||||
// we're in masked mode because fragment shader needs the color varyings
|
||||
@@ -527,7 +536,7 @@ std::string ShaderGenerator::createSurfaceFragmentProgram(ShaderModel const shad
|
||||
io::sstream fs;
|
||||
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, variant);
|
||||
|
||||
generateUserSpecConstants(cg, fs, mConstants);
|
||||
generateAllUserSpecConstants(cg, fs, mConstants, mMutableConstants);
|
||||
|
||||
generateSurfaceMaterialVariantDefines(
|
||||
fs, ShaderStage::FRAGMENT, featureLevel, material, variant);
|
||||
@@ -683,7 +692,7 @@ std::string ShaderGenerator::createSurfaceComputeProgram(ShaderModel const shade
|
||||
|
||||
cg.generateCommonProlog(s, ShaderStage::COMPUTE, material, {});
|
||||
|
||||
generateUserSpecConstants(cg, s, mConstants);
|
||||
generateAllUserSpecConstants(cg, s, mConstants, mMutableConstants);
|
||||
|
||||
CodeGenerator::generateSurfaceTypes(s, ShaderStage::COMPUTE);
|
||||
|
||||
@@ -723,7 +732,7 @@ std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel const sm
|
||||
io::sstream vs;
|
||||
cg.generateCommonProlog(vs, ShaderStage::VERTEX, material, {});
|
||||
|
||||
generateUserSpecConstants(cg, vs, mConstants);
|
||||
generateAllUserSpecConstants(cg, vs, mConstants, mMutableConstants);
|
||||
|
||||
CodeGenerator::generateDefine(vs, "LOCATION_POSITION", uint32_t(POSITION));
|
||||
|
||||
@@ -767,7 +776,7 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel const
|
||||
io::sstream fs;
|
||||
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, {});
|
||||
|
||||
generateUserSpecConstants(cg, fs, mConstants);
|
||||
generateAllUserSpecConstants(cg, fs, mConstants, mMutableConstants);
|
||||
|
||||
generatePostProcessMaterialVariantDefines(fs, PostProcessVariant(variant));
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
MaterialBuilder::OutputList const& outputs,
|
||||
MaterialBuilder::PreprocessorDefineList const& defines,
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
MaterialBuilder::ConstantList const& mutableConstants,
|
||||
MaterialBuilder::PushConstantList const& pushConstants,
|
||||
utils::CString const& materialCode,
|
||||
size_t lineOffset,
|
||||
@@ -104,9 +105,14 @@ private:
|
||||
static void generatePostProcessMaterialVariantDefines(utils::io::sstream& out,
|
||||
filament::PostProcessVariant variant) noexcept;
|
||||
|
||||
static void generateAllUserSpecConstants(const CodeGenerator& cg, utils::io::sstream& fs,
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
MaterialBuilder::ConstantList const& mutableConstants);
|
||||
|
||||
static void generateUserSpecConstants(
|
||||
const CodeGenerator& cg, utils::io::sstream& fs,
|
||||
MaterialBuilder::ConstantList const& constants);
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
size_t offset);
|
||||
|
||||
std::string createPostProcessVertexProgram(filament::backend::ShaderModel sm,
|
||||
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
||||
@@ -135,6 +141,7 @@ private:
|
||||
MaterialBuilder::MaterialDomain mMaterialDomain;
|
||||
MaterialBuilder::PreprocessorDefineList mDefines;
|
||||
MaterialBuilder::ConstantList mConstants;
|
||||
MaterialBuilder::ConstantList mMutableConstants;
|
||||
MaterialBuilder::PushConstantList mPushConstants;
|
||||
utils::CString mMaterialFragmentCode; // fragment or compute code
|
||||
utils::CString mMaterialVertexCode;
|
||||
|
||||
@@ -888,10 +888,10 @@ TEST_F(MaterialCompiler, ConstantParameter) {
|
||||
}
|
||||
)");
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
|
||||
builder.constant("myIntConstant", ConstantType::INT, 123);
|
||||
builder.constant("myBoolConstant", ConstantType::BOOL, true);
|
||||
builder.constant<bool>("myOtherBoolConstant", ConstantType::BOOL);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
|
||||
builder.constant("myIntConstant", ConstantType::INT, /*isMutable=*/false, 123);
|
||||
builder.constant("myBoolConstant", ConstantType::BOOL, /*isMutable=*/false, true);
|
||||
builder.constant<bool>("myOtherBoolConstant", ConstantType::BOOL, /*isMutable=*/false);
|
||||
|
||||
builder.shading(filament::Shading::LIT);
|
||||
builder.material(shaderCode.c_str());
|
||||
@@ -904,8 +904,8 @@ TEST_F(MaterialCompiler, ConstantParameterSameName) {
|
||||
#ifdef __EXCEPTIONS
|
||||
EXPECT_THROW({
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
|
||||
}, utils::PostconditionPanic);
|
||||
#endif
|
||||
}
|
||||
@@ -914,7 +914,7 @@ TEST_F(MaterialCompiler, ConstantParameterWrongType) {
|
||||
#ifdef __EXCEPTIONS
|
||||
EXPECT_THROW({
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, 10);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 10);
|
||||
}, utils::PostconditionPanic);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -352,6 +352,19 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
|
||||
return false;
|
||||
}
|
||||
|
||||
const JsonishValue* mutableValue = jsonObject.getValue("mutable");
|
||||
bool isMutable;
|
||||
if (mutableValue) {
|
||||
const JsonishBool* mutableBool = mutableValue->toJsonBool();
|
||||
if (!mutableBool) {
|
||||
std::cerr << "constants: mutable value must be BOOL." << std::endl;
|
||||
return false;
|
||||
}
|
||||
isMutable = mutableBool->getBool();
|
||||
} else {
|
||||
isMutable = false;
|
||||
}
|
||||
|
||||
auto typeString = typeValue->toJsonString()->getString();
|
||||
auto nameString = nameValue->toJsonString()->getString();
|
||||
const JsonishValue* defaultValue = jsonObject.getValue("default");
|
||||
@@ -370,7 +383,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
|
||||
// FIXME: Jsonish doesn't distinguish between integers and floats.
|
||||
intDefault = (int32_t)defaultValue->toJsonNumber()->getFloat();
|
||||
}
|
||||
builder.constant(nameString.c_str(), type, intDefault);
|
||||
builder.constant(nameString.c_str(), type, isMutable, intDefault);
|
||||
break;
|
||||
}
|
||||
case ConstantType::FLOAT: {
|
||||
@@ -383,7 +396,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
|
||||
}
|
||||
floatDefault = defaultValue->toJsonNumber()->getFloat();
|
||||
}
|
||||
builder.constant(nameString.c_str(), type, floatDefault);
|
||||
builder.constant(nameString.c_str(), type, isMutable, floatDefault);
|
||||
break;
|
||||
}
|
||||
case ConstantType::BOOL:
|
||||
@@ -396,7 +409,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
|
||||
}
|
||||
boolDefault = defaultValue->toJsonBool()->getBool();
|
||||
}
|
||||
builder.constant(nameString.c_str(), type, boolDefault);
|
||||
builder.constant(nameString.c_str(), type, isMutable, boolDefault);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -19,6 +19,7 @@ set(RESOURCE_BINS)
|
||||
set(MATERIAL_SRCS
|
||||
materials/aiDefaultMat.mat
|
||||
materials/bakedColor.mat
|
||||
materials/bakedColorWithMutableConstants.mat
|
||||
materials/bakedTexture.mat
|
||||
materials/aoPreview.mat
|
||||
materials/arrayTexture.mat
|
||||
@@ -281,6 +282,7 @@ if (NOT ANDROID)
|
||||
add_demo(lightbulb)
|
||||
add_demo(material_sandbox)
|
||||
add_demo(multiple_windows)
|
||||
add_demo(mutable_constants)
|
||||
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
|
||||
# https://github.com/gpuweb/gpuweb/issues/1190
|
||||
if (NOT FILAMENT_SUPPORTS_WEBGPU)
|
||||
|
||||
44
samples/materials/bakedColorWithMutableConstants.mat
Normal file
44
samples/materials/bakedColorWithMutableConstants.mat
Normal file
@@ -0,0 +1,44 @@
|
||||
material {
|
||||
name : BakedColorWithMutableConstants,
|
||||
requires : [
|
||||
color
|
||||
],
|
||||
constants : [
|
||||
{
|
||||
name: swizzle,
|
||||
type: bool,
|
||||
mutable: true,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: darken,
|
||||
type: bool,
|
||||
mutable: true,
|
||||
},
|
||||
{
|
||||
// In generated shader sources, we first specify immutable
|
||||
// specialization constants, then mutable specialization constants.
|
||||
// By including this dummy constant, we offset the ID of the first
|
||||
// mutable constant by one. This is done here purely for testing
|
||||
// purposes.
|
||||
name: dummy,
|
||||
type: bool,
|
||||
}
|
||||
],
|
||||
shadingModel : unlit,
|
||||
culling : none,
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
if (materialConstants_swizzle) {
|
||||
material.baseColor = getColor().gbra;
|
||||
} else {
|
||||
material.baseColor = getColor();
|
||||
}
|
||||
if (materialConstants_darken) {
|
||||
material.baseColor.rgb *= 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
214
samples/mutable_constants.cpp
Normal file
214
samples/mutable_constants.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <utils/EntityManager.h>
|
||||
|
||||
#include <filamentapp/Config.h>
|
||||
#include <filamentapp/FilamentApp.h>
|
||||
|
||||
#include <getopt/getopt.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#include "generated/resources/resources.h"
|
||||
|
||||
using namespace filament;
|
||||
using utils::Entity;
|
||||
using utils::EntityManager;
|
||||
|
||||
struct App {
|
||||
Config config;
|
||||
VertexBuffer* vb;
|
||||
IndexBuffer* ib;
|
||||
Material* mat;
|
||||
Camera* cam;
|
||||
Entity camera;
|
||||
Skybox* skybox;
|
||||
Entity renderable;
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
filament::math::float2 position;
|
||||
uint32_t color;
|
||||
};
|
||||
|
||||
static const Vertex TRIANGLE_VERTICES[3] = {
|
||||
{{1, 0}, 0xffff0000u},
|
||||
{{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3)}, 0xff00ff00u},
|
||||
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu},
|
||||
};
|
||||
|
||||
static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
|
||||
|
||||
static void printUsage(char* name) {
|
||||
std::string exec_name(utils::Path(name).getName());
|
||||
std::string usage(
|
||||
"MUTABLE_CONSTANTS renders a spinning colored triangle\n"
|
||||
"Usage:\n"
|
||||
" MUTABLE_CONSTANTS [options]\n"
|
||||
"Options:\n"
|
||||
" --help, -h\n"
|
||||
" Prints this message\n\n"
|
||||
" --api, -a\n"
|
||||
" Specify the backend API: opengl, vulkan, metal, or webgpu\n"
|
||||
" (note: webgpu is a no-op for now, printing backend\n"
|
||||
" component info if FILAMENT_BACKEND_DEBUG_FLAG, \n"
|
||||
" set at build time, includes the \n"
|
||||
" FWGPU_PRINT_SYSTEM bit flag 0x2)\n"
|
||||
);
|
||||
const std::string from("MUTABLE_CONSTANTS");
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
usage.replace(pos, from.length(), exec_name);
|
||||
}
|
||||
std::cout << usage;
|
||||
}
|
||||
|
||||
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
|
||||
static constexpr const char* OPTSTR = "ha:";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "api", required_argument, nullptr, 'a' },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
int opt;
|
||||
int option_index = 0;
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
|
||||
std::string arg(optarg ? optarg : "");
|
||||
switch (opt) {
|
||||
default:
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
exit(0);
|
||||
case 'a':
|
||||
if (arg == "opengl") {
|
||||
app->config.backend = Engine::Backend::OPENGL;
|
||||
} else if (arg == "vulkan") {
|
||||
app->config.backend = Engine::Backend::VULKAN;
|
||||
} else if (arg == "metal") {
|
||||
app->config.backend = Engine::Backend::METAL;
|
||||
} else if (arg == "webgpu") {
|
||||
app->config.backend = Engine::Backend::WEBGPU;
|
||||
} else {
|
||||
std::cerr << "Unrecognized backend. Must be "
|
||||
"'opengl'|'vulkan'|'metal'|'webgpu'.\n";
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return optind;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
App app{};
|
||||
app.config.title = "mutable_constants";
|
||||
handleCommandLineArguments(argc, argv, &app);
|
||||
|
||||
auto setup = [&app](Engine* engine, View* view, Scene* scene) {
|
||||
app.skybox = Skybox::Builder().color({0.1, 0.125, 0.25, 1.0}).build(*engine);
|
||||
scene->setSkybox(app.skybox);
|
||||
view->setPostProcessingEnabled(false);
|
||||
static_assert(sizeof(Vertex) == 12, "Strange vertex size.");
|
||||
app.vb = VertexBuffer::Builder()
|
||||
.vertexCount(3)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
|
||||
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 8, 12)
|
||||
.normalized(VertexAttribute::COLOR)
|
||||
.build(*engine);
|
||||
app.vb->setBufferAt(*engine, 0,
|
||||
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
|
||||
app.ib = IndexBuffer::Builder()
|
||||
.indexCount(3)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(*engine);
|
||||
app.ib->setBuffer(*engine,
|
||||
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
|
||||
app.mat = Material::Builder().package(RESOURCES_BAKEDCOLORWITHMUTABLECONSTANTS_DATA,
|
||||
RESOURCES_BAKEDCOLORWITHMUTABLECONSTANTS_SIZE)
|
||||
.build(*engine);
|
||||
app.renderable = EntityManager::get().create();
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
|
||||
.material(0, app.mat->getDefaultInstance())
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.vb, app.ib, 0, 3)
|
||||
.culling(false)
|
||||
.receiveShadows(false)
|
||||
.castShadows(false)
|
||||
.build(*engine, app.renderable);
|
||||
scene->addEntity(app.renderable);
|
||||
app.camera = utils::EntityManager::get().create();
|
||||
app.cam = engine->createCamera(app.camera);
|
||||
view->setCamera(app.cam);
|
||||
};
|
||||
|
||||
auto cleanup = [&app](Engine* engine, View*, Scene*) {
|
||||
engine->destroy(app.skybox);
|
||||
engine->destroy(app.renderable);
|
||||
engine->destroy(app.mat);
|
||||
engine->destroy(app.vb);
|
||||
engine->destroy(app.ib);
|
||||
engine->destroyCameraComponent(app.camera);
|
||||
utils::EntityManager::get().destroy(app.camera);
|
||||
};
|
||||
|
||||
auto gui = [&app](Engine* engine, View* view) {
|
||||
MaterialInstance* mi = app.mat->getDefaultInstance();
|
||||
bool swizzle = mi->getConstant<bool>("swizzle");
|
||||
bool darken = mi->getConstant<bool>("darken");
|
||||
ImGui::Checkbox("Swizzle", &swizzle);
|
||||
ImGui::Checkbox("Darken", &darken);
|
||||
if (swizzle != mi->getConstant<bool>("swizzle")) {
|
||||
mi->setConstant("swizzle", swizzle);
|
||||
}
|
||||
if (darken != mi->getConstant<bool>("darken")) {
|
||||
mi->setConstant("darken", darken);
|
||||
}
|
||||
};
|
||||
|
||||
FilamentApp::get().animate([&app](Engine* engine, View* view, double now) {
|
||||
constexpr float ZOOM = 1.5f;
|
||||
const uint32_t w = view->getViewport().width;
|
||||
const uint32_t h = view->getViewport().height;
|
||||
const float aspect = (float) w / h;
|
||||
app.cam->setProjection(Camera::Projection::ORTHO,
|
||||
-aspect * ZOOM, aspect * ZOOM,
|
||||
-ZOOM, ZOOM, 0, 1);
|
||||
auto& tcm = engine->getTransformManager();
|
||||
tcm.setTransform(tcm.getInstance(app.renderable),
|
||||
filament::math::mat4f::rotation(now, filament::math::float3{ 0, 0, 1 }));
|
||||
});
|
||||
|
||||
FilamentApp::get().run(app.config, setup, cleanup, gui);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user