engine: encapsulate material cache handling (#9663)

* engine: encapsulate material cache handling

We will soon allow `MaterialInstance` to override the value of spec constants.
To avoid code duplication, we introduce a new class `MaterialPrograms` which
handles the chunky bits of managing the program cache and values of the spec
constants.

* MaterialPrograms: add explicit initialize method

* MaterialPrograms: backport fixes, address comments

* MaterialPrograms: set all constants at once

* MaterialPrograms: rename to LocalProgramCache
This commit is contained in:
Eliza
2026-03-05 15:42:05 +09:00
committed by GitHub
parent 8a3c48fef1
commit 83653fb358
7 changed files with 474 additions and 228 deletions

View File

@@ -93,6 +93,7 @@ set(SRCS
src/MorphTargetBuffer.cpp
src/PostProcessManager.cpp
src/ProgramSpecialization.cpp
src/LocalProgramCache.cpp
src/RenderPass.cpp
src/RenderPrimitive.cpp
src/RenderTarget.cpp
@@ -186,6 +187,7 @@ set(PRIVATE_HDRS
src/MaterialCache.h
src/MaterialDefinition.h
src/MaterialParser.h
src/LocalProgramCache.h
src/MaterialInstanceManager.h
src/PIDController.h
src/PostProcessManager.h

View File

@@ -0,0 +1,265 @@
/*
* Copyright (C) 2026 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 "LocalProgramCache.h"
#include "MaterialParser.h"
#include "details/Engine.h"
#include "details/Material.h"
#include <backend/DriverApiForward.h>
namespace filament {
using namespace backend;
using namespace utils;
LocalProgramCache::LocalProgramCache(LocalProgramCache const& other)
: mMaterial(other.mMaterial),
mCachedPrograms(other.mCachedPrograms.size()),
mSpecializationConstants((other.mMaterial != nullptr)
? other.mMaterial->getEngine()
.getMaterialCache()
.getSpecializationConstantsInternPool()
.acquire(other.mSpecializationConstants)
: SpecializationConstants()) {}
LocalProgramCache& LocalProgramCache::operator=(LocalProgramCache const& other) {
assert_invariant(mMaterial == nullptr);
assert_invariant(mCachedPrograms.empty());
assert_invariant(mSpecializationConstants.empty());
mMaterial = other.mMaterial;
if (mMaterial != nullptr) {
mCachedPrograms = FixedCapacityVector<Handle<HwProgram>>(other.mCachedPrograms.size());
mSpecializationConstants = other.mMaterial->getEngine()
.getMaterialCache()
.getSpecializationConstantsInternPool()
.acquire(other.mSpecializationConstants);
}
return *this;
}
void LocalProgramCache::initializeForMaterial(FEngine& engine, FMaterial const& material,
utils::FixedCapacityVector<backend::Program::SpecializationConstant>
specializationConstants) {
assert_invariant(mMaterial == nullptr);
assert_invariant(mCachedPrograms.empty());
assert_invariant(mSpecializationConstants.empty());
mMaterial = &material;
mSpecializationConstants =
engine.getMaterialCache().getSpecializationConstantsInternPool().acquire(
std::move(specializationConstants));
size_t cachedProgramsSize;
switch (material.getMaterialDomain()) {
case filament::MaterialDomain::SURFACE:
cachedProgramsSize = 1 << VARIANT_BITS;
break;
case filament::MaterialDomain::POST_PROCESS:
cachedProgramsSize = 1 << POST_PROCESS_VARIANT_BITS;
break;
case filament::MaterialDomain::COMPUTE:
cachedProgramsSize = 1;
break;
}
mCachedPrograms = FixedCapacityVector<Handle<HwProgram>>(cachedProgramsSize);
material.getDefinition().acquirePrograms(engine, mCachedPrograms.as_slice(),
material.getMaterialParser(), mSpecializationConstants, material.isDefaultMaterial());
}
void LocalProgramCache::initializeForMaterialInstance(FEngine& engine, FMaterial const& material) {
assert_invariant(mMaterial == nullptr);
assert_invariant(mCachedPrograms.empty());
assert_invariant(mSpecializationConstants.empty());
mMaterial = &material;
LocalProgramCache const& programs = material.getPrograms();
mSpecializationConstants =
engine.getMaterialCache().getSpecializationConstantsInternPool().acquire(
programs.getSpecializationConstants());
mCachedPrograms =
FixedCapacityVector<Handle<HwProgram>>(material.getPrograms().mCachedPrograms.size());
}
Handle<HwProgram> LocalProgramCache::prepareProgramSlow(DriverApi& driver, Variant const variant,
CompilerPriorityQueue const priorityQueue) const noexcept {
assert_invariant(mMaterial != nullptr);
FEngine& engine = mMaterial->getEngine();
if (mMaterial->isSharedVariant(variant)) {
FMaterial const* defaultMaterial = engine.getDefaultMaterial();
assert_invariant(defaultMaterial);
LocalProgramCache const& defaultPrograms = defaultMaterial->getPrograms();
Handle<HwProgram> program = defaultPrograms.mCachedPrograms[variant.key];
if (program) {
return mCachedPrograms[variant.key] = program;
}
return mCachedPrograms[variant.key] =
defaultPrograms.prepareProgram(driver, variant, priorityQueue);
}
return mCachedPrograms[variant.key] = mMaterial->getDefinition().prepareProgram(engine, driver,
mMaterial->getMaterialParser(), getProgramSpecialization(variant), priorityQueue);
}
ProgramSpecialization LocalProgramCache::getProgramSpecialization(Variant variant) const noexcept {
assert_invariant(mMaterial != nullptr);
return ProgramSpecialization {
.materialCrc32 = mMaterial->getMaterialParser().getCrc32(),
.variant = variant,
.specializationConstants = mSpecializationConstants,
};
}
void LocalProgramCache::terminate(FEngine& engine) {
assert_invariant(mMaterial != nullptr);
mMaterial->getDefinition().releasePrograms(engine, mCachedPrograms.as_slice(),
mMaterial->getMaterialParser(), mSpecializationConstants,
mMaterial->isDefaultMaterial());
engine.getMaterialCache().releaseMaterial(engine, mMaterial->getDefinition());
engine.getMaterialCache().getSpecializationConstantsInternPool().release(
mSpecializationConstants);
}
void LocalProgramCache::clear(FEngine& engine) {
assert_invariant(mMaterial != nullptr);
mMaterial->getDefinition().releasePrograms(engine, mCachedPrograms.as_slice(),
mMaterial->getMaterialParser(), mSpecializationConstants,
mMaterial->isDefaultMaterial());
}
Variant LocalProgramCache::filterVariantForGetProgram(Variant variant) const noexcept {
if (UTILS_UNLIKELY(mMaterial->getEngine().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 (mMaterial->getMaterialDomain() == MaterialDomain::SURFACE) {
BlendingMode const blendingMode = mMaterial->getBlendingMode();
bool const hasScreenSpaceRefraction =
mMaterial->getRefractionMode() == RefractionMode::SCREEN_SPACE;
bool const isBlendingCommand = !hasScreenSpaceRefraction &&
(blendingMode != BlendingMode::OPAQUE && blendingMode != BlendingMode::MASKED);
if (!isBlendingCommand) {
variant.setFog(false);
}
}
}
return variant;
}
Program::SpecializationConstant LocalProgramCache::getConstantImpl(uint32_t id) const noexcept {
return mSpecializationConstants[id];
}
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);
}
std::string name_cstring(name);
PANIC_PRECONDITION("No such constant exists: %s", name_cstring.c_str());
return {};
}
void LocalProgramCache::setConstants(
std::initializer_list<std::pair<uint32_t, Program::SpecializationConstant>>
constants) noexcept {
assert_invariant(mMaterial != nullptr);
auto newSpecializationConstants =
FixedCapacityVector<Program::SpecializationConstant>(mSpecializationConstants);
bool hasChanged = false;
for (const auto& [id, value] : constants) {
if (newSpecializationConstants[id] != value) {
newSpecializationConstants[id] = value;
hasChanged = true;
}
}
if (hasChanged) {
setConstantsImpl(std::move(newSpecializationConstants));
}
}
void LocalProgramCache::setConstants(
std::initializer_list<std::pair<std::string_view, Program::SpecializationConstant>>
constants) noexcept {
assert_invariant(mMaterial != nullptr);
auto newSpecializationConstants =
FixedCapacityVector<Program::SpecializationConstant>(mSpecializationConstants);
bool hasChanged = false;
for (const auto& [name, value] : constants) {
MaterialDefinition const& definition = mMaterial->getDefinition();
auto it = definition.specializationConstantsNameToIndex.find(name);
if (it != definition.specializationConstantsNameToIndex.cend()) {
uint32_t id = it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
if (newSpecializationConstants[id] != value) {
newSpecializationConstants[id] = value;
hasChanged = true;
}
}
}
if (hasChanged) {
setConstantsImpl(std::move(newSpecializationConstants));
}
}
void LocalProgramCache::setConstantsImpl(
FixedCapacityVector<Program::SpecializationConstant> constants) noexcept {
FEngine& engine = mMaterial->getEngine();
auto& internPool = engine.getMaterialCache().getSpecializationConstantsInternPool();
MaterialParser const& materialParser = mMaterial->getMaterialParser();
MaterialDefinition const& definition = mMaterial->getDefinition();
const bool isDefaultMaterial = mMaterial->isDefaultMaterial();
// Release old resources...
definition.releasePrograms(engine, mCachedPrograms.as_slice(), materialParser,
mSpecializationConstants, isDefaultMaterial);
internPool.release(mSpecializationConstants);
// Then acquire new ones.
mSpecializationConstants = internPool.acquire(std::move(constants));
definition.acquirePrograms(engine, mCachedPrograms.as_slice(), materialParser,
mSpecializationConstants, isDefaultMaterial);
}
template int32_t LocalProgramCache::getConstant<int32_t>(uint32_t id) const noexcept;
template float LocalProgramCache::getConstant<float>(uint32_t id) const noexcept;
template bool LocalProgramCache::getConstant<bool>(uint32_t id) const noexcept;
template int32_t LocalProgramCache::getConstant<int32_t>(std::string_view name) const noexcept;
template float LocalProgramCache::getConstant<float>(std::string_view name) const noexcept;
template bool LocalProgramCache::getConstant<bool>(std::string_view name) const noexcept;
} // namespace filament

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2026 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.
*/
#ifndef TNT_FILAMENT_LOCALPROGRAMCACHE_H
#define TNT_FILAMENT_LOCALPROGRAMCACHE_H
#include "MaterialDefinition.h"
#include <backend/Handle.h>
#include <private/filament/Variant.h>
#include <backend/DriverEnums.h>
#include <backend/DriverApiForward.h>
#include <backend/Program.h>
#include <utility>
namespace filament {
class FMaterial;
// L0 cache for material programs. Manages recompiling them on-demand; owned by Material and
// MaterialInstance.
class LocalProgramCache {
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>>;
public:
using Programs = utils::Slice<const backend::Handle<backend::HwProgram>>;
using SpecializationConstants = utils::Slice<const backend::Program::SpecializationConstant>;
LocalProgramCache() = default;
LocalProgramCache(LocalProgramCache const& other);
LocalProgramCache& operator=(LocalProgramCache const& other);
// Initialize for use in a Material.
void initializeForMaterial(FEngine& engine, FMaterial const& material,
utils::FixedCapacityVector<backend::Program::SpecializationConstant>
specializationConstants);
// Initialize for use in a MaterialInstance. Copies the set of spec constants currently in use
// from its Material.
void initializeForMaterialInstance(FEngine& engine, FMaterial const& material);
bool isInitialized() const noexcept { return mMaterial != nullptr; }
// 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 {
backend::Handle<backend::HwProgram> program = mCachedPrograms[variant.key];
if (UTILS_LIKELY(program)) {
return program;
}
return prepareProgramSlow(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 {
variant = filterVariantForGetProgram(variant);
backend::Handle<backend::HwProgram> program = mCachedPrograms[variant.key];
assert_invariant(program);
return program;
}
SpecializationConstants getSpecializationConstants() const noexcept {
return mSpecializationConstants;
}
Programs getPrograms() const noexcept { return mCachedPrograms.as_slice(); }
// Free all engine resources associated with this instance.
void terminate(FEngine& engine);
// Clear all cached programs. Used primarily by matdbg to "sever" a Material's connection to the
// global material cache.
void clear(FEngine& engine);
// Get constant by ID.
template<typename T, typename = is_supported_constant_parameter_t<T>>
T getConstant(uint32_t id) const noexcept {
return std::get<T>(getConstantImpl(id));
}
// Get constant by name.
template<typename T, typename = is_supported_constant_parameter_t<T>>
T getConstant(std::string_view name) const noexcept {
return std::get<T>(getConstantImpl(name));
}
// Set constants by ID.
void setConstants(
std::initializer_list<std::pair<uint32_t, backend::Program::SpecializationConstant>>
constants) noexcept;
// Set constants by name.
void setConstants(std::initializer_list<
std::pair<std::string_view, backend::Program::SpecializationConstant>>
constants) noexcept;
private:
backend::Handle<backend::HwProgram> prepareProgramSlow(backend::DriverApi& driver,
Variant const variant,
backend::CompilerPriorityQueue const priorityQueue) const noexcept;
ProgramSpecialization getProgramSpecialization(Variant variant) const noexcept;
Variant filterVariantForGetProgram(Variant const variant) const noexcept;
void setConstantsImpl(utils::FixedCapacityVector<backend::Program::SpecializationConstant>
constants) noexcept;
backend::Program::SpecializationConstant getConstantImpl(uint32_t id) const noexcept;
backend::Program::SpecializationConstant getConstantImpl(std::string_view name) const noexcept;
FMaterial const* mMaterial = nullptr;
mutable utils::FixedCapacityVector<backend::Handle<backend::HwProgram>> mCachedPrograms;
SpecializationConstants mSpecializationConstants;
};
} // namespace filament
#endif // TNT_FILAMENT_LOCALPROGRAMCACHE_H

View File

@@ -998,13 +998,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
auto& material = getPostProcessMaterial(materialName);
FMaterial* ma = material.getMaterial(mEngine, driver);
{
FMaterial::SpecializationConstantsBuilder maConstants =
ma->getSpecializationConstantsBuilder();
maConstants.set("useVisibilityBitmasks", options.gtao.useVisibilityBitmasks);
maConstants.set("linearThickness", options.gtao.linearThickness);
ma->setSpecializationConstants(std::move(maConstants));
}
ma->getPrograms().setConstants({
{ "useVisibilityBitmasks", options.gtao.useVisibilityBitmasks },
{ "linearThickness", options.gtao.linearThickness },
});
ma = material.getMaterial(mEngine, driver);
FMaterialInstance* const mi = getMaterialInstance(ma);
@@ -2917,20 +2914,18 @@ void PostProcessManager::configureTemporalAntiAliasingMaterial(backend::DriverAp
TemporalAntiAliasingOptions const& taaOptions) noexcept {
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine, driver);
FMaterial::SpecializationConstantsBuilder maConstants = ma->getSpecializationConstantsBuilder();
maConstants.set("upscaling", taaOptions.upscaling > 1.0f);
maConstants.set("historyReprojection", taaOptions.historyReprojection);
maConstants.set("filterHistory", taaOptions.filterHistory);
maConstants.set("filterInput", taaOptions.filterInput);
maConstants.set("useYCoCg", taaOptions.useYCoCg);
maConstants.set("hdr", taaOptions.hdr);
maConstants.set("preventFlickering", taaOptions.preventFlickering);
maConstants.set("boxType", int32_t(taaOptions.boxType));
maConstants.set("boxClipping", int32_t(taaOptions.boxClipping));
maConstants.set("varianceGamma", taaOptions.varianceGamma);
ma->setSpecializationConstants(std::move(maConstants));
ma->getPrograms().setConstants({
{ "upscaling", taaOptions.upscaling > 1.0f },
{ "historyReprojection", taaOptions.historyReprojection },
{ "filterHistory", taaOptions.filterHistory },
{ "filterInput", taaOptions.filterInput },
{ "useYCoCg", taaOptions.useYCoCg },
{ "hdr", taaOptions.hdr },
{ "preventFlickering", taaOptions.preventFlickering },
{ "boxType", int32_t(taaOptions.boxType) },
{ "boxClipping", int32_t(taaOptions.boxClipping) },
{ "varianceGamma", taaOptions.varianceGamma },
});
}
FMaterialInstance* PostProcessManager::configureColorGradingMaterial(backend::DriverApi& driver,
@@ -2938,13 +2933,10 @@ FMaterialInstance* PostProcessManager::configureColorGradingMaterial(backend::Dr
ColorGradingConfig const& colorGradingConfig, VignetteOptions const& vignetteOptions,
uint32_t const width, uint32_t const height) noexcept {
FMaterial* ma = material.getMaterial(mEngine, driver);
{
FMaterial::SpecializationConstantsBuilder maConstants =
ma->getSpecializationConstantsBuilder();
maConstants.set("isOneDimensional", colorGrading->isOneDimensional());
maConstants.set("isLDR", colorGrading->isLDR());
ma->setSpecializationConstants(std::move(maConstants));
}
ma->getPrograms().setConstants({
{ "isOneDimensional", colorGrading->isOneDimensional() },
{ "isLDR", colorGrading->isLDR() },
});
PostProcessVariant const variant = colorGradingConfig.translucent
? PostProcessVariant::TRANSLUCENT

View File

@@ -544,12 +544,10 @@ void FEngine::init() {
&debug.shadowmap.debug_directional_shadowmap, [this] {
mMaterials.forEach([this](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
FMaterial::SpecializationConstantsBuilder constants =
material->getSpecializationConstantsBuilder();
constants.set(+ReservedSpecializationConstants::
CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP,
debug.shadowmap.debug_directional_shadowmap);
material->setSpecializationConstants(std::move(constants));
material->getPrograms().setConstants({
{ +ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP,
debug.shadowmap.debug_directional_shadowmap },
});
}
});
});
@@ -558,12 +556,10 @@ void FEngine::init() {
&debug.lighting.debug_froxel_visualization, [this] {
mMaterials.forEach([this](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
FMaterial::SpecializationConstantsBuilder constants =
material->getSpecializationConstantsBuilder();
constants.set(+ReservedSpecializationConstants::
CONFIG_DEBUG_FROXEL_VISUALIZATION,
debug.lighting.debug_froxel_visualization);
material->setSpecializationConstants(std::move(constants));
material->getPrograms().setConstants({
{ +ReservedSpecializationConstants::CONFIG_DEBUG_FROXEL_VISUALIZATION,
debug.lighting.debug_froxel_visualization },
});
}
});
});

View File

@@ -176,28 +176,7 @@ FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition
driver.isWorkaroundNeeded(Workaround::DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL);
mDefaultMaterial = engine.getDefaultMaterial();
FixedCapacityVector<Program::SpecializationConstant> specializationConstants =
processSpecializationConstants(builder);
mSpecializationConstants =
engine.getMaterialCache().getSpecializationConstantsInternPool().acquire(
std::move(specializationConstants));
size_t cachedProgramsSize;
switch (mDefinition.materialDomain) {
case filament::MaterialDomain::SURFACE:
cachedProgramsSize = 1 << VARIANT_BITS;
break;
case filament::MaterialDomain::POST_PROCESS:
cachedProgramsSize = 1 << POST_PROCESS_VARIANT_BITS;
break;
case filament::MaterialDomain::COMPUTE:
cachedProgramsSize = 1;
break;
}
mCachedPrograms = FixedCapacityVector<backend::Handle<backend::HwProgram>>(cachedProgramsSize);
mDefinition.acquirePrograms(engine, mCachedPrograms.as_slice(), getMaterialParser(),
mSpecializationConstants, mIsDefaultMaterial);
mPrograms.initializeForMaterial(engine, *this, processSpecializationConstants(builder));
#if FILAMENT_ENABLE_MATDBG
// Register the material with matdbg.
@@ -237,11 +216,7 @@ void FMaterial::terminate(FEngine& engine) {
}
#endif
mDefinition.releasePrograms(engine, mCachedPrograms.as_slice(),
getMaterialParser(), mSpecializationConstants, mIsDefaultMaterial);
engine.getMaterialCache().releaseMaterial(engine, mDefinition);
engine.getMaterialCache().getSpecializationConstantsInternPool().release(
mSpecializationConstants);
mPrograms.terminate(engine);
}
filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
@@ -306,22 +281,6 @@ void FMaterial::compile(CompilerPriorityQueue const priority,
}
}
Handle<HwProgram> FMaterial::prepareProgramSlow(DriverApi& driver, Variant const variant,
CompilerPriorityQueue const priorityQueue) const noexcept {
if (isSharedVariant(variant)) {
FMaterial const* defaultMaterial = mEngine.getDefaultMaterial();
FILAMENT_CHECK_PRECONDITION(defaultMaterial);
Handle<HwProgram> program = defaultMaterial->mCachedPrograms[variant.key];
if (program) {
return mCachedPrograms[variant.key] = program;
}
return mCachedPrograms[variant.key] =
defaultMaterial->prepareProgram(driver, variant, priorityQueue);
}
return mCachedPrograms[variant.key] = mDefinition.prepareProgram(mEngine, driver,
getMaterialParser(), getProgramSpecialization(variant), priorityQueue);
}
FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
if (mDefaultMaterialInstance) {
// if we have a default instance, use it to create a new one
@@ -356,14 +315,6 @@ BufferInterfaceBlock::FieldInfo const* FMaterial::reflect(
return mDefinition.uniformInterfaceBlock.getFieldInfo(name);
}
ProgramSpecialization FMaterial::getProgramSpecialization(Variant const variant) const noexcept {
return ProgramSpecialization {
.materialCrc32 = mDefinition.getMaterialParser().getCrc32(),
.variant = variant,
.specializationConstants = mSpecializationConstants,
};
}
size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const noexcept {
count = std::min(count, getParameterCount());
@@ -430,8 +381,7 @@ void FMaterial::updateActiveProgramsForMatdbg(Variant const variant) const noexc
void FMaterial::applyPendingEdits() noexcept {
const char* name = mDefinition.name.c_str();
DLOG(INFO) << "Applying edits to " << (name ? name : "(untitled)");
mDefinition.releasePrograms(mEngine, mCachedPrograms.as_slice(), getMaterialParser(),
mSpecializationConstants, mIsDefaultMaterial);
mPrograms.clear(mEngine);
latchPendingEdits();
}
@@ -480,26 +430,6 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
#endif // FILAMENT_ENABLE_MATDBG
void FMaterial::setSpecializationConstants(SpecializationConstantsBuilder&& builder) noexcept {
if (builder.mConstants.empty()) {
// Nothing was changed.
return;
}
auto& internPool = mEngine.getMaterialCache().getSpecializationConstantsInternPool();
MaterialParser const& materialParser = getMaterialParser();
// Release old resources...
mDefinition.releasePrograms(mEngine, mCachedPrograms.as_slice(), materialParser,
mSpecializationConstants, mIsDefaultMaterial);
internPool.release(mSpecializationConstants);
// Then acquire new ones.
mSpecializationConstants = internPool.acquire(std::move(builder.mConstants));
mDefinition.acquirePrograms(mEngine, mCachedPrograms.as_slice(), materialParser,
mSpecializationConstants, mIsDefaultMaterial);
}
FixedCapacityVector<Program::SpecializationConstant> FMaterial::processSpecializationConstants(
Builder const& builder) {
FixedCapacityVector<Program::SpecializationConstant> specializationConstants =
@@ -560,12 +490,4 @@ const char* FMaterial::getParameterTransformName(std::string_view samplerName) c
return info->transformName.c_str();
}
template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set<int32_t>(uint32_t id, int32_t value) noexcept;
template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set<float>(uint32_t id, float value) noexcept;
template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set<bool>(uint32_t id, bool value) noexcept;
template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set<int32_t>(std::string_view name, int32_t value) noexcept;
template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set<float>(std::string_view name, float value) noexcept;
template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set<bool>(std::string_view name, bool value) noexcept;
} // namespace filament

View File

@@ -18,6 +18,7 @@
#define TNT_FILAMENT_DETAILS_MATERIAL_H
#include "downcast.h"
#include "LocalProgramCache.h"
#include "details/MaterialInstance.h"
@@ -45,7 +46,6 @@
#include <utils/Invocable.h>
#include <utils/Mutex.h>
#include <optional>
#include <string_view>
#include <stddef.h>
@@ -72,62 +72,6 @@ public:
DefaultMaterialBuilder();
};
// Used to change specialization constants at runtime internally by Filament.
//
// Call Material::getSpecializationConstantsBuilder() to return an instance of
// SpecializationConstantsBuilder. The lifetime of this object must not exceed the lifetime of
// the corresponding Material. After spec constants are changed with calls to set(), apply the
// changes to the original Material via setSpecializationConstants().
//
// It would be nice to reuse Builder for this, but Builder allocates an entire map and asserts
// if specified constants don't exist. In constrast, SpecializationConstantsBuilder is
// copy-on-write and allows non-existent specialization constants to be named.
class SpecializationConstantsBuilder {
friend class FMaterial;
public:
SpecializationConstantsBuilder(SpecializationConstantsBuilder const& rhs) = delete;
SpecializationConstantsBuilder& operator=(
SpecializationConstantsBuilder const& rhs) = delete;
SpecializationConstantsBuilder(SpecializationConstantsBuilder&& rhs) = default;
SpecializationConstantsBuilder& operator=(SpecializationConstantsBuilder&& rhs) = default;
template<typename T, typename = Builder::is_supported_constant_parameter_t<T>>
SpecializationConstantsBuilder& set(uint32_t id, T value) noexcept {
// Don't allocate if we can help it.
if (mConstants.empty()) {
if (std::get<T>(mDefaultConstants[id]) == value) {
return *this;
}
mConstants = utils::FixedCapacityVector<backend::Program::SpecializationConstant>(
mDefaultConstants);
}
mConstants[id] = value;
return *this;
}
template<typename T, typename = Builder::is_supported_constant_parameter_t<T>>
SpecializationConstantsBuilder& set(std::string_view name, T value) noexcept {
auto it = mDefinition->specializationConstantsNameToIndex.find(name);
if (it != mDefinition->specializationConstantsNameToIndex.cend()) {
set(it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS, value);
}
return *this;
}
private:
SpecializationConstantsBuilder(MaterialDefinition const& definition,
utils::Slice<const backend::Program::SpecializationConstant> defaultConstants)
: mDefinition(&definition),
mDefaultConstants(defaultConstants) {}
MaterialDefinition const* mDefinition;
utils::Slice<const backend::Program::SpecializationConstant> mDefaultConstants;
// Copy-on-write vector.
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mConstants;
};
void terminate(FEngine& engine);
// return the uniform interface block for this material
@@ -187,11 +131,7 @@ public:
backend::Handle<backend::HwProgram> prepareProgram(backend::DriverApi& driver,
Variant const variant,
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
backend::Handle<backend::HwProgram> program = mCachedPrograms[variant.key];
if (UTILS_LIKELY(program)) {
return program;
}
return prepareProgramSlow(driver, variant, priorityQueue);
return mPrograms.prepareProgram(driver, variant, priorityQueue);
}
// getProgram returns the backend program for the material's given variant.
@@ -216,9 +156,7 @@ public:
#if FILAMENT_ENABLE_MATDBG
updateActiveProgramsForMatdbg(variant);
#endif
backend::Handle<backend::HwProgram> program = mCachedPrograms[variant.key];
assert_invariant(program);
return program;
return mPrograms.getProgram(variant);
}
// MaterialInstance::use() binds descriptor sets before drawing. For shared variants,
@@ -275,6 +213,8 @@ public:
float getSpecularAntiAliasingVariance() const noexcept { return mDefinition.specularAntiAliasingVariance; }
float getSpecularAntiAliasingThreshold() const noexcept { return mDefinition.specularAntiAliasingThreshold; }
bool isDefaultMaterial() const noexcept { return mIsDefaultMaterial; }
backend::descriptor_binding_t getSamplerBinding(
std::string_view const& name) const;
@@ -297,12 +237,7 @@ public:
uint32_t generateMaterialInstanceId() const noexcept { return mMaterialInstanceId++; }
// Update specialization constants.
SpecializationConstantsBuilder getSpecializationConstantsBuilder() const noexcept {
return SpecializationConstantsBuilder(mDefinition, mSpecializationConstants);
}
void setSpecializationConstants(SpecializationConstantsBuilder&& builder) noexcept;
LocalProgramCache& getPrograms() noexcept { return mPrograms; }
uint8_t getPerViewLayoutIndex() const noexcept {
return mDefinition.perViewLayoutIndex;
@@ -316,6 +251,30 @@ public:
return mDefinition.source.c_str_safe();
}
inline bool isSharedVariant(Variant const variant) const {
// HACK: The default material "should" have MNT | DEP, but then we'd have to compile it as a
// lit material, which would increase binary size. Perhaps we could specially compile it
// with this variant, but with the shader program cache in active development, the days of
// the default material are numbered anyway.
constexpr Variant::type_t vsmAndDep = Variant::MNT | Variant::DEP;
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
(variant.key & vsmAndDep) != vsmAndDep;
}
MaterialParser const& getMaterialParser() const noexcept {
#if FILAMENT_ENABLE_MATDBG
if (mEditedMaterialParser) {
return *mEditedMaterialParser;
}
#endif
return mDefinition.getMaterialParser();
}
MaterialDefinition const& getDefinition() const noexcept { return mDefinition; }
LocalProgramCache const& getPrograms() const noexcept { return mPrograms; }
#if FILAMENT_ENABLE_MATDBG
void applyPendingEdits() noexcept;
@@ -349,21 +308,6 @@ public:
#endif
private:
MaterialParser const& getMaterialParser() const noexcept {
#if FILAMENT_ENABLE_MATDBG
if (mEditedMaterialParser) {
return *mEditedMaterialParser;
}
#endif
return mDefinition.getMaterialParser();
}
ProgramSpecialization getProgramSpecialization(Variant const variant) const noexcept;
backend::Handle<backend::HwProgram> prepareProgramSlow(backend::DriverApi& driver,
Variant const variant,
backend::CompilerPriorityQueue const priorityQueue) const noexcept;
utils::FixedCapacityVector<backend::Program::SpecializationConstant>
processSpecializationConstants(Builder const& builder);
@@ -371,24 +315,6 @@ private:
void createAndCacheProgram(backend::DriverApi& driver, backend::Program&& p, Variant variant) const noexcept;
backend::DescriptorSetLayout const& getPerViewDescriptorSetLayoutDescription(
Variant variant, bool useVsmDescriptorSetLayout) const noexcept;
backend::DescriptorSetLayout const& getDescriptorSetLayoutDescription(
Variant variant = {}) const noexcept;
bool isSharedVariant(Variant const variant) const {
// HACK: The default material "should" have MNT | DEP, but then we'd have to compile it as a
// lit material, which would increase binary size. Perhaps we could specially compile it
// with this variant, but with the shader program cache in active development, the days of
// the default material are numbered anyway.
constexpr Variant::type_t vsmAndDep = Variant::MNT | Variant::DEP;
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
(variant.key & vsmAndDep) != vsmAndDep;
}
mutable utils::FixedCapacityVector<backend::Handle<backend::HwProgram>> mCachedPrograms;
MaterialDefinition const& mDefinition;
bool mIsDefaultMaterial = false;
@@ -403,9 +329,6 @@ private:
// reserve some space to construct the default material instance
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
// current specialization constants for the HwProgram
utils::Slice<const backend::Program::SpecializationConstant> mSpecializationConstants;
#if FILAMENT_ENABLE_MATDBG
matdbg::MaterialKey mDebuggerId;
mutable utils::Mutex mActiveProgramsLock;
@@ -423,6 +346,8 @@ private:
FEngine& mEngine;
const uint32_t mMaterialId;
mutable uint32_t mMaterialInstanceId = 0;
LocalProgramCache mPrograms;
};