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:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user