gltfio: Better behavior for releaseSourceData()
This commit is contained in:
@@ -82,7 +82,10 @@ struct BufferSlot {
|
||||
MorphTargetBuffer* morphTargetBuffer;
|
||||
};
|
||||
|
||||
// Encapsulates a connection between Texture and MaterialInstance.
|
||||
// Stores a connection between Texture and MaterialInstance; consumed by resource loader so that it
|
||||
// can call "setParameter" on the given MaterialInstance after the Texture has been created.
|
||||
// Since material instances are not typically shared between FilamentInstance, the slots are a
|
||||
// unified list across all instances that exist before creation of Texture objects.
|
||||
struct TextureSlot {
|
||||
size_t sourceTexture; // index into cgltf_texture
|
||||
MaterialInstance* materialInstance;
|
||||
@@ -257,9 +260,16 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
mTextureBindings[tb.sourceTexture] = texture;
|
||||
}
|
||||
|
||||
// If a Filament Texture already exists for the given slot, go ahead and bind it to the
|
||||
// given material instance. (This can occur when creating new FilamentInstances)
|
||||
// Otherwise, stash the binding information so that ResourceLoader can trigger the bind.
|
||||
void addTextureSlot(const TextureSlot& tb) {
|
||||
if (Texture* texture = mTextureBindings[tb.sourceTexture]; texture) {
|
||||
if (mResourcesLoaded) {
|
||||
Texture* texture = mTextureBindings[tb.sourceTexture];
|
||||
assert_invariant(texture);
|
||||
tb.materialInstance->setParameter(tb.materialParameter, texture, tb.sampler);
|
||||
// Intentionally omit adding a dependency graph edge here. We do not need progressive
|
||||
// reveal for multiple FilamentInstance.
|
||||
} else {
|
||||
mTextureSlots.push_back(tb);
|
||||
mDependencyGraph.addEdge(tb.materialInstance, tb.materialParameter);
|
||||
@@ -323,13 +333,19 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
using SourceHandle = std::shared_ptr<SourceAsset>;
|
||||
SourceHandle mSourceAsset;
|
||||
|
||||
// Transient source data that can freed via releaseSourceData:
|
||||
utils::FixedCapacityVector<Texture*> mTextureBindings; // one element for each cgltf_texture
|
||||
// Mapping from cgltf_texture to Texture* is required when creating new instances.
|
||||
utils::FixedCapacityVector<Texture*> mTextureBindings;
|
||||
|
||||
// Resource URIs can be queried by the end user.
|
||||
utils::FixedCapacityVector<const char*> mResourceUris;
|
||||
|
||||
// The mapping from cgltf_mesh to VertexBuffer* (etc) is required when creating new instances.
|
||||
MeshCache mMeshCache;
|
||||
|
||||
// Asset information that is produced by AssetLoader and consumed by ResourceLoader:
|
||||
std::vector<BufferSlot> mBufferSlots;
|
||||
std::vector<TextureSlot> mTextureSlots;
|
||||
utils::FixedCapacityVector<const char*> mResourceUris;
|
||||
std::vector<std::pair<const cgltf_primitive*, VertexBuffer*> > mPrimitives;
|
||||
MeshCache mMeshCache; // one element for each cgltf_mesh
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(FilamentAsset)
|
||||
|
||||
@@ -80,6 +80,7 @@ struct FFilamentInstance : public FilamentInstance {
|
||||
// may be sparsely populated. This is used as a simple mapping between cgltf_node and Entity,
|
||||
// and therefore has the same size as the number of cgltf_node in the original asset. We
|
||||
// considered using the ECS for this, but we need Node => Entity, not the other way around.
|
||||
// This is discarded after the animator is created.
|
||||
utils::FixedCapacityVector<utils::Entity> nodeMap;
|
||||
|
||||
Aabb boundingBox;
|
||||
|
||||
@@ -160,18 +160,12 @@ Entity FFilamentAsset::getWireframe() noexcept {
|
||||
|
||||
void FFilamentAsset::releaseSourceData() noexcept {
|
||||
// To ensure that all possible memory is freed, we reassign to new containers rather than
|
||||
// calling clear(). With many container types (such as robin_map), clearing is a fast
|
||||
// operation that merely frees the storage for the items.
|
||||
// calling clear(). With many container types, clearing is a fast operation that merely frees
|
||||
// the storage for the items but not the actual container.
|
||||
mTextureBindings = {};
|
||||
mMeshCache = {};
|
||||
mResourceUris = {};
|
||||
mPrimitives = {};
|
||||
mBufferSlots = {};
|
||||
mTextureSlots = {};
|
||||
mSourceAsset.reset();
|
||||
for (FFilamentInstance* instance : mInstances) {
|
||||
instance->nodeMap = {};
|
||||
}
|
||||
}
|
||||
|
||||
const char* FFilamentAsset::getName(utils::Entity entity) const noexcept {
|
||||
|
||||
@@ -41,6 +41,7 @@ Animator* FFilamentInstance::getAnimator() const noexcept {
|
||||
void FFilamentInstance::createAnimator() {
|
||||
assert_invariant(animator == nullptr);
|
||||
animator = new Animator(owner, this);
|
||||
nodeMap = {};
|
||||
}
|
||||
|
||||
size_t FFilamentInstance::getSkinCount() const noexcept {
|
||||
|
||||
@@ -167,8 +167,7 @@ static void decodeDracoMeshes(FFilamentAsset* asset) {
|
||||
};
|
||||
|
||||
// Go through every primitive and check if it has a Draco mesh.
|
||||
for (auto& pair : asset->mPrimitives) {
|
||||
const cgltf_primitive* prim = pair.first;
|
||||
for (auto& [prim, vertexBuffer] : asset->mPrimitives) {
|
||||
if (!prim->has_draco_mesh_compression) {
|
||||
continue;
|
||||
}
|
||||
@@ -177,7 +176,6 @@ static void decodeDracoMeshes(FFilamentAsset* asset) {
|
||||
|
||||
// If an error occurs, we can simply set the primitive's associated VertexBuffer to null.
|
||||
// This does not cause a leak because it is a weak reference.
|
||||
auto& vertexBuffer = pair.second;
|
||||
|
||||
// Check if we have already decoded this mesh.
|
||||
DracoMesh* mesh = dracoCache->findOrCreateMesh(draco.buffer_view);
|
||||
@@ -494,6 +492,9 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
// we need to generate the contents of a GPU buffer by processing one or more CPU buffer(s).
|
||||
pImpl->computeTangents(asset);
|
||||
|
||||
asset->mBufferSlots = {};
|
||||
asset->mPrimitives = {};
|
||||
|
||||
// If any decoding jobs are still underway from a previous load, wait for them to finish.
|
||||
for (const auto& iter : pImpl->mTextureProviders) {
|
||||
iter.second->waitForCompletion();
|
||||
@@ -674,11 +675,12 @@ void ResourceLoader::Impl::cancelTextureDecoding() {
|
||||
void ResourceLoader::Impl::createTextures(FFilamentAsset* asset, bool async) {
|
||||
// Create new texture objects if they are not cached and kick off decoding jobs.
|
||||
mRemainingTextureDownloads = 0;
|
||||
for (auto slot : asset->mTextureSlots) {
|
||||
for (const TextureSlot& slot : asset->mTextureSlots) {
|
||||
if (Texture* texture = getOrCreateTexture(asset, slot)) {
|
||||
asset->bindTexture(slot, texture);
|
||||
}
|
||||
}
|
||||
asset->mTextureSlots = {};
|
||||
|
||||
// Non-threaded systems are required to use the asynchronous API.
|
||||
assert_invariant(UTILS_HAS_THREADING || async);
|
||||
@@ -711,14 +713,13 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) {
|
||||
// Create a job description for each triangle-based primitive.
|
||||
using Params = TangentsJob::Params;
|
||||
std::vector<Params> jobParams;
|
||||
for (auto pair : asset->mPrimitives) {
|
||||
if (UTILS_UNLIKELY(pair.first->type != cgltf_primitive_type_triangles)) {
|
||||
for (auto [prim, vb] : asset->mPrimitives) {
|
||||
if (UTILS_UNLIKELY(prim->type != cgltf_primitive_type_triangles)) {
|
||||
continue;
|
||||
}
|
||||
VertexBuffer* vb = pair.second;
|
||||
auto iter = baseTangents.find(vb);
|
||||
if (iter != baseTangents.end()) {
|
||||
jobParams.emplace_back(Params {{ pair.first }, {vb, nullptr, iter->second }});
|
||||
jobParams.emplace_back(Params {{ prim }, {vb, nullptr, iter->second }});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user