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:
@@ -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
|
||||
|
||||
265
filament/src/LocalProgramCache.cpp
Normal file
265
filament/src/LocalProgramCache.cpp
Normal 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
|
||||
144
filament/src/LocalProgramCache.h
Normal file
144
filament/src/LocalProgramCache.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user