don't rely on FMaterialInstance having a default ctor (#7781)

* don't rely on FMaterialInstance having a default ctor

FMaterialInstance needed a default ctor because it is a field of
FMaterial but cannot be initialized before FMaterial itself is
initialized. So we had a defautl ctor and we'd finish the initialization
later. Conceptually the default material instance should have been 
new'ed and a pointer to it stored instead. 

That's basically what we do now, but to avoid the extra allocation,
we in-place new and delete the default material instance into an
aligned_storage inside FMaterial.

* Update filament/src/details/Material.h

Co-authored-by: Ben Doherty <bendoherty@google.com>

---------

Co-authored-by: Ben Doherty <bendoherty@google.com>
This commit is contained in:
Mathias Agopian
2024-04-19 14:38:30 -07:00
committed by GitHub
parent 7a3b762b53
commit ec31a516f7
4 changed files with 70 additions and 61 deletions

View File

@@ -290,7 +290,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
processDepthVariants(engine, parser);
// we can only initialize the default instance once we're initialized ourselves
mDefaultInstance.initDefaultInstance(engine, this);
new(&mDefaultInstanceStorage) FMaterialInstance(engine, this);
#if FILAMENT_ENABLE_MATDBG
@@ -303,7 +303,9 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
#endif
}
FMaterial::~FMaterial() noexcept = default;
FMaterial::~FMaterial() noexcept {
std::destroy_at(getDefaultInstance());
}
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
if (mMaterialDomain == MaterialDomain::SURFACE) {
@@ -361,7 +363,8 @@ void FMaterial::terminate(FEngine& engine) {
#endif
destroyPrograms(engine);
mDefaultInstance.terminate(engine);
getDefaultInstance()->terminate(engine);
}
void FMaterial::compile(CompilerPriorityQueue priority,
@@ -408,7 +411,7 @@ void FMaterial::compile(CompilerPriorityQueue priority,
}
FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
return FMaterialInstance::duplicate(&mDefaultInstance, name);
return FMaterialInstance::duplicate(getDefaultInstance(), name);
}
bool FMaterial::hasParameter(const char* name) const noexcept {

View File

@@ -48,6 +48,7 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <new>
#include <optional>
#include <string_view>
#include <unordered_map>
@@ -104,8 +105,13 @@ public:
BufferInterfaceBlock::FieldInfo const* reflect(std::string_view name) const noexcept;
FMaterialInstance const* getDefaultInstance() const noexcept { return &mDefaultInstance; }
FMaterialInstance* getDefaultInstance() noexcept { return &mDefaultInstance; }
FMaterialInstance const* getDefaultInstance() const noexcept {
return const_cast<FMaterial*>(this)->getDefaultInstance();
}
FMaterialInstance* getDefaultInstance() noexcept {
return std::launder(reinterpret_cast<FMaterialInstance*>(&mDefaultInstanceStorage));
}
FEngine& getEngine() const noexcept { return mEngine; }
@@ -284,7 +290,10 @@ private:
bool mIsDefaultMaterial = false;
bool mSpecularAntiAliasing = false;
FMaterialInstance mDefaultInstance;
// reserve some space to construct the default material instance
std::aligned_storage<sizeof(FMaterialInstance), alignof(FMaterialInstance)>::type mDefaultInstanceStorage;
static_assert(sizeof(mDefaultInstanceStorage) >= sizeof(mDefaultInstanceStorage));
SamplerInterfaceBlock mSamplerInterfaceBlock;
BufferInterfaceBlock mUniformInterfaceBlock;
SubpassInfo mSubpassInfo;

View File

@@ -35,14 +35,61 @@ namespace filament {
using namespace backend;
FMaterialInstance::FMaterialInstance() noexcept
: mCulling(CullingMode::BACK),
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material) noexcept
: mMaterial(material),
mCulling(CullingMode::BACK),
mDepthFunc(RasterState::DepthFunc::LE),
mColorWrite(false),
mDepthWrite(false),
mHasScissor(false),
mIsDoubleSided(false),
mTransparencyMode(TransparencyMode::DEFAULT) {
FEngine::DriverApi& driver = engine.getDriverApi();
if (!material->getUniformInterfaceBlock().isEmpty()) {
mUniforms = UniformBuffer(material->getUniformInterfaceBlock().getSize());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC);
}
if (!material->getSamplerInterfaceBlock().isEmpty()) {
mSamplers = SamplerGroup(material->getSamplerInterfaceBlock().getSize());
mSbHandle = driver.createSamplerGroup(
mSamplers.getSize(), utils::FixedSizeString<32>(mMaterial->getName().c_str_safe()));
}
const RasterState& rasterState = material->getRasterState();
// At the moment, only MaterialInstances have a stencil state, but in the future it should be
// possible to set the stencil state directly on a material (through material definitions, or
// MaterialBuilder).
// TODO: Here is where we'd "inherit" the stencil state from the Material.
// mStencilState = material->getStencilState();
// We inherit the resolved culling mode rather than the builder-set culling mode.
// This preserves the property whereby double-sidedness automatically disables culling.
mCulling = rasterState.culling;
mColorWrite = rasterState.colorWrite;
mDepthWrite = rasterState.depthWrite;
mDepthFunc = rasterState.depthFunc;
mMaterialSortingKey = RenderPass::makeMaterialSortingKey(
material->getId(), material->generateMaterialInstanceId());
if (material->getBlendingMode() == BlendingMode::MASKED) {
setMaskThreshold(material->getMaskThreshold());
}
if (material->hasDoubleSidedCapability()) {
setDoubleSided(material->isDoubleSided());
}
if (material->hasSpecularAntiAliasing()) {
setSpecularAntiAliasingVariance(material->getSpecularAntiAliasingVariance());
setSpecularAntiAliasingThreshold(material->getSpecularAntiAliasingThreshold());
}
setTransparencyMode(material->getTransparencyMode());
}
FMaterialInstance::FMaterialInstance(FEngine& engine,
@@ -103,56 +150,6 @@ FMaterialInstance* FMaterialInstance::duplicate(
return engine.createMaterialInstance(material, other, name);
}
void FMaterialInstance::initDefaultInstance(FEngine& engine, FMaterial const* material) {
FEngine::DriverApi& driver = engine.getDriverApi();
mMaterial = material;
if (!material->getUniformInterfaceBlock().isEmpty()) {
mUniforms = UniformBuffer(material->getUniformInterfaceBlock().getSize());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC);
}
if (!material->getSamplerInterfaceBlock().isEmpty()) {
mSamplers = SamplerGroup(material->getSamplerInterfaceBlock().getSize());
mSbHandle = driver.createSamplerGroup(
mSamplers.getSize(), utils::FixedSizeString<32>(mMaterial->getName().c_str_safe()));
}
const RasterState& rasterState = material->getRasterState();
// At the moment, only MaterialInstances have a stencil state, but in the future it should be
// possible to set the stencil state directly on a material (through material definitions, or
// MaterialBuilder).
// TODO: Here is where we'd "inherit" the stencil state from the Material.
// mStencilState = material->getStencilState();
// We inherit the resolved culling mode rather than the builder-set culling mode.
// This preserves the property whereby double-sidedness automatically disables culling.
mCulling = rasterState.culling;
mColorWrite = rasterState.colorWrite;
mDepthWrite = rasterState.depthWrite;
mDepthFunc = rasterState.depthFunc;
mMaterialSortingKey = RenderPass::makeMaterialSortingKey(
material->getId(), material->generateMaterialInstanceId());
if (material->getBlendingMode() == BlendingMode::MASKED) {
setMaskThreshold(material->getMaskThreshold());
}
if (material->hasDoubleSidedCapability()) {
setDoubleSided(material->isDoubleSided());
}
if (material->hasSpecularAntiAliasing()) {
setSpecularAntiAliasingVariance(material->getSpecularAntiAliasingVariance());
setSpecularAntiAliasingThreshold(material->getSpecularAntiAliasingThreshold());
}
setTransparencyMode(material->getTransparencyMode());
}
FMaterialInstance::~FMaterialInstance() noexcept = default;
void FMaterialInstance::terminate(FEngine& engine) {

View File

@@ -231,8 +231,8 @@ private:
void setParameterImpl(std::string_view name,
FTexture const* texture, TextureSampler const& sampler);
FMaterialInstance() noexcept;
void initDefaultInstance(FEngine& engine, FMaterial const* material);
// initialize the default instance
FMaterialInstance(FEngine& engine, FMaterial const* material) noexcept;
void commitSlow(FEngine::DriverApi& driver) const;