diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index f82ad0528a..98a14f3c8b 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -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 diff --git a/filament/src/LocalProgramCache.cpp b/filament/src/LocalProgramCache.cpp new file mode 100644 index 0000000000..b79efaeb35 --- /dev/null +++ b/filament/src/LocalProgramCache.cpp @@ -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 + +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>(other.mCachedPrograms.size()); + mSpecializationConstants = other.mMaterial->getEngine() + .getMaterialCache() + .getSpecializationConstantsInternPool() + .acquire(other.mSpecializationConstants); + } + + return *this; +} + +void LocalProgramCache::initializeForMaterial(FEngine& engine, FMaterial const& material, + utils::FixedCapacityVector + 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>(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>(material.getPrograms().mCachedPrograms.size()); +} + +Handle 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 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> + constants) noexcept { + assert_invariant(mMaterial != nullptr); + + auto newSpecializationConstants = + FixedCapacityVector(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> + constants) noexcept { + assert_invariant(mMaterial != nullptr); + + auto newSpecializationConstants = + FixedCapacityVector(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 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(uint32_t id) const noexcept; +template float LocalProgramCache::getConstant(uint32_t id) const noexcept; +template bool LocalProgramCache::getConstant(uint32_t id) const noexcept; + +template int32_t LocalProgramCache::getConstant(std::string_view name) const noexcept; +template float LocalProgramCache::getConstant(std::string_view name) const noexcept; +template bool LocalProgramCache::getConstant(std::string_view name) const noexcept; + +} // namespace filament diff --git a/filament/src/LocalProgramCache.h b/filament/src/LocalProgramCache.h new file mode 100644 index 0000000000..b216aa9445 --- /dev/null +++ b/filament/src/LocalProgramCache.h @@ -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 + +#include + +#include +#include +#include + +#include + +namespace filament { + +class FMaterial; + +// L0 cache for material programs. Manages recompiling them on-demand; owned by Material and +// MaterialInstance. +class LocalProgramCache { + template + using is_supported_constant_parameter_t = + std::enable_if_t || std::is_same_v || + std::is_same_v>; + +public: + using Programs = utils::Slice>; + using SpecializationConstants = utils::Slice; + + 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 + 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 prepareProgram(backend::DriverApi& driver, + Variant const variant, + backend::CompilerPriorityQueue const priorityQueue) const noexcept { + backend::Handle 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 getProgram(Variant variant) const noexcept { + variant = filterVariantForGetProgram(variant); + backend::Handle 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> + T getConstant(uint32_t id) const noexcept { + return std::get(getConstantImpl(id)); + } + + // Get constant by name. + template> + T getConstant(std::string_view name) const noexcept { + return std::get(getConstantImpl(name)); + } + + // Set constants by ID. + void setConstants( + std::initializer_list> + constants) noexcept; + + // Set constants by name. + void setConstants(std::initializer_list< + std::pair> + constants) noexcept; + +private: + backend::Handle 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 + 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> mCachedPrograms; + SpecializationConstants mSpecializationConstants; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_LOCALPROGRAMCACHE_H diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 5d5a9a4b42..0ff6c13b35 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -998,13 +998,10 @@ FrameGraphId 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 diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index cdf7f584b6..db6aeb484a 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -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 }, + }); } }); }); diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 469e55b0e7..0c08842478 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -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 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>(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 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 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 FMaterial::processSpecializationConstants( Builder const& builder) { FixedCapacityVector 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(uint32_t id, int32_t value) noexcept; -template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set(uint32_t id, float value) noexcept; -template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set(uint32_t id, bool value) noexcept; - -template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set(std::string_view name, int32_t value) noexcept; -template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set(std::string_view name, float value) noexcept; -template FMaterial::SpecializationConstantsBuilder& FMaterial::SpecializationConstantsBuilder::set(std::string_view name, bool value) noexcept; - } // namespace filament diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index 4a61dd930f..6f3fbbce4c 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -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 #include -#include #include #include @@ -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> - SpecializationConstantsBuilder& set(uint32_t id, T value) noexcept { - // Don't allocate if we can help it. - if (mConstants.empty()) { - if (std::get(mDefaultConstants[id]) == value) { - return *this; - } - mConstants = utils::FixedCapacityVector( - mDefaultConstants); - } - mConstants[id] = value; - return *this; - } - - template> - 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 defaultConstants) - : mDefinition(&definition), - mDefaultConstants(defaultConstants) {} - - MaterialDefinition const* mDefinition; - utils::Slice mDefaultConstants; - // Copy-on-write vector. - utils::FixedCapacityVector mConstants; - }; - void terminate(FEngine& engine); // return the uniform interface block for this material @@ -187,11 +131,7 @@ public: backend::Handle prepareProgram(backend::DriverApi& driver, Variant const variant, backend::CompilerPriorityQueue const priorityQueue) const noexcept { - backend::Handle 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 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 prepareProgramSlow(backend::DriverApi& driver, - Variant const variant, - backend::CompilerPriorityQueue const priorityQueue) const noexcept; - utils::FixedCapacityVector 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> 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 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; };