Compare commits

..

1 Commits

Author SHA1 Message Date
Powei Feng
36c76daf34 material: fix depth variant leak
The default material does not cover all of the depth variants,
and so for the client material's depth variants (with no custom
depth shader), we need to check if the program is allocated for
the material or if it is actually part of the default material.
2024-11-14 15:13:48 -08:00
15 changed files with 115 additions and 226 deletions

View File

@@ -7,3 +7,4 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- vk: fix stage pool gc logic

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.56.3'
implementation 'com.google.android.filament:filament-android:1.56.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.56.3'
pod 'Filament', '~> 1.56.0'
```
## Documentation

View File

@@ -7,13 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.56.3
## v1.56.2
- vk: fix stage pool gc logic
## v1.56.1
## v1.56.0

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.56.3
VERSION_NAME=1.56.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -529,31 +529,19 @@ void FEngine::shutdown() {
mPerViewDescriptorSetLayoutSsrVariant.terminate(mHwDescriptorSetLayoutFactory, driver);
mPerRenderableDescriptorSetLayout.terminate(mHwDescriptorSetLayoutFactory, driver);
driver.destroyRenderPrimitive(std::move(mFullScreenTriangleRph));
driver.destroyRenderPrimitive(mFullScreenTriangleRph);
destroy(mFullScreenTriangleIb);
mFullScreenTriangleIb = nullptr;
destroy(mFullScreenTriangleVb);
mFullScreenTriangleVb = nullptr;
destroy(mDummyMorphTargetBuffer);
mDummyMorphTargetBuffer = nullptr;
destroy(mDefaultIblTexture);
mDefaultIblTexture = nullptr;
destroy(mDefaultIbl);
mDefaultIbl = nullptr;
destroy(mDefaultColorGrading);
mDefaultColorGrading = nullptr;
destroy(mDefaultMaterial);
mDefaultMaterial = nullptr;
destroy(mUnprotectedDummySwapchain);
mUnprotectedDummySwapchain = nullptr;
/*
* clean-up after the user -- we call terminate on each "leaked" object and clear each list.
@@ -570,7 +558,6 @@ void FEngine::shutdown() {
// this must be done after Skyboxes and before materials
destroy(mSkyboxMaterial);
mSkyboxMaterial = nullptr;
cleanupResourceList(std::move(mBufferObjects));
cleanupResourceList(std::move(mIndexBuffers));
@@ -587,12 +574,12 @@ void FEngine::shutdown() {
cleanupResourceListLocked(mFenceListLock, std::move(mFences));
driver.destroyTexture(std::move(mDummyOneTexture));
driver.destroyTexture(std::move(mDummyOneTextureArray));
driver.destroyTexture(std::move(mDummyZeroTexture));
driver.destroyTexture(std::move(mDummyZeroTextureArray));
driver.destroyTexture(mDummyOneTexture);
driver.destroyTexture(mDummyOneTextureArray);
driver.destroyTexture(mDummyZeroTexture);
driver.destroyTexture(mDummyZeroTextureArray);
driver.destroyRenderTarget(std::move(mDefaultRenderTarget));
driver.destroyRenderTarget(mDefaultRenderTarget);
/*
* Shutdown the backend...

View File

@@ -321,7 +321,11 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
parser->getSpecularAntiAliasingThreshold(&mSpecularAntiAliasingThreshold);
}
parser->hasCustomDepthShader(&mHasCustomDepthShader);
processBlendingMode(parser);
processSpecializationConstants(engine, builder, parser);
processPushConstants(engine, parser);
processDepthVariants(engine, parser);
processDescriptorSets(engine, parser);
mPerViewLayoutIndex = ColorPassDescriptorSet::getIndex(
mIsVariantLit,
@@ -329,12 +333,6 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
mRefractionMode == RefractionMode::SCREEN_SPACE,
!(mVariantFilterMask & +UserVariantFilterBit::FOG));
processBlendingMode(parser);
processSpecializationConstants(engine, builder, parser);
processPushConstants(engine, parser);
processDescriptorSets(engine, parser);
precacheDepthVariants(engine);
#if FILAMENT_ENABLE_MATDBG
// Register the material with matdbg.
matdbg::DebugServer* server = downcast(engine).debug.server;
@@ -348,21 +346,53 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
FMaterial::~FMaterial() noexcept = default;
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
// Note: This API is not public at the moment, so it's okay to have some debugging logs
// and extra checks.
if (mMaterialDomain == MaterialDomain::SURFACE &&
!mIsDefaultMaterial &&
!mHasCustomDepthShader) {
// it would be unsafe to invalidate any of the cached depth variant
if (UTILS_UNLIKELY(!((variantMask & Variant::DEP) && !(variantValue & Variant::DEP)))) {
slog.w << io::hex << "FMaterial::invalidate("
<< +variantMask << ", " << +variantValue
<< ") would corrupt the depth variant cache" << io::endl;
DriverApi& driverApi = mEngine.getDriverApi();
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
auto hasDefaultDepthVariant = [pDefaultMaterial](Variant k) {
auto const& end = pDefaultMaterial->mDepthVariants.end();
return end != std::find(pDefaultMaterial->mDepthVariants.begin(), end, k);
};
if (mMaterialDomain == MaterialDomain::SURFACE) {
auto& cachedPrograms = mCachedPrograms;
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
Variant const variant(k);
if ((k & variantMask) == variantValue) {
if (UTILS_LIKELY(!mIsDefaultMaterial)) {
// The depth variants may be shared with the default material, in which case
// we should not free it now.
bool const isSharedVariant = Variant::isValidDepthVariant(variant) &&
!mHasCustomDepthShader &&
hasDefaultDepthVariant(variant);
if (isSharedVariant) {
// we don't own this variant, skip.
continue;
}
}
driverApi.destroyProgram(cachedPrograms[k]);
cachedPrograms[k].clear();
}
}
variantMask |= Variant::DEP;
variantValue &= ~Variant::DEP;
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
for (Variant const variant: pDefaultMaterial->mDepthVariants) {
pDefaultMaterial->prepareProgram(variant);
if (!cachedPrograms[variant.key]) {
cachedPrograms[variant.key] = pDefaultMaterial->getProgram(variant);
}
}
}
} else if (mMaterialDomain == MaterialDomain::POST_PROCESS) {
auto& cachedPrograms = mCachedPrograms;
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
if ((k & variantMask) == variantValue) {
driverApi.destroyProgram(cachedPrograms[k]);
cachedPrograms[k].clear();
}
}
} else if (mMaterialDomain == MaterialDomain::COMPUTE) {
// TODO: handle compute variants if any
}
destroyPrograms(mEngine, variantMask, variantValue);
}
void FMaterial::terminate(FEngine& engine) {
@@ -602,42 +632,11 @@ Program FMaterial::getProgramWithVariants(
}
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
FEngine const& engine = mEngine;
DriverApi& driverApi = mEngine.getDriverApi();
bool const isSharedVariant =
(mMaterialDomain == MaterialDomain::SURFACE) &&
!mIsDefaultMaterial && !mHasCustomDepthShader &&
Variant::isValidDepthVariant(variant);
// Check if the default material has this program cached
if (isSharedVariant) {
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
if (pDefaultMaterial) {
auto program = pDefaultMaterial->mCachedPrograms[variant.key];
if (program) {
mCachedPrograms[variant.key] = program;
return;
}
}
}
auto program = driverApi.createProgram(std::move(p));
driverApi.setDebugTag(program.getId(), mName);
assert_invariant(!mCachedPrograms[variant.key]);
auto program = mEngine.getDriverApi().createProgram(std::move(p));
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
assert_invariant(program);
mCachedPrograms[variant.key] = program;
// If the default material doesn't already have this program cached, and all caching conditions
// are met (Surface Domain and no custom depth shader), cache it now.
// New Materials will inherit these program automatically.
if (isSharedVariant) {
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[variant.key]) {
// set the tag to the default material name
driverApi.setDebugTag(program.getId(), mName);
pDefaultMaterial->mCachedPrograms[variant.key] = program;
}
}
}
size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const noexcept {
@@ -742,76 +741,29 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pVariants) {
#endif // FILAMENT_ENABLE_MATDBG
void FMaterial::destroyPrograms(FEngine& engine,
Variant::type_t const variantMask, Variant::type_t const variantValue) {
void FMaterial::destroyPrograms(FEngine& engine) {
DriverApi& driverApi = engine.getDriverApi();
auto& cachedPrograms = mCachedPrograms;
switch (mMaterialDomain) {
case MaterialDomain::SURFACE: {
if (mIsDefaultMaterial || mHasCustomDepthShader) {
// default material or we have custom depth shaders, we destroy all variants
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
if ((k & variantMask) == variantValue) {
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
// of variants, and this generates traffic in the command queue.
if (cachedPrograms[k]) {
driverApi.destroyProgram(std::move(cachedPrograms[k]));
}
}
}
} else {
// The depth variants may be shared with the default material, in which case
// we should not free them now.
auto hasDefaultDepthVariant = [pDefaultMaterial = engine.getDefaultMaterial()](Variant k) {
auto const& end = pDefaultMaterial->mDepthVariants.end();
return end != std::find(pDefaultMaterial->mDepthVariants.begin(), end, k);
};
// During Engine::shutdown(), auto-cleanup destroys the default material first,
// so this can be null, but this is only used for debugging.
UTILS_UNUSED_IN_RELEASE
auto UTILS_NULLABLE pDefaultMaterial = engine.getDefaultMaterial();
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
if ((k & variantMask) == variantValue) {
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
// of variant, and this generates traffic in the command queue.
if (cachedPrograms[k]) {
if (Variant::isValidDepthVariant(Variant(k))) {
// By construction this should always be true, because this
// field is populated only when a material creates the program
// for this variant.
// During Engine::shutdown, auto-cleanup destroys the
// default material first
assert_invariant(!pDefaultMaterial ||
pDefaultMaterial->mCachedPrograms[k]);
// we don't own this variant, skip, but clear the entry.
cachedPrograms[k].clear();
continue;
}
driverApi.destroyProgram(std::move(cachedPrograms[k]));
}
}
}
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
const Variant variant(k);
if (!mIsDefaultMaterial) {
// The depth variants may be shared with the default material, in which case
// we should not free it now.
bool const isSharedVariant = Variant::isValidDepthVariant(variant) &&
!mHasCustomDepthShader && hasDefaultDepthVariant(variant);
if (isSharedVariant) {
// we don't own this variant, skip.
continue;
}
break;
}
case MaterialDomain::POST_PROCESS: {
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
if ((k & variantMask) == variantValue) {
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
// of variant, and this generates traffic in the command queue.
if (cachedPrograms[k]) {
driverApi.destroyProgram(std::move(cachedPrograms[k]));
}
}
}
break;
}
case MaterialDomain::COMPUTE: {
// Compute programs don't have variants
driverApi.destroyProgram(std::move(cachedPrograms[0]));
break;
}
driverApi.destroyProgram(cachedPrograms[k]);
cachedPrograms[k].clear();
}
}
@@ -1076,32 +1028,34 @@ void FMaterial::processPushConstants(FEngine& engine, MaterialParser const* pars
});
}
void FMaterial::precacheDepthVariants(FEngine& engine) {
// pre-cache all depth variants inside the default material. Note that this should be
// entirely optional; if we remove this pre-caching, these variants will be populated
// later, when/if needed by createAndCacheProgram(). Doing it now potentially uses more
// memory and increases init time, but reduces hiccups during the first frame.
void FMaterial::processDepthVariants(FEngine& engine, MaterialParser const* const parser) {
parser->hasCustomDepthShader(&mHasCustomDepthShader);
if (UTILS_UNLIKELY(mIsDefaultMaterial)) {
auto const allDepthVariants = VariantUtils::getDepthVariants();
for (auto const variant: allDepthVariants) {
assert_invariant(Variant::isValidDepthVariant(variant));
if (hasVariant(variant)) {
prepareProgram(variant);
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
filaflat::MaterialChunk const& materialChunk{ parser->getMaterialChunk() };
auto variants = FixedCapacityVector<Variant>::with_capacity(materialChunk.getShaderCount());
materialChunk.visitShaders([&variants](
ShaderModel, Variant variant, ShaderStage) {
if (Variant::isValidDepthVariant(variant)) {
variants.push_back(variant);
}
}
return;
});
std::sort(variants.begin(), variants.end(),
[](Variant lhs, Variant rhs) { return lhs.key < rhs.key; });
auto pos = std::unique(variants.begin(), variants.end());
variants.resize(std::distance(variants.begin(), pos));
std::swap(mDepthVariants, variants);
}
// if possible pre-cache all depth variants from the default material
if (mMaterialDomain == MaterialDomain::SURFACE &&
!mIsDefaultMaterial &&
!mHasCustomDepthShader) {
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
assert_invariant(pDefaultMaterial);
auto const allDepthVariants = VariantUtils::getDepthVariants();
for (auto const variant: allDepthVariants) {
assert_invariant(Variant::isValidDepthVariant(variant));
mCachedPrograms[variant.key] = pDefaultMaterial->mCachedPrograms[variant.key];
if (mMaterialDomain == MaterialDomain::SURFACE) {
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
auto& cachedPrograms = mCachedPrograms;
for (Variant const variant: pDefaultMaterial->mDepthVariants) {
pDefaultMaterial->prepareProgram(variant);
cachedPrograms[variant.key] = pDefaultMaterial->getProgram(variant);
}
}
}
}

View File

@@ -218,9 +218,7 @@ public:
uint32_t generateMaterialInstanceId() const noexcept { return mMaterialInstanceId++; }
void destroyPrograms(FEngine& engine,
Variant::type_t variantMask = 0,
Variant::type_t variantValue = 0);
void destroyPrograms(FEngine& engine);
// return the id of a specialization constant specified by name for this material
std::optional<uint32_t> getSpecializationConstantId(std::string_view name) const noexcept ;
@@ -284,7 +282,7 @@ private:
void processPushConstants(FEngine& engine, MaterialParser const* parser);
void precacheDepthVariants(FEngine& engine);
void processDepthVariants(FEngine& engine, MaterialParser const* parser);
void processDescriptorSets(FEngine& engine, MaterialParser const* parser);
@@ -333,6 +331,7 @@ private:
SamplerInterfaceBlock mSamplerInterfaceBlock;
BufferInterfaceBlock mUniformInterfaceBlock;
SubpassInfo mSubpassInfo;
utils::FixedCapacityVector<Variant> mDepthVariants; // only populated with default material
using BindingUniformInfoContainer = utils::FixedCapacityVector<std::tuple<
uint8_t, utils::CString, backend::Program::UniformInfo>>;

View File

@@ -534,7 +534,7 @@ private:
Viewport mViewport;
bool mCulling = true;
bool mFrontFaceWindingInverted = false;
bool mIsTransparentPickingEnabled = false;
bool mIsTransparentPickingEnabled = true;
FRenderTarget* mRenderTarget = nullptr;

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.56.3"
spec.version = "1.56.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.3/filament-v1.56.3-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.0/filament-v1.56.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 56;
static constexpr size_t MATERIAL_VERSION = 55;
/**
* Supported shading models

View File

@@ -19,7 +19,6 @@
#include <filament/MaterialEnums.h>
#include <utils/compiler.h>
#include <utils/bitset.h>
#include <utils/Slice.h>
@@ -272,8 +271,6 @@ namespace VariantUtils {
utils::Slice<Variant> getLitVariants() noexcept UTILS_PURE;
// list of unlit variants
utils::Slice<Variant> getUnlitVariants() noexcept UTILS_PURE;
// list of depth variants
utils::Slice<Variant> getDepthVariants() noexcept UTILS_PURE;
}
} // namespace filament

View File

@@ -16,15 +16,8 @@
#include <private/filament/Variant.h>
#include <filament/MaterialEnums.h>
#include <utils/Slice.h>
#include <array>
#include <stddef.h>
#include <stdint.h>
namespace filament {
Variant Variant::filterUserVariant(
@@ -72,8 +65,6 @@ Variant Variant::filterUserVariant(
namespace details {
namespace {
// Compile-time variant count for lit and unlit
constexpr inline size_t variant_count(bool lit) noexcept {
size_t count = 0;
@@ -90,17 +81,6 @@ constexpr inline size_t variant_count(bool lit) noexcept {
return count;
}
constexpr inline size_t depth_variant_count() noexcept {
size_t count = 0;
for (size_t i = 0; i < VARIANT_COUNT; i++) {
Variant const variant(i);
if (Variant::isValidDepthVariant(variant)) {
count++;
}
}
return count;
}
// Compile-time variant list for lit and unlit
template<bool LIT>
constexpr auto get_variants() noexcept {
@@ -118,18 +98,9 @@ constexpr auto get_variants() noexcept {
}
return variants;
}
static auto const gLitVariants{ details::get_variants<true>() };
static auto const gUnlitVariants{ details::get_variants<false>() };
constexpr auto get_depth_variants() noexcept {
std::array<Variant, depth_variant_count()> variants;
size_t count = 0;
for (size_t i = 0; i < VARIANT_COUNT; i++) {
Variant const variant(i);
if (Variant::isValidDepthVariant(variant)) {
variants[count++] = variant;
}
}
return variants;
}
// Below are compile time sanity-check tests
constexpr inline bool reserved_is_not_valid() noexcept {
@@ -192,13 +163,6 @@ constexpr inline size_t fragment_variant_count() noexcept {
return count;
}
} // anonymous namespace
static auto const gLitVariants{ details::get_variants<true>() };
static auto const gUnlitVariants{ details::get_variants<false>() };
static auto const gDepthVariants{ details::get_depth_variants() };
static_assert(reserved_is_not_valid());
static_assert(reserved_variant_count() == 160);
static_assert(valid_variant_count() == 96);
@@ -218,10 +182,6 @@ utils::Slice<Variant> getUnlitVariants() noexcept {
return { details::gUnlitVariants.data(), details::gUnlitVariants.size() };
}
utils::Slice<Variant> getDepthVariants() noexcept {
return { details::gDepthVariants.data(), details::gDepthVariants.size() };
}
}; // VariantUtils
} // namespace filament

View File

@@ -264,7 +264,7 @@ size_t Animator::getAnimationCount() const {
void Animator::applyAnimation(size_t animationIndex, float time) const {
const Animation& anim = mImpl->animations[animationIndex];
time = time == anim.duration ? time : fmod(time, anim.duration);
time = fmod(time, anim.duration);
TransformManager& transformManager = *mImpl->transformManager;
transformManager.openLocalTransformTransaction();
for (const auto& channel : anim.channels) {

View File

@@ -1043,8 +1043,6 @@ int main(int argc, char** argv) {
delete app.resourceLoader;
delete app.stbDecoder;
delete app.ktxDecoder;
delete app.automationSpec;
delete app.automationEngine;
AssetLoader::destroy(&app.assetLoader);
};

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.56.3",
"version": "1.56.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",