From 61be531444396e1953c0710dd7cb12b26a9906ca Mon Sep 17 00:00:00 2001 From: Leonardo Zide Date: Mon, 19 Oct 2026 10:02:26 -0700 Subject: [PATCH] Manage piece data ownership with scoped asset references Replace manual piece-data retain/release bookkeeping with shared copyable asset-reference handles. Pieces, models, selection, minifigs, thumbnails, and stored insertion previews retain their dependencies explicitly, while transient queries continue to borrow pointers. Separate registered lookup and acquisition from asynchronous load requests and explicit readiness waits. Keep loader-held references through work and notification delivery, resetting shared request handles on the UI thread and guarding callback-triggered library teardown. Validated with the Qt 6.10 debug build and 26 offscreen lifecycle checks, including cancellation, stale requests, copy/move operations, model/preview teardown, undo, project merge/export, and loader shutdown. --- common/lc_assetloader.cpp | 51 ++++++------ common/lc_assetloader.h | 7 +- common/lc_assettypes.cpp | 139 ++++++++++++++++++++++++++++++++ common/lc_assettypes.h | 96 +++++++++++++++++++++- common/lc_findreplacewidget.cpp | 2 +- common/lc_library.cpp | 122 ++++++++++++++-------------- common/lc_library.h | 36 +++++---- common/lc_lxf.cpp | 10 ++- common/lc_mainwindow.cpp | 27 +++---- common/lc_mainwindow.h | 5 +- common/lc_minifigdialog.cpp | 4 +- common/lc_model.cpp | 138 +++++++++++++++---------------- common/lc_model.h | 24 ++++-- common/lc_previewwidget.cpp | 40 ++++----- common/lc_synth.cpp | 20 ++--- common/lc_texturemanager.cpp | 2 - common/lc_thumbnailmanager.cpp | 73 +++++++++-------- common/lc_thumbnailmanager.h | 5 +- common/lc_timelinewidget.cpp | 2 +- common/lc_traintrack.cpp | 10 +-- common/lc_view.cpp | 20 ++--- common/lc_view.h | 3 +- common/minifig.cpp | 81 ++++++++----------- common/minifig.h | 3 +- common/piece.cpp | 53 ++++++------ common/piece.h | 6 +- common/project.cpp | 44 +++++----- common/project.h | 3 +- docs/asset-loading.md | 79 +++++++++++++----- leocad.pro | 1 + 30 files changed, 686 insertions(+), 420 deletions(-) create mode 100644 common/lc_assettypes.cpp diff --git a/common/lc_assetloader.cpp b/common/lc_assetloader.cpp index 6ae0f668..1423932c 100644 --- a/common/lc_assetloader.cpp +++ b/common/lc_assetloader.cpp @@ -41,11 +41,9 @@ void lcAssetLoader::RequestPieceLoad(lcPieceData* PieceData, lcLoadPriority Load return; } - mLibrary->AddPieceDataReference(PieceData); // The request owns a separate in-flight hold. - std::shared_ptr RequestedPiece = std::make_shared(); - RequestedPiece->PieceData = PieceData; + RequestedPiece->PieceData = lcPieceDataRef(PieceData); RequestedPiece->Source = mLibrary->SnapshotPieceDataSource(PieceData); RequestedPiece->LoadPriority = LoadPriority; RequestedPiece->QueuedAt = QDateTime::currentMSecsSinceEpoch(); @@ -54,7 +52,7 @@ void lcAssetLoader::RequestPieceLoad(lcPieceData* PieceData, lcLoadPriority Load DispatchJobs(); - emit mLibrary->AssetStatusChanged(); + mLibrary->NotifyAssetStatusChanged(); } void lcAssetLoader::PromotePiecePriority(const std::shared_ptr& RequestedPiece, lcLoadPriority LoadPriority) @@ -102,7 +100,7 @@ void lcAssetLoader::RequestSynthMesh(lcPiece* Piece, lcPieceData* PieceData, std DispatchJobs(); - emit mLibrary->AssetStatusChanged(); + mLibrary->NotifyAssetStatusChanged(); } void lcAssetLoader::RequestTextureLoad(lcTexture* Texture, lcFileSource Source, lcLoadPriority LoadPriority) @@ -330,7 +328,7 @@ void lcAssetLoader::ProcessPieceCompletion(Completion& Completed) Q_ASSERT(QThread::currentThread() == thread()); const std::shared_ptr& RequestedPiece = Completed.RequestedPiece; - lcPieceData* PieceData = RequestedPiece->PieceData; + lcPieceData* PieceData = RequestedPiece->PieceData.Get(); const auto It = mPieceRequests.find(PieceData); const bool Current = It != mPieceRequests.end() && It->second == RequestedPiece; const bool Stale = mStopping || !Current; @@ -348,7 +346,7 @@ void lcAssetLoader::ProcessPieceCompletion(Completion& Completed) RequestedPiece->State = RequestState::Cancelled; - mLibrary->ReleasePieceDataLoadHold(PieceData); + RequestedPiece->PieceData.Reset(); return; } @@ -609,7 +607,7 @@ void lcAssetLoader::CompletePieceRequest(const std::shared_ptr& Re { Q_ASSERT(QThread::currentThread() == thread()); - lcPieceData* PieceData = RequestedPiece->PieceData; + lcPieceData* PieceData = RequestedPiece->PieceData.Get(); QString LoadError = RequestedPiece->Error; if (LoadError.isEmpty() && !RequestedPiece->StagedMesh && !RequestedPiece->EmptyGeometry) @@ -651,8 +649,7 @@ void lcAssetLoader::CompleteSynthRequest(const std::shared_ptr& Re RequestedSynth->State = Loaded ? RequestState::Succeeded : RequestState::Failed; - mLibrary->AddPieceDataReference(PieceData); - mSynthNotifications.push_back({ RequestedSynth, PieceData }); + mSynthNotifications.push_back({ RequestedSynth, lcPieceDataRef(PieceData) }); QCoreApplication::postEvent(this, new QEvent(lcAssetLoaderCompletionEvent)); } @@ -670,11 +667,11 @@ void lcAssetLoader::DispatchNotifications() mPieceNotifications.pop_front(); - lcPieceData* PieceData = Notification.RequestedPiece->PieceData; + lcPieceData* PieceData = Notification.RequestedPiece->PieceData.Get(); if (Notification.RequestedPiece->State == RequestState::Cancelled || !mLibrary->HasPieceDataConsumers(PieceData)) { - mLibrary->ReleasePieceDataLoadHold(PieceData); + Notification.RequestedPiece->PieceData.Reset(); continue; } @@ -688,9 +685,13 @@ void lcAssetLoader::DispatchNotifications() emit mLibrary->PartLoadFailed(PieceData, Notification.Error); if (!LoaderGuard || !LibraryGuard) + { + // Library teardown can delete the definition while delivering this callback. + Notification.RequestedPiece->PieceData.Release(); return; + } - mLibrary->ReleasePieceDataLoadHold(PieceData); + Notification.RequestedPiece->PieceData.Reset(); } std::unordered_set SettledSynths; @@ -701,7 +702,7 @@ void lcAssetLoader::DispatchNotifications() mSynthNotifications.pop_front(); - lcPieceData* PieceData = Notification.PieceData; + lcPieceData* PieceData = Notification.PieceData.Get(); if (!Notification.RequestedSynth->Cancelled.load(std::memory_order_relaxed) && mLibrary->HasPieceDataConsumers(PieceData) && SettledSynths.insert(PieceData).second) { @@ -711,10 +712,14 @@ void lcAssetLoader::DispatchNotifications() emit mLibrary->GeneratedMeshSettled(PieceData); if (!LoaderGuard || !LibraryGuard) + { + // Library teardown can delete the definition while delivering this callback. + Notification.PieceData.Release(); return; + } } - mLibrary->ReleasePieceDataLoadHold(PieceData); + Notification.PieceData.Reset(); } if (!mPieceNotifications.empty() || !mSynthNotifications.empty()) @@ -946,7 +951,7 @@ void lcAssetLoader::InvalidatePieceLoad(lcPieceData* PieceData) if (It != mPieceRequests.end()) DiscardPieceRequest(It->second); - emit mLibrary->AssetStatusChanged(); + mLibrary->NotifyAssetStatusChanged(); } void lcAssetLoader::CancelPieceLoad(lcPieceData* PieceData) @@ -998,13 +1003,13 @@ void lcAssetLoader::DiscardPieceRequest(std::shared_ptr Request) const bool HasOutstandingWork = Request->State == RequestState::AwaitingCompletion; Request->State = RequestState::Cancelled; - mPieceRequests.erase(Request->PieceData); + mPieceRequests.erase(Request->PieceData.Get()); // Running builds retain their hold until their stale completion arrives. if (!HasOutstandingWork) { Request->StagedMesh.reset(); - mLibrary->ReleasePieceDataLoadHold(Request->PieceData); + Request->PieceData.Reset(); } CancelUnusedTextureRequests(); @@ -1093,7 +1098,7 @@ void lcAssetLoader::ResumeQueuedWork() for (const PieceRequestMap::value_type& Entry : mPieceRequests) if (Entry.second->State == RequestState::Queued) - Entry.second->Source = mLibrary->SnapshotPieceDataSource(Entry.second->PieceData); + Entry.second->Source = mLibrary->SnapshotPieceDataSource(Entry.second->PieceData.Get()); mPaused = false; DispatchJobs(); @@ -1119,7 +1124,7 @@ void lcAssetLoader::Shutdown() else { It->second->State = RequestState::Cancelled; - mLibrary->PieceDataLoadCancelled(It->second->PieceData); + mLibrary->PieceDataLoadCancelled(It->second->PieceData.Get()); Cancelled.push_back(It->second); @@ -1155,7 +1160,7 @@ void lcAssetLoader::Shutdown() } for (const std::shared_ptr& RequestedPiece : Cancelled) - mLibrary->ReleasePieceDataLoadHold(RequestedPiece->PieceData); + RequestedPiece->PieceData.Reset(); for (const std::shared_ptr& RequestedTexture : CancelledTextures) mLibrary->ReleaseTexture(RequestedTexture->Texture); @@ -1168,7 +1173,7 @@ void lcAssetLoader::Shutdown() mPieceNotifications.pop_front(); - mLibrary->ReleasePieceDataLoadHold(Notification.RequestedPiece->PieceData); + Notification.RequestedPiece->PieceData.Reset(); } while (!mSynthNotifications.empty()) @@ -1177,7 +1182,7 @@ void lcAssetLoader::Shutdown() mSynthNotifications.pop_front(); - mLibrary->ReleasePieceDataLoadHold(Notification.PieceData); + Notification.PieceData.Reset(); } mLibrary->mCancelLoading.store(WasCancelled); diff --git a/common/lc_assetloader.h b/common/lc_assetloader.h index 88787f5a..a11fb9ed 100644 --- a/common/lc_assetloader.h +++ b/common/lc_assetloader.h @@ -87,11 +87,12 @@ private: } }; - // Owns one lcPieceData load hold through completion and deferred notification. + // Owns one definition reference through completion and deferred notification. + // Reset on the UI thread before worker-shared request storage can be destroyed. // One worker job builds and converts geometry; textures settle before publication. struct PieceRequest : RequestBase { - lcPieceData* PieceData; + lcPieceDataRef PieceData; lcPieceSourceSnapshot Source; std::unique_ptr StagedMesh; bool EmptyGeometry = false; @@ -147,7 +148,7 @@ private: struct SynthNotification { std::shared_ptr RequestedSynth; - lcPieceData* PieceData; + lcPieceDataRef PieceData; }; // Queue and promote requests on the UI thread. diff --git a/common/lc_assettypes.cpp b/common/lc_assettypes.cpp new file mode 100644 index 00000000..d99610a1 --- /dev/null +++ b/common/lc_assettypes.cpp @@ -0,0 +1,139 @@ +#include "lc_global.h" +#include "lc_assettypes.h" +#include "lc_library.h" +#include "lc_application.h" +#include "lc_mesh.h" + +template<> +void lcAssetRef::Retain(lcPieceData* Asset, lcPiecesLibrary* Library) +{ + Library->AddPieceDataReference(Asset); +} + +template<> +void lcAssetRef::Release(lcPieceData* Asset, lcPiecesLibrary* Library) +{ + Library->ReleasePieceData(Asset); +} + +template<> +void lcAssetRef::Retain(lcTexture* Asset, lcPiecesLibrary* Library) +{ + Library->AddTextureReference(Asset); +} + +template<> +void lcAssetRef::Release(lcTexture* Asset, lcPiecesLibrary* Library) +{ + Library->ReleaseTexture(Asset); +} + +template +lcAssetRef::lcAssetRef(T* Asset) + : lcAssetRef(Asset, Asset ? lcGetPiecesLibrary() : nullptr) +{ +} + +template +lcAssetRef::lcAssetRef(T* Asset, lcPiecesLibrary* Library) + : mAsset(Asset), mLibrary(Library) +{ + if (mAsset) + { + Q_ASSERT(mLibrary && QThread::currentThread() == mLibrary->thread()); + Retain(mAsset, mLibrary); + } +} + +template +lcAssetRef::lcAssetRef(const lcAssetRef& OtherRef) + : lcAssetRef(OtherRef.mAsset, OtherRef.mLibrary) +{ +} + +template +lcAssetRef::lcAssetRef(lcAssetRef&& OtherRef) noexcept + : mAsset(std::exchange(OtherRef.mAsset, nullptr)), mLibrary(std::exchange(OtherRef.mLibrary, nullptr)) +{ +} + +template +lcAssetRef::~lcAssetRef() +{ + if (mAsset) + { + Q_ASSERT(mLibrary && QThread::currentThread() == mLibrary->thread()); + Release(mAsset, mLibrary); + } +} + +template +lcAssetRef& lcAssetRef::operator=(const lcAssetRef& OtherRef) +{ + if (this != &OtherRef) + { + lcAssetRef CopyRef(OtherRef); + Swap(CopyRef); + } + + return *this; +} + +template +lcAssetRef& lcAssetRef::operator=(lcAssetRef&& OtherRef) noexcept +{ + if (this != &OtherRef) + { + lcAssetRef MovedRef(std::move(OtherRef)); + Swap(MovedRef); + } + + return *this; +} + +template +void lcAssetRef::Reset() +{ + lcAssetRef EmptyRef; + Swap(EmptyRef); +} + +template +T* lcAssetRef::Release() +{ + mLibrary = nullptr; + return std::exchange(mAsset, nullptr); +} + +template +void lcAssetRef::Swap(lcAssetRef& OtherRef) noexcept +{ + std::swap(mAsset, OtherRef.mAsset); + std::swap(mLibrary, OtherRef.mLibrary); +} + +template +lcAssetRef lcAssetRef::Adopt(T* Asset, lcPiecesLibrary* Library) +{ + Q_ASSERT(!Asset || (Library && QThread::currentThread() == Library->thread())); + + lcAssetRef AssetRef; + AssetRef.mAsset = Asset; + AssetRef.mLibrary = Library; + return AssetRef; +} + +template class lcAssetRef; +template class lcAssetRef; + +lcPieceSourceSnapshot::lcPieceSourceSnapshot() + : ZipFileType(lcZipFileType::Count), StudStyle(lcStudStyle::Plain) +{ +} + +lcPieceBuildResult::lcPieceBuildResult() = default; +lcPieceBuildResult::~lcPieceBuildResult() = default; +lcPieceBuildResult::lcPieceBuildResult(lcPieceBuildResult&&) noexcept = default; +lcPieceBuildResult& lcPieceBuildResult::operator=(lcPieceBuildResult&&) noexcept = default; + +lcTextureBuildResult::~lcTextureBuildResult() = default; diff --git a/common/lc_assettypes.h b/common/lc_assettypes.h index 35ae9dea..ba7f754a 100644 --- a/common/lc_assettypes.h +++ b/common/lc_assettypes.h @@ -1,10 +1,104 @@ #pragma once +class lcPieceData; +class lcTexture; +class lcPiecesLibrary; class lcMesh; struct lcEmbeddedData; enum class lcStudStyle; enum class lcZipFileType; +// Scoped asset reference. Construction from a borrowed pointer and copying retain; +// moving transfers ownership. Use and destroy on the UI thread before library unload. +// Ownership operations are instantiated for piece definitions and textures in the cpp. +template +class lcAssetRef +{ +public: + lcAssetRef() = default; + explicit lcAssetRef(T* Asset); + lcAssetRef(const lcAssetRef& OtherRef); + lcAssetRef(lcAssetRef&& OtherRef) noexcept; + ~lcAssetRef(); + + lcAssetRef& operator=(const lcAssetRef& OtherRef); + lcAssetRef& operator=(lcAssetRef&& OtherRef) noexcept; + + T* Get() const + { + return mAsset; + } + + T* operator->() const + { + return mAsset; + } + + T& operator*() const + { + return *mAsset; + } + + explicit operator bool() const + { + return mAsset != nullptr; + } + + friend bool operator==(const lcAssetRef& LeftRef, const lcAssetRef& RightRef) noexcept + { + return LeftRef.mAsset == RightRef.mAsset; + } + + friend bool operator!=(const lcAssetRef& LeftRef, const lcAssetRef& RightRef) noexcept + { + return !(LeftRef == RightRef); + } + + friend bool operator<(const lcAssetRef& LeftRef, const lcAssetRef& RightRef) noexcept + { + return std::less()(LeftRef.mAsset, RightRef.mAsset); + } + + friend bool operator==(const lcAssetRef& LeftRef, const T* Right) noexcept + { + return LeftRef.mAsset == Right; + } + + friend bool operator!=(const lcAssetRef& LeftRef, const T* Right) noexcept + { + return !(LeftRef == Right); + } + + friend bool operator==(const T* Left, const lcAssetRef& RightRef) noexcept + { + return Left == RightRef.mAsset; + } + + friend bool operator!=(const T* Left, const lcAssetRef& RightRef) noexcept + { + return !(Left == RightRef); + } + + void Reset(); + T* Release(); + void Swap(lcAssetRef& OtherRef) noexcept; + +private: + friend class lcPiecesLibrary; + + lcAssetRef(T* Asset, lcPiecesLibrary* Library); + // The library uses this only for a reference it has already acquired. + static lcAssetRef Adopt(T* Asset, lcPiecesLibrary* Library); + static void Retain(T* Asset, lcPiecesLibrary* Library); + static void Release(T* Asset, lcPiecesLibrary* Library); + + T* mAsset = nullptr; + lcPiecesLibrary* mLibrary = nullptr; +}; + +using lcPieceDataRef = lcAssetRef; +using lcTextureRef = lcAssetRef; + // Asset readiness; detailed scheduling progress belongs to loader requests. enum class lcAssetState { @@ -14,7 +108,7 @@ enum class lcAssetState Failed }; -// Scheduling urgency. The library's RequestPieceDataLoad() also waits when Blocking is requested. +// Scheduling urgency only. Use the library's wait APIs for synchronous loading. // Queued requests only promote and keep their priority until completion or cancellation. enum class lcLoadPriority { diff --git a/common/lc_findreplacewidget.cpp b/common/lc_findreplacewidget.cpp index e95c1a65..f39ea34d 100644 --- a/common/lc_findreplacewidget.cpp +++ b/common/lc_findreplacewidget.cpp @@ -111,7 +111,7 @@ lcFindReplaceWidget::lcFindReplaceWidget(QWidget* Parent, lcModel* Model, bool R { lcFindReplaceParams& Params = lcView::GetFindReplaceParams(); - Params.FindPieceData = Focus->mPieceData; + Params.FindPieceData = Focus->mPieceData.Get(); Params.FindColorIndex = Focus->GetColorIndex(); FindColorPicker->SetCurrentColor(Params.FindColorIndex); diff --git a/common/lc_library.cpp b/common/lc_library.cpp index 2a4f1079..00ad0897 100644 --- a/common/lc_library.cpp +++ b/common/lc_library.cpp @@ -37,16 +37,6 @@ enum class lcLibraryCacheFlag : quint32 Directory = 0x0002 }; -lcPieceSourceSnapshot::lcPieceSourceSnapshot() - : ZipFileType(lcZipFileType::Count), StudStyle(lcStudStyle::Plain) -{ -} - -lcPieceBuildResult::lcPieceBuildResult() = default; -lcPieceBuildResult::~lcPieceBuildResult() = default; -lcPieceBuildResult::lcPieceBuildResult(lcPieceBuildResult&&) noexcept = default; -lcPieceBuildResult& lcPieceBuildResult::operator=(lcPieceBuildResult&&) noexcept = default; - lcPiecesLibrary::lcPiecesLibrary(lcStudStyle StudStyle, bool StudCylinderColorEnabled) : mStudStyle(StudStyle), mStudCylinderColorEnabled(StudCylinderColorEnabled) { @@ -348,7 +338,7 @@ std::string lcPiecesLibrary::NormalizePieceDataName(const char* PieceName) return Name; } -lcPieceData* lcPiecesLibrary::AcquireModelPieceData(const char* PieceName, Project* Project, bool& Reused) +lcPieceDataRef lcPiecesLibrary::AcquireModelPieceData(const char* PieceName, Project* Project, bool& Reused) { const std::string Name = NormalizePieceDataName(PieceName); lcPieceData* PieceData; @@ -373,10 +363,12 @@ lcPieceData* lcPiecesLibrary::AcquireModelPieceData(const char* PieceName, Proje PieceData->AddRef(); } + lcPieceDataRef PieceDataRef = lcPieceDataRef::Adopt(PieceData, this); + if (Reused) InvalidatePieceDataLoad(PieceData); - return PieceData; + return PieceDataRef; } lcResult lcPiecesLibrary::LoadExternalProject(const QFileInfo& FileInfo, bool Preview, bool DeferModelMeshRequests) @@ -404,31 +396,37 @@ lcResult lcPiecesLibrary::LoadExternalProject(const QFileInfo& FileInf return ExternalProject; } -lcPieceData* lcPiecesLibrary::FindPieceData(const char* PieceName, Project* CurrentProject, bool CreateMissing, bool SearchProjectFolder) +lcPieceData* lcPiecesLibrary::FindPieceData(const char* PieceName, Project* CurrentProject) { const std::string Name = NormalizePieceDataName(PieceName); - QString ProjectPath; - QString LoadError; + QMutexLocker LoadLock(&mLoadMutex); if (CurrentProject) - { - QMutexLocker LoadLock(&mLoadMutex); - if (lcPieceData* PieceData = CurrentProject->FindPieceData(Name)) return PieceData; - if (SearchProjectFolder && !CurrentProject->GetFileName().isEmpty()) - ProjectPath = QFileInfo(CurrentProject->GetFileName()).absolutePath(); - } + const std::map::const_iterator PieceIt = mPieces.find(Name); + + return PieceIt != mPieces.end() ? PieceIt->second : nullptr; +} + +lcPieceDataRef lcPiecesLibrary::AcquirePieceData(const char* PieceName, Project* CurrentProject, bool CreateMissing, bool SearchProjectFolder) +{ + QString ProjectPath; + QString LoadError; { QMutexLocker LoadLock(&mLoadMutex); - const std::map::const_iterator PieceIt = mPieces.find(Name); - if (PieceIt != mPieces.end()) - return PieceIt->second; + if (lcPieceData* PieceData = FindPieceData(PieceName, CurrentProject)) + return lcPieceDataRef(PieceData, this); + + if (CurrentProject && SearchProjectFolder && !CurrentProject->GetFileName().isEmpty()) + ProjectPath = QFileInfo(CurrentProject->GetFileName()).absolutePath(); } + const std::string Name = NormalizePieceDataName(PieceName); + if (!ProjectPath.isEmpty()) { QFileInfo ProjectFile = QFileInfo(ProjectPath + QDir::separator() + PieceName); @@ -444,7 +442,7 @@ lcPieceData* lcPiecesLibrary::FindPieceData(const char* PieceName, Project* Curr PieceData->CreateProject(ExternalProject.value(), PieceName); RegisterProjectPieceData(CurrentProject, Name, PieceData); - return PieceData; + return lcPieceDataRef(PieceData, this); } else LoadError = ExternalProject.error(); @@ -466,10 +464,10 @@ lcPieceData* lcPiecesLibrary::FindPieceData(const char* PieceName, Project* Curr mPieces[Name] = PieceData; } - return PieceData; + return lcPieceDataRef(PieceData, this); } - return nullptr; + return lcPieceDataRef(); } bool lcPiecesLibrary::RemapProjectPieceData(lcPieceData* PieceData, const QString& ProjectDirectory, bool IsPreview) @@ -975,7 +973,7 @@ bool lcPiecesLibrary::OpenArchive(std::unique_ptr File, lcZipFileType Zi if (memcmp(Name, "S/", 2)) { - lcPieceData* PieceData = FindPieceData(Name, nullptr, false, false); + lcPieceData* PieceData = FindPieceData(Name, nullptr); if (!PieceData) { @@ -1656,18 +1654,7 @@ bool lcPiecesLibrary::SaveArchiveCacheIndex(const QString& FileName) return WriteArchiveCacheFile(FileName, IndexFile); } -bool lcPiecesLibrary::RequestPieceDataLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority) -{ - Q_ASSERT(QThread::currentThread() == thread()); - - AddPieceDataReference(PieceData); - - StartPieceLoad(PieceData, LoadPriority); - - return LoadPriority != lcLoadPriority::Blocking || WaitForPieceData({ PieceData }); -} - -void lcPiecesLibrary::StartPieceLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority) +void lcPiecesLibrary::RequestPieceDataLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority) { Q_ASSERT(QThread::currentThread() == thread()); @@ -1711,9 +1698,9 @@ void lcPiecesLibrary::NotifyAssetStatusChanged() emit AssetStatusChanged(); } -bool lcPiecesLibrary::EnsurePieceDataAssetsReady(lcPieceData* PieceData) +bool lcPiecesLibrary::EnsurePieceDataAssetsReady(const lcPieceDataRef& PieceData) { - const bool PieceReady = WaitForPieceData({ PieceData }); + const bool PieceReady = WaitForPieceData(PieceData); if (PieceData->IsModel()) return static_cast(PieceData->GetModel()->EnsureAssetsReady()) && PieceReady; @@ -1722,25 +1709,44 @@ bool lcPiecesLibrary::EnsurePieceDataAssetsReady(lcPieceData* PieceData) { const bool ProjectReady = static_cast(PieceData->GetProject()->EnsureAssetsReady()); - PieceData->GetProject()->UpdatePieceData(PieceData); + PieceData->GetProject()->UpdatePieceData(PieceData.Get()); return ProjectReady && PieceReady; } return PieceReady; } -bool lcPiecesLibrary::WaitForPieceData(const std::vector& Pieces) +bool lcPiecesLibrary::WaitForPieceData(const lcPieceDataRef& PieceDataRef) { Q_ASSERT(QThread::currentThread() == thread()); - for (lcPieceData* PieceData : Pieces) - if (PieceData->mState == lcAssetState::Unloaded) - StartPieceLoad(PieceData, lcLoadPriority::Blocking); + if (PieceDataRef->mState == lcAssetState::Unloaded) + RequestPieceDataLoad(PieceDataRef.Get(), lcLoadPriority::Blocking); + + const bool RequestsSucceeded = mAssetLoader->WaitForPieces({ PieceDataRef.Get() }); + + return RequestsSucceeded && PieceDataRef->mState == lcAssetState::Loaded; +} + +bool lcPiecesLibrary::WaitForPieceData(const std::vector& PieceDataRefs) +{ + Q_ASSERT(QThread::currentThread() == thread()); + + std::vector Pieces; + Pieces.reserve(PieceDataRefs.size()); + + for (const lcPieceDataRef& PieceDataRef : PieceDataRefs) + { + if (PieceDataRef->mState == lcAssetState::Unloaded) + RequestPieceDataLoad(PieceDataRef.Get(), lcLoadPriority::Blocking); + + Pieces.push_back(PieceDataRef.Get()); + } const bool RequestsSucceeded = mAssetLoader->WaitForPieces(Pieces); - for (const lcPieceData* PieceData : Pieces) - if (PieceData->mState != lcAssetState::Loaded) + for (const lcPieceDataRef& PieceDataRef : PieceDataRefs) + if (PieceDataRef->mState != lcAssetState::Loaded) return false; return RequestsSucceeded; @@ -1750,7 +1756,7 @@ void lcPiecesLibrary::RequestSynthMesh(lcPiece* Piece) { Q_ASSERT(QThread::currentThread() == thread()); - lcPieceData* PieceData = Piece->mPieceData; + lcPieceData* PieceData = Piece->mPieceData.Get(); std::shared_ptr SynthInfo = PieceData ? PieceData->GetSynthInfoShared() : nullptr; if (!SynthInfo) @@ -1854,7 +1860,7 @@ void lcPiecesLibrary::RebuildModelPieceData(lcPieceData* PieceData) InvalidatePieceDataLoad(PieceData); PieceData->ReleaseMesh(); - StartPieceLoad(PieceData, lcLoadPriority::Normal); + RequestPieceDataLoad(PieceData, lcLoadPriority::Normal); } void lcPiecesLibrary::InvalidatePieceDataLoad(lcPieceData* PieceData) @@ -1902,14 +1908,6 @@ bool lcPiecesLibrary::HasPieceDataConsumers(const lcPieceData* PieceData) return PieceData->GetRefCount() > 1; } -void lcPiecesLibrary::ReleasePieceDataLoadHold(lcPieceData* PieceData) -{ - QMutexLocker LoadLock(&mLoadMutex); - - if (PieceData->Release() == 0) - UnloadPieceData(PieceData); -} - void lcPiecesLibrary::SetPieceDataLoadError(const lcPieceData* PieceData, QString Error) { QMutexLocker ErrorLock(&mPieceErrorMutex); @@ -2452,12 +2450,12 @@ void lcPiecesLibrary::UpdateStudStyleAndColors(lcStudStyle StudStyle, bool StudC PieceData->ReleaseMesh(); PieceData->mState = lcAssetState::Unloaded; - StartPieceLoad(PieceData, lcLoadPriority::Low); + RequestPieceDataLoad(PieceData, lcLoadPriority::Low); } else if (!PieceData->IsProject()) { PieceData->Unload(); - StartPieceLoad(PieceData, lcLoadPriority::Low); + RequestPieceDataLoad(PieceData, lcLoadPriority::Low); } mBuffersDirty = true; @@ -2738,7 +2736,7 @@ void lcPiecesLibrary::GetCategoryEntries(const char* CategoryKeywords, bool Grou *strchr(ParentName, 'P') = '\0'; lcstrcat(ParentName, ".dat"); - Parent = FindPieceData(ParentName, nullptr, false, false); + Parent = FindPieceData(ParentName, nullptr); if (Parent) { diff --git a/common/lc_library.h b/common/lc_library.h index ab690ecc..12342de1 100644 --- a/common/lc_library.h +++ b/common/lc_library.h @@ -170,23 +170,26 @@ public: void SetModelPieceDataName(lcPieceData* PieceData, const char* Name); bool RenamePieceData(lcPieceData* PieceData, const char* NewName); static std::string NormalizePieceDataName(const char* PieceName); - // Returns one acquired model reference, paired with ReleasePieceData(). - lcPieceData* AcquireModelPieceData(const char* PieceName, Project* Project, bool& Reused); - // Returns a borrowed identity; lookup does not acquire a reference or ensure readiness. - lcPieceData* FindPieceData(const char* PieceName, Project* Project, bool CreateMissing, bool SearchProjectFolder); + // Returns one scoped model reference, ready to transfer to the model. + lcPieceDataRef AcquireModelPieceData(const char* PieceName, Project* Project, bool& Reused); + // Borrowed registered lookup: project names, then the shared catalog. No I/O or creation. + lcPieceData* FindPieceData(const char* PieceName, Project* Project); + // Resolves or creates an identity and returns one scoped reference; does not ensure readiness. + lcPieceDataRef AcquirePieceData(const char* PieceName, Project* Project, bool CreateMissing, bool SearchProjectFolder); bool RemapProjectPieceData(lcPieceData* PieceData, const QString& ProjectDirectory, bool IsPreview); // Asset scheduling, waits, invalidation, and consumer releases below run on the UI thread. - // Acquires one reference even on failure; always pair with ReleasePieceData(). - // Blocking waits and reports success; Low and Normal return immediately. See docs/asset-loading.md. - bool RequestPieceDataLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority); + // Start or promote loading asynchronously. Adds no consumer references and never waits. + // Priority controls scheduling only; the caller retains the identity through completion or cancellation. + void RequestPieceDataLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority); void NotifyAssetStatusChanged(); // Start or promote loading, then wait. Adds no consumer references. // Returns false for failed or cancelled pieces. // Does not traverse container children or wait for per-instance synth geometry. - bool WaitForPieceData(const std::vector& Pieces); + bool WaitForPieceData(const lcPieceDataRef& PieceDataRef); + bool WaitForPieceData(const std::vector& PieceDataRefs); // Also settles container children/synths and updates bounds; caller retains the identity. - bool EnsurePieceDataAssetsReady(lcPieceData* PieceData); + bool EnsurePieceDataAssetsReady(const lcPieceDataRef& PieceData); // Obsoletes pending work and resets readiness without acquiring a reference. void InvalidatePieceDataLoad(lcPieceData* PieceData); @@ -200,14 +203,8 @@ public: // Last generation error, or an empty string; an empty error does not imply readiness. QString GetSynthMeshError(const lcPiece* Piece) const; void RebuildModelPieceData(lcPieceData* PieceData); - // Releases one reference and lets the loader cancel unused queued work. - void ReleasePieceData(lcPieceData* PieceData); - // Lifetime hold only: does not start loading or establish readiness. - void AddPieceDataReference(lcPieceData* PieceData); // Loader helper: assumes a pending request/notification owns one in-flight hold. bool HasPieceDataConsumers(const lcPieceData* PieceData); - // Releases that in-flight hold without triggering consumer-release cancellation. - void ReleasePieceDataLoadHold(lcPieceData* PieceData); void SetPieceDataLoadError(const lcPieceData* PieceData, QString Error); QString GetPieceDataLoadError(const lcPieceData* PieceData) const; void ClearPieceDataLoadError(const lcPieceData* PieceData); @@ -287,9 +284,14 @@ signals: protected: friend class lcAssetLoader; + template friend class lcAssetRef; + + // Internal lifetime primitive for scoped acquisitions and loader holds. Never starts work. + void AddPieceDataReference(lcPieceData* PieceData); + + // Releases one reference and lets the loader cancel unused queued work. + void ReleasePieceData(lcPieceData* PieceData); - // Prepare asset readiness and request work without acquiring a consumer reference or waiting. - void StartPieceLoad(lcPieceData* PieceData, lcLoadPriority LoadPriority); // The loader calls this only when cancelling the current request, before releasing its hold. void PieceDataLoadCancelled(lcPieceData* PieceData); diff --git a/common/lc_lxf.cpp b/common/lc_lxf.cpp index fe2efe3b..805b6c67 100644 --- a/common/lc_lxf.cpp +++ b/common/lc_lxf.cpp @@ -151,15 +151,17 @@ bool lcImportLXFMLFile(const QString& FileData, Project* Project, std::vectorFindPieceData(BrickIt->second.c_str(), Project, true, false); + PieceData = lcGetPiecesLibrary()->AcquirePieceData(BrickIt->second.c_str(), Project, true, false); else - PieceData = lcGetPiecesLibrary()->FindPieceData(LegoID.toLatin1() + ".dat", Project, true, false); + PieceData = lcGetPiecesLibrary()->AcquirePieceData(LegoID.toLatin1() + ".dat", Project, true, false); const auto ColorIt = MaterialTable.find(Material); int ColorCode = 16; + if (ColorIt != MaterialTable.end()) ColorCode = ColorIt->second; @@ -179,7 +181,7 @@ bool lcImportLXFMLFile(const QString& FileData, Project* Project, std::vectorSetPieceData(PieceData, QString(), false, true); + Piece->SetPieceData(std::move(PieceData), QString(), false, true); Piece->Initialize(lcMatrix44LDrawToLeoCAD(WorldMatrix), 1); Piece->SetColorCode(ColorCode); Pieces.push_back(Piece); diff --git a/common/lc_mainwindow.cpp b/common/lc_mainwindow.cpp index 93682ec0..fc2f3858 100644 --- a/common/lc_mainwindow.cpp +++ b/common/lc_mainwindow.cpp @@ -64,11 +64,7 @@ lcMainWindow::~lcMainWindow() lcPiecesLibrary* Library = lcGetPiecesLibrary(); disconnect(Library, nullptr, this, nullptr); - if (mInsertPieceData) - { - Library->ReleasePieceData(mInsertPieceData); - mInsertPieceData = nullptr; - } + mInsertPieceData.Reset(); for (size_t FileIndex = 0; FileIndex < mRecentFiles.size(); FileIndex++) lcSetProfileString((LC_PROFILE_KEY)(LC_PROFILE_RECENT_FILE1 + FileIndex), mRecentFiles[FileIndex]); @@ -1023,11 +1019,12 @@ void lcMainWindow::UpdateAssets() lcView::UpdateProjectViews(Project); - const std::vector Required = Project->GetRequiredPieceData(); + const std::vector RequiredPieceData = Project->GetRequiredPieceData(); const std::vector GeneratedPieces = Project->GetRequiredSynthPieces(); mStatusRequiredAssets.clear(); - mStatusRequiredAssets.insert(Required.begin(), Required.end()); + for (const lcPieceDataRef& RequiredData : RequiredPieceData) + mStatusRequiredAssets.insert(RequiredData.Get()); int PendingParts = 0; int PendingSynths = 0; @@ -1042,8 +1039,9 @@ void lcMainWindow::UpdateAssets() FailureDetails += Warning; } - for (const lcPieceData* PieceData : Required) + for (const lcPieceDataRef& RequiredData : RequiredPieceData) { + const lcPieceData* PieceData = RequiredData.Get(); if (PieceData->mState == lcAssetState::Failed) { Failed++; @@ -1082,7 +1080,7 @@ void lcMainWindow::UpdateAssets() } } - const int Total = static_cast(Required.size() + GeneratedPieces.size()); + const int Total = static_cast(RequiredPieceData.size() + GeneratedPieces.size()); const int Pending = PendingParts + PendingSynths; QString LoadingText; @@ -2014,15 +2012,10 @@ void lcMainWindow::SetTransformType(lcTransformType TransformType) void lcMainWindow::SetInsertPieceData(lcPieceData* PieceData) { - lcPiecesLibrary* Library = lcGetPiecesLibrary(); + mInsertPieceData = lcPieceDataRef(PieceData); if (mInsertPieceData) - Library->ReleasePieceData(mInsertPieceData); - - mInsertPieceData = PieceData; - - if (mInsertPieceData) - Library->RequestPieceDataLoad(mInsertPieceData, lcLoadPriority::Normal); + lcGetPiecesLibrary()->RequestPieceDataLoad(mInsertPieceData.Get(), lcLoadPriority::Normal); } lcVector3 lcMainWindow::GetTransformAmount() @@ -2286,7 +2279,7 @@ void lcMainWindow::UpdateSelectedObjects(bool SelectionChanged) int ColorIndex = Piece->GetColorIndex(); quint32 ColorCode = lcGetColorCode(ColorIndex); - PreviewPiece(Piece->mPieceData, ColorCode, false); + PreviewPiece(Piece->mPieceData.Get(), ColorCode, false); } QString Message; diff --git a/common/lc_mainwindow.h b/common/lc_mainwindow.h index f09266c7..437c0f8a 100644 --- a/common/lc_mainwindow.h +++ b/common/lc_mainwindow.h @@ -1,6 +1,7 @@ #pragma once #include "lc_application.h" +#include "lc_assettypes.h" #include "lc_shortcuts.h" #include "lc_result.h" #include "lc_commands.h" @@ -140,7 +141,7 @@ public: return mSelectionMode; } - lcPieceData* GetInsertPieceData() const + const lcPieceDataRef& GetInsertPieceData() const { return mInsertPieceData; } @@ -347,7 +348,7 @@ protected: int mAngleSnapIndex; bool mRelativeTransform = true; bool mLocalTransform = false; - lcPieceData* mInsertPieceData = nullptr; + lcPieceDataRef mInsertPieceData; lcSelectionMode mSelectionMode; int mMousePressTabIndex = -1; diff --git a/common/lc_minifigdialog.cpp b/common/lc_minifigdialog.cpp index df26b37d..bfaed5fe 100644 --- a/common/lc_minifigdialog.cpp +++ b/common/lc_minifigdialog.cpp @@ -206,7 +206,7 @@ void lcMinifigDialog::TemplateComboBoxCurrentTextChanged(const QString& Template { if (!Template.Parts[PartIdx].isEmpty()) { - lcPieceData* PieceData = mMinifigWizard->mMinifig.Parts[PartIdx]; + lcPieceData* PieceData = mMinifigWizard->mMinifig.PieceData[PartIdx].Get(); if (PieceData) { @@ -368,7 +368,7 @@ void lcMinifigDialog::PieceButtonClicked() return; int ItemIndex = static_cast(std::distance(mPieceButtons.begin(), Search)); - lcPieceData* CurrentPieceData = mMinifigWizard->mMinifig.Parts[ItemIndex]; + lcPieceData* CurrentPieceData = mMinifigWizard->mMinifig.PieceData[ItemIndex].Get(); QPoint Position = PieceButton->mapToGlobal(PieceButton->rect().bottomLeft()); std::vector> Parts; diff --git a/common/lc_model.cpp b/common/lc_model.cpp index d6591ac8..89b1ffe9 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -184,7 +184,6 @@ lcModel::lcModel(const QString& FileName, Project* Project, bool Preview) mActive = false; mCurrentStep = 1; - mPieceData = nullptr; } void lcModel::SetProject(Project* Project) @@ -257,15 +256,7 @@ std::vector lcModel::GetAssetSearchDirectories() const lcModel::~lcModel() { - if (!mPreviewInsertPieceInfo.empty()) - { - lcPiecesLibrary* Library = lcGetPiecesLibrary(); - - for (lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo) - Library->ReleasePieceData(PreviewPieceInfoTransform.PieceData); - - mPreviewInsertPieceInfo.clear(); - } + mPreviewInsertPieceInfo.clear(); if (mPieceData) { @@ -275,14 +266,13 @@ lcModel::~lcModel() gMainWindow->SetInsertPieceData(nullptr); // Keep the name indexed while placements still reference the deleted model. - lcGetPiecesLibrary()->InvalidatePieceDataLoad(mPieceData); + lcGetPiecesLibrary()->InvalidatePieceDataLoad(mPieceData.Get()); mPieceData->DetachContainer(); mPieceData->SetFailed(tr("Model '%1' was deleted.").arg(QString::fromLatin1(mPieceData->mFileName))); lcGetPiecesLibrary()->NotifyAssetStatusChanged(); } - lcPiecesLibrary* Library = lcGetPiecesLibrary(); - Library->ReleasePieceData(mPieceData); + mPieceData.Reset(); } DeleteModel(); @@ -298,7 +288,7 @@ bool lcModel::GetPieceWorldMatrix(lcPiece* Piece, lcMatrix44& ParentWorldMatrix) return true; } - const lcPieceData* PieceData = ModelPiece->mPieceData; + const lcPieceData* PieceData = ModelPiece->mPieceData.Get(); if (PieceData->IsModel()) { @@ -328,7 +318,7 @@ bool lcModel::IncludesModel(const lcModel* Model) const for (const std::unique_ptr& Piece : Current->mPieces) { - const lcPieceData* PieceData = Piece->mPieceData; + const lcPieceData* PieceData = Piece->mPieceData.Get(); if (PieceData && PieceData->IsModel() && Visit(PieceData->GetModel())) return true; @@ -389,12 +379,14 @@ void lcModel::CreatePieceData(Project* Project) }), mPieces.end()); } - // A model with deferred mesh requests retains its initial AcquireModelPieceData reference - // until its source lines have been parsed and QueueMeshBuild() runs. + // The model owns the reference acquired by AcquireModelPieceData(), including while + // mesh requests are deferred until its source lines have been parsed. if (!Project->DefersModelMeshRequests()) { - Library->RequestPieceDataLoad(mPieceData, mIsPreview ? lcLoadPriority::Normal : lcLoadPriority::Blocking); - Library->ReleasePieceData(mPieceData); + if (mIsPreview) + Library->RequestPieceDataLoad(mPieceData.Get(), lcLoadPriority::Normal); + else + Library->WaitForPieceData(mPieceData); } if (Reused) @@ -408,10 +400,9 @@ void lcModel::CreatePieceData(Project* Project) bool lcModel::RebindPieceData(lcPieceData* Previous, lcPieceData* Replacement) { - lcPiecesLibrary* Library = lcGetPiecesLibrary(); bool Changed = false; - mPieces.erase(std::remove_if(mPieces.begin(), mPieces.end(), [this, Previous, Replacement, Library, &Changed](const std::unique_ptr& Piece) + mPieces.erase(std::remove_if(mPieces.begin(), mPieces.end(), [this, Previous, Replacement, &Changed](const std::unique_ptr& Piece) { if (Piece->mPieceData != Previous) return false; @@ -422,8 +413,7 @@ bool lcModel::RebindPieceData(lcPieceData* Previous, lcPieceData* Replacement) return true; const QString PieceId = Piece->GetID(); - Piece->SetPieceData(Replacement, PieceId, false, false); - Library->ReleasePieceData(Previous); + Piece->SetPieceData(lcPieceDataRef(Replacement), PieceId, false, false); return false; }), mPieces.end()); @@ -434,7 +424,7 @@ bool lcModel::RebindPieceData(lcPieceData* Previous, lcPieceData* Replacement) void lcModel::QueueMeshBuild() { mPieceData->SetModel(this); - lcGetPiecesLibrary()->RebuildModelPieceData(mPieceData); + lcGetPiecesLibrary()->RebuildModelPieceData(mPieceData.Get()); } void lcModel::UpdateAllViews() const @@ -451,38 +441,38 @@ void lcModel::RefreshPreview() const gMainWindow->GetPreviewWidget()->RefreshModel(this); } -std::vector lcModel::GetRequiredPieceData() const +std::vector lcModel::GetRequiredPieceData() const { - std::vector Required; + std::vector RequiredPieceData; std::unordered_set Visited; - const std::function Collect = [&Required, &Visited, &Collect](const lcModel* Model) + const std::function Collect = [&RequiredPieceData, &Visited, &Collect](const lcModel* Model) { if (!Model || !Visited.insert(Model).second) return; if (Model->mPieceData) - Required.push_back(Model->mPieceData); + RequiredPieceData.push_back(Model->mPieceData); for (const std::unique_ptr& Piece : Model->mPieces) { - lcPieceData* PieceData = Piece->mPieceData; + const lcPieceDataRef& PieceData = Piece->mPieceData; if (PieceData->IsModel()) Collect(PieceData->GetModel()); else if (PieceData->IsProject()) Collect(PieceData->GetProject()->GetMainModel()); else - Required.push_back(PieceData); + RequiredPieceData.push_back(PieceData); } }; Collect(this); - std::sort(Required.begin(), Required.end()); - Required.erase(std::unique(Required.begin(), Required.end()), Required.end()); + std::sort(RequiredPieceData.begin(), RequiredPieceData.end()); + RequiredPieceData.erase(std::unique(RequiredPieceData.begin(), RequiredPieceData.end()), RequiredPieceData.end()); - return Required; + return RequiredPieceData; } std::vector lcModel::GetRequiredSynthPieces() const @@ -497,7 +487,7 @@ std::vector lcModel::GetRequiredSynthPieces() const for (const std::unique_ptr& Piece : Model->mPieces) { - lcPieceData* PieceData = Piece->mPieceData; + lcPieceData* PieceData = Piece->mPieceData.Get(); if (PieceData->IsModel()) Collect(PieceData->GetModel()); @@ -515,10 +505,10 @@ std::vector lcModel::GetRequiredSynthPieces() const void lcModel::WaitForAssets() const { - const std::vector Required = GetRequiredPieceData(); + const std::vector RequiredPieceData = GetRequiredPieceData(); lcPiecesLibrary* Library = lcGetPiecesLibrary(); - Library->WaitForPieceData(Required); + Library->WaitForPieceData(RequiredPieceData); Library->WaitForSynthMeshes(GetRequiredSynthPieces()); // Temporary minifigure and thumbnail models have no container piece to update. @@ -533,15 +523,15 @@ lcResult lcModel::EnsureAssetsReady() const { WaitForAssets(); - const std::vector Required = GetRequiredPieceData(); + const std::vector RequiredPieceData = GetRequiredPieceData(); lcPiecesLibrary* Library = lcGetPiecesLibrary(); - for (const lcPieceData* PieceData : Required) + for (const lcPieceDataRef& RequiredData : RequiredPieceData) { - if (PieceData->mState == lcAssetState::Loaded) + if (RequiredData->mState == lcAssetState::Loaded) continue; - const QString Error = Library->GetPieceDataLoadError(PieceData); + const QString Error = Library->GetPieceDataLoadError(RequiredData.Get()); if (!Error.isEmpty()) return lcUnexpected(Error); @@ -1022,14 +1012,14 @@ void lcModel::LoadLDraw(QIODevice& Device, Project* Project) if (!CurrentGroups.empty()) Piece->SetGroup(CurrentGroups[CurrentGroups.size() - 1]); - lcPieceData* PieceData = Library->FindPieceData(PartId.toLatin1().constData(), Project, true, true); + lcPieceDataRef PieceData = Library->AcquirePieceData(PartId.toLatin1().constData(), Project, true, true); const float* Matrix = IncludeTransform.GetFloats(); const lcMatrix44 Transform(lcVector4(Matrix[0], Matrix[2], -Matrix[1], 0.0f), lcVector4(Matrix[8], Matrix[10], -Matrix[9], 0.0f), lcVector4(-Matrix[4], -Matrix[6], Matrix[5], 0.0f), lcVector4(Matrix[12], Matrix[14], -Matrix[13], 1.0f)); Piece->SetFileLine(mFileLines.size()); - Piece->SetPieceData(PieceData, PartId, false, false); + Piece->SetPieceData(std::move(PieceData), PartId, false, false); Piece->Initialize(Transform, CurrentStep); Piece->SetColorCode(ColorCode); Piece->VerifyControlPoints(ControlPoints); @@ -1145,8 +1135,7 @@ bool lcModel::LoadBinary(lcFile* file) lcMatrix44 WorldMatrix = lcMul(lcMatrix44RotationZ(rot[2] * LC_DTOR), lcMul(lcMatrix44RotationY(rot[1] * LC_DTOR), lcMatrix44RotationX(rot[0] * LC_DTOR))); WorldMatrix.SetTranslation(pos); - lcPieceData* PieceData = Library->FindPieceData(name, mProject, true, false); - lcPiece* pPiece = new lcPiece(PieceData); + lcPiece* pPiece = new lcPiece(Library->AcquirePieceData(name, mProject, true, false)); pPiece->Initialize(WorldMatrix, step); pPiece->SetColorCode(lcGetColorCodeFromOriginalColor(color)); @@ -1346,7 +1335,7 @@ bool lcModel::LoadInventory(const std::vector& SetInventory) for (const lcSetInventoryItem& SetInventoryItem : SetInventory) { - lcPieceData* PieceData = Library->FindPieceData(SetInventoryItem.PartID + ".dat", mProject, true, false); + const lcPieceDataRef PieceData = Library->AcquirePieceData(SetInventoryItem.PartID + ".dat", mProject, true, false); int Quantity = SetInventoryItem.Quantity; while (Quantity--) @@ -1362,14 +1351,14 @@ bool lcModel::LoadInventory(const std::vector& SetInventory) if (mPieces.empty()) return false; - std::vector Required; + std::vector RequiredPieceData; - Required.reserve(mPieces.size()); + RequiredPieceData.reserve(mPieces.size()); for (const std::unique_ptr& Piece : mPieces) - Required.push_back(Piece->mPieceData); + RequiredPieceData.push_back(Piece->mPieceData); - if (!Library->WaitForPieceData(Required)) + if (!Library->WaitForPieceData(RequiredPieceData)) return false; Library->mBuffersDirty = true; @@ -1671,7 +1660,7 @@ void lcModel::GetScene(lcScene* Scene, const lcCamera* ViewCamera, bool AllowHig if (Scene->GetDrawInsertPreview()) { - for (const lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo) + for (const lcPreviewInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo) PreviewPieceInfoTransform.PieceData->AddRenderMeshes(Scene, PreviewPieceInfoTransform.Transform, PreviewPieceInfoTransform.ColorIndex, lcRenderMeshState::Preview, false); } } @@ -1691,7 +1680,7 @@ void lcModel::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMat if (Scene->GetDrawInsertPreview()) { - for (const lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo) + for (const lcPreviewInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo) PreviewPieceInfoTransform.PieceData->AddRenderMeshes(Scene, lcMul(PreviewPieceInfoTransform.Transform, WorldMatrix), PreviewPieceInfoTransform.ColorIndex, lcRenderMeshState::Preview, false); } } @@ -3102,11 +3091,13 @@ lcMatrix33 lcModel::GetRelativeRotation() const lcPiece* lcModel::AddPiece(lcPieceData* PieceData, quint32 Section) { if (!PieceData) - PieceData = gMainWindow->GetInsertPieceData(); + PieceData = gMainWindow->GetInsertPieceData().Get(); if (!PieceData) return nullptr; + const lcPieceDataRef PieceDataRef(PieceData); + lcPiece* Last = mPieces.empty() ? nullptr : mPieces.back().get(); for (const std::unique_ptr& Piece : mPieces) @@ -3118,7 +3109,7 @@ lcPiece* lcModel::AddPiece(lcPieceData* PieceData, quint32 Section) } } - lcGetPiecesLibrary()->EnsurePieceDataAssetsReady(PieceData); + lcGetPiecesLibrary()->EnsurePieceDataAssetsReady(PieceDataRef); if (Last) { @@ -3201,7 +3192,7 @@ lcPiece* lcModel::AddPiece(lcPieceData* PieceData, quint32 Section) void lcModel::AddPiece(std::unique_ptr Piece, size_t PieceIndex) { - const lcPieceData* PieceData = Piece->mPieceData; + const lcPieceData* PieceData = Piece->mPieceData.Get(); if (!PieceData->IsModel()) { @@ -3332,7 +3323,7 @@ void lcModel::RotateFocusedTrainTrack(int Direction) lcMatrix44 ConnectionTransform = lcMul(TrainTrackInfo->GetConnections()[RotateConnectionIndex].Transform, FocusPiece->mModelWorld); int NewConnectionIndex = (RotateConnectionIndex + Direction + static_cast(TrainTrackInfo->GetConnections().size())) % TrainTrackInfo->GetConnections().size(); - Transform = lcTrainTrackInfo::CalculateTransformToConnection(ConnectionTransform, FocusPiece->mPieceData, NewConnectionIndex); + Transform = lcTrainTrackInfo::CalculateTransformToConnection(ConnectionTransform, FocusPiece->mPieceData.Get(), NewConnectionIndex); if (!Transform) return; @@ -3412,22 +3403,21 @@ void lcModel::UpdateSelectedPiecesTrainTrackConnections() void lcModel::SetPreviewInsertPieceInfo(std::vector&& PreviewInsertPieceInfo) { lcPiecesLibrary* Library = lcGetPiecesLibrary(); - std::vector Required; + std::vector RequiredPieceData; + std::vector PreviewPieces; - Required.reserve(PreviewInsertPieceInfo.size()); + RequiredPieceData.reserve(PreviewInsertPieceInfo.size()); + PreviewPieces.reserve(PreviewInsertPieceInfo.size()); - for (lcInsertPieceInfo& InfoTransform : PreviewInsertPieceInfo) + for (const lcInsertPieceInfo& InfoTransform : PreviewInsertPieceInfo) { + PreviewPieces.emplace_back(InfoTransform); Library->RequestPieceDataLoad(InfoTransform.PieceData, lcLoadPriority::Normal); - Required.push_back(InfoTransform.PieceData); + RequiredPieceData.push_back(PreviewPieces.back().PieceData); } - Library->WaitForPieceData(Required); - - for (lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo) - Library->ReleasePieceData(PreviewPieceInfoTransform.PieceData); - - mPreviewInsertPieceInfo = std::move(PreviewInsertPieceInfo); + Library->WaitForPieceData(RequiredPieceData); + mPreviewInsertPieceInfo = std::move(PreviewPieces); UpdateAllViews(); } @@ -3746,7 +3736,7 @@ void lcModel::RenamePieces(const std::vector& Renames) } } - auto RenameHistorySequence = [&Renames, ModelPieceData = mPieceData](std::vector>& HistorySequence) + auto RenameHistorySequence = [&Renames, ModelPieceData = mPieceData.Get()](std::vector>& HistorySequence) { for (const std::unique_ptr& ModelHistory : HistorySequence) ModelHistory->RenamePieces(Renames, ModelPieceData); @@ -4933,7 +4923,7 @@ void lcModel::GetPartsListForStep(lcStep Step, int DefaultColorIndex, lcPartsLis void lcModel::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex, std::vector& ModelParts) const { if (mHasDirectGeometry && mPieceData->GetMesh()) - ModelParts.emplace_back(lcModelPartsEntry{ WorldMatrix, mPieceData, mPieceData->GetMesh(), DefaultColorIndex }); + ModelParts.emplace_back(lcModelPartsEntry{ WorldMatrix, mPieceData.Get(), mPieceData->GetMesh(), DefaultColorIndex }); for (const std::unique_ptr& Piece : mPieces) Piece->GetModelParts(WorldMatrix, DefaultColorIndex, ModelParts); @@ -5098,7 +5088,7 @@ void lcModel::RemoveFromSelectionAction(const std::vector& Objects) std::vector lcModel::GetSelectionModePieces(lcSelectionMode SelectionMode, const lcPiece* SelectedPiece) const { - const lcPieceData* PieceData = SelectedPiece->mPieceData; + const lcPieceData* PieceData = SelectedPiece->mPieceData.Get(); const int ColorIndex = SelectedPiece->GetColorIndex(); std::vector Pieces; @@ -5351,7 +5341,7 @@ void lcModel::FindReplacePiece(bool SearchForward, bool FindAll, bool Replace) Piece->SetColorIndex(Params.ReplaceColorIndex); if (ReplacePieceData) - Piece->SetPieceData(Params.ReplacePieceData, QString(), true, true); + Piece->SetPieceData(lcPieceDataRef(Params.ReplacePieceData), QString(), true, true); }; size_t StartIndex = mPieces.size() - 1; @@ -5635,7 +5625,7 @@ bool lcModel::InsertPieceToolClicked() lcPiece* Piece = nullptr; - for (const lcInsertPieceInfo& InsertPieceInfo : mPreviewInsertPieceInfo) + for (const lcPreviewInsertPieceInfo& InsertPieceInfo : mPreviewInsertPieceInfo) { Piece = new lcPiece(InsertPieceInfo.PieceData); @@ -6236,10 +6226,10 @@ void lcModel::ShowMinifigDialog() for (int PartIndex = 0; PartIndex < LC_MFW_NUMITEMS; PartIndex++) { - if (!Minifig.Parts[PartIndex]) + if (!Minifig.PieceData[PartIndex]) continue; - lcPiece* Piece = new lcPiece(Minifig.Parts[PartIndex]); + lcPiece* Piece = new lcPiece(Minifig.PieceData[PartIndex]); Piece->Initialize(Minifig.Matrices[PartIndex], mCurrentStep); Piece->SetColorIndex(Minifig.ColorIndices[PartIndex]); @@ -6268,10 +6258,10 @@ void lcModel::SetMinifig(const lcMinifig& Minifig) for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++) { - if (!Minifig.Parts[PartIdx]) + if (!Minifig.PieceData[PartIdx]) continue; - lcPiece* Piece = new lcPiece(Minifig.Parts[PartIdx]); + lcPiece* Piece = new lcPiece(Minifig.PieceData[PartIdx]); Piece->Initialize(Minifig.Matrices[PartIdx], 1); Piece->SetColorIndex(Minifig.ColorIndices[PartIdx]); diff --git a/common/lc_model.h b/common/lc_model.h index beaad3bb..5d800cc7 100644 --- a/common/lc_model.h +++ b/common/lc_model.h @@ -1,6 +1,7 @@ #pragma once #include "lc_math.h" +#include "lc_assettypes.h" #include "lc_commands.h" #include "lc_objectproperty.h" #include "lc_result.h" @@ -74,6 +75,19 @@ struct lcInsertPieceInfo int ColorIndex; }; +// Stored insertion previews own their definitions; lcInsertPieceInfo remains borrowed. +struct lcPreviewInsertPieceInfo +{ + explicit lcPreviewInsertPieceInfo(const lcInsertPieceInfo& Info) + : PieceData(Info.PieceData), Transform(Info.Transform), ColorIndex(Info.ColorIndex) + { + } + + lcPieceDataRef PieceData; + lcMatrix44 Transform; + int ColorIndex; +}; + struct lcModelPieceRename { lcPieceData* PieceData; @@ -187,14 +201,14 @@ public: void QueueMeshBuild(); void UpdateAllViews() const; void RefreshPreview() const; - std::vector GetRequiredPieceData() const; + std::vector GetRequiredPieceData() const; std::vector GetRequiredSynthPieces() const; void WaitForAssets() const; lcResult EnsureAssetsReady() const; lcPieceData* GetPieceData() const { - return mPieceData; + return mPieceData.Get(); } const std::vector>& GetPieces() const @@ -329,7 +343,7 @@ public: void UpdateSelectedPiecesTrainTrackConnections(); void SetPreviewInsertPieceInfo(std::vector&& PreviewInsertPieceInfo); - const std::vector& GetPreviewInsertPieceInfo() const + const std::vector& GetPreviewInsertPieceInfo() const { return mPreviewInsertPieceInfo; } @@ -506,7 +520,7 @@ protected: // Their order and the current-directory preference are saved with the model. std::vector mAssetSourceDirectories; bool mPreferCurrentAssetDirectory = true; - lcPieceData* mPieceData; + lcPieceDataRef mPieceData; bool mIsPreview; bool mActive; @@ -517,7 +531,7 @@ protected: bool mMouseToolMovePositionValid = false; bool mMouseToolMoveUsesFocus = false; bool mMouseToolFirstMove; - std::vector mPreviewInsertPieceInfo; + std::vector mPreviewInsertPieceInfo; std::vector> mPieces; std::vector> mCameras; diff --git a/common/lc_previewwidget.cpp b/common/lc_previewwidget.cpp index f2e5fad3..894b267d 100644 --- a/common/lc_previewwidget.cpp +++ b/common/lc_previewwidget.cpp @@ -94,9 +94,9 @@ void lcPreviewDockWidget::RefreshModel(const lcModel* Model) if (mLockAction->isChecked() || mRefreshingModel) return; - const std::vector Required = mPreview->GetModel()->GetRequiredPieceData(); + const std::vector RequiredPieceData = mPreview->GetModel()->GetRequiredPieceData(); - if (std::find(Required.begin(), Required.end(), Model->GetPieceData()) == Required.end()) + if (std::find(RequiredPieceData.begin(), RequiredPieceData.end(), Model->GetPieceData()) == RequiredPieceData.end()) return; mRefreshingModel = true; @@ -106,13 +106,12 @@ void lcPreviewDockWidget::RefreshModel(const lcModel* Model) lcPiecesLibrary* Library = lcGetPiecesLibrary(); - for (lcPieceData* PieceData : Required) + for (const lcPieceDataRef& RequiredData : RequiredPieceData) { - if (PieceData->mState != lcAssetState::Unloaded) + if (RequiredData->mState != lcAssetState::Unloaded) continue; - Library->RequestPieceDataLoad(PieceData, lcLoadPriority::Normal); - Library->ReleasePieceData(PieceData); + Library->RequestPieceDataLoad(RequiredData.Get(), lcLoadPriority::Normal); } std::vector UpdatedModels; @@ -128,10 +127,10 @@ void lcPreviewDockWidget::RefreshAfterAssetChange(const std::vectorGetModel()->GetPieces().empty()) return; - const std::vector Required = mPreview->GetModel()->GetRequiredPieceData(); - bool Affected = std::any_of(Required.begin(), Required.end(), [&ChangedPieces](lcPieceData* PieceData) + const std::vector RequiredPieceData = mPreview->GetModel()->GetRequiredPieceData(); + bool Affected = std::any_of(RequiredPieceData.begin(), RequiredPieceData.end(), [&ChangedPieces](const lcPieceDataRef& RequiredData) { - return std::find(ChangedPieces.begin(), ChangedPieces.end(), PieceData) != ChangedPieces.end(); + return std::find(ChangedPieces.begin(), ChangedPieces.end(), RequiredData) != ChangedPieces.end(); }); if (!Affected) @@ -154,11 +153,11 @@ void lcPreviewDockWidget::RefreshAfterAssetChange(const std::vector Required = mPreview->GetModel()->GetRequiredPieceData(); + const std::vector RequiredPieceData = mPreview->GetModel()->GetRequiredPieceData(); - for (const lcPieceData* PieceData : Required) + for (const lcPieceDataRef& RequiredData : RequiredPieceData) { - if (PieceData->mState == lcAssetState::Failed || PieceData->mState == lcAssetState::Unloaded) + if (RequiredData->mState == lcAssetState::Failed || RequiredData->mState == lcAssetState::Unloaded) { mAssetRefreshPending = false; mViewWidget->hide(); @@ -166,7 +165,7 @@ void lcPreviewDockWidget::RefreshPendingAssets() return; } - if (PieceData->mState != lcAssetState::Loaded) + if (RequiredData->mState != lcAssetState::Loaded) { mAssetRefreshPending = true; mViewWidget->hide(); @@ -294,7 +293,9 @@ bool lcPreview::SetCurrentPiece(lcPieceData* PieceData, int ColorCode) } lcPiecesLibrary* Library = lcGetPiecesLibrary(); - bool Ready = Library->RequestPieceDataLoad(PieceData, lcLoadPriority::Blocking); + const lcPieceDataRef PieceDataRef(PieceData); + + bool Ready = Library->WaitForPieceData(PieceDataRef); if (Ready && PieceData->IsModel()) Ready = static_cast(PieceData->GetModel()->EnsureAssetsReady()); @@ -303,7 +304,6 @@ bool lcPreview::SetCurrentPiece(lcPieceData* PieceData, int ColorCode) if (!Ready) { - Library->ReleasePieceData(PieceData); ClearPreview(); return false; @@ -320,7 +320,6 @@ bool lcPreview::SetCurrentPiece(lcPieceData* PieceData, int ColorCode) mModel->UpdatePieceData(UpdatedModels); - Library->ReleasePieceData(PieceData); ZoomExtents(lcGeometryBoundsMode::IncludeFallback, true); return true; @@ -350,24 +349,19 @@ void lcPreview::UpdatePreview() { if (ModelPiece->mPieceData) { - PieceData = ModelPiece->mPieceData; + PieceData = ModelPiece->mPieceData.Get(); ColorCode = ModelPiece->GetColorCode(); break; } } - lcPiecesLibrary* Library = lcGetPiecesLibrary(); - - if (PieceData) - Library->AddPieceDataReference(PieceData); + lcPieceDataRef PreviousPieceData(PieceData); ClearPreview(); if (PieceData && ColorCode > -1) SetCurrentPiece(PieceData, ColorCode); - if (PieceData) - Library->ReleasePieceData(PieceData); } void lcPreview::RefreshDescription() diff --git a/common/lc_synth.cpp b/common/lc_synth.cpp index 03d0580a..5ebd6da0 100644 --- a/common/lc_synth.cpp +++ b/common/lc_synth.cpp @@ -173,7 +173,7 @@ void lcSynthInit() for (const auto& HoseInfo: FlexibleHoses) { - lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoFlexibleHose(HoseInfo.Length, HoseInfo.NumSections, HoseInfo.EdgePart2)); @@ -224,7 +224,7 @@ void lcSynthInit() for (const auto& HoseInfo: FlexSystemHoses) { - lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoFlexSystemHose(HoseInfo.Length, HoseInfo.NumSections)); @@ -272,7 +272,7 @@ void lcSynthInit() for (const auto& TubeInfo: PneumaticTubes) { - lcPieceData* PieceData = Library->FindPieceData(TubeInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(TubeInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoPneumaticTube(TubeInfo.Length, TubeInfo.NumSections, "71533k02.dat")); @@ -281,7 +281,7 @@ void lcSynthInit() RegularInfo.PartID[7] = '2'; RegularInfo.Length -= 40.0f; - PieceData = Library->FindPieceData(RegularInfo.PartID, nullptr, false, false); + PieceData = Library->FindPieceData(RegularInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoPneumaticTube(RegularInfo.Length, RegularInfo.NumSections, "71533k01.dat")); @@ -318,7 +318,7 @@ void lcSynthInit() for (const auto& HoseInfo: RibbedHoses) { - lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(HoseInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoRibbedHose(HoseInfo.Length, HoseInfo.NumSections)); @@ -343,7 +343,7 @@ void lcSynthInit() for (const auto& AxleInfo: FlexibleAxles) { - lcPieceData* PieceData = Library->FindPieceData(AxleInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(AxleInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoFlexibleAxle(AxleInfo.Length, AxleInfo.NumSections)); @@ -364,7 +364,7 @@ void lcSynthInit() for (const auto& StringInfo: BraidedStrings) { - lcPieceData* PieceData = Library->FindPieceData(StringInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(StringInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoBraidedString(StringInfo.Length, StringInfo.NumSections)); @@ -385,7 +385,7 @@ void lcSynthInit() for (const auto& AbsorberInfo: ShockAbsorbers) { - lcPieceData* PieceData = Library->FindPieceData(AbsorberInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(AbsorberInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoShockAbsorber(AbsorberInfo.SpringPart)); @@ -414,7 +414,7 @@ void lcSynthInit() for (const auto& ActuatorInfo: Actuators) { - lcPieceData* PieceData = Library->FindPieceData(ActuatorInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(ActuatorInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoActuator(ActuatorInfo.BodyPart, ActuatorInfo.PistonPart, @@ -439,7 +439,7 @@ void lcSynthInit() for (const auto& JointInfo: UniversalJoints) { - lcPieceData* PieceData = Library->FindPieceData(JointInfo.PartID, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(JointInfo.PartID, nullptr); if (PieceData) PieceData->SetSynthInfo(new lcSynthInfoUniversalJoint(JointInfo.Length, JointInfo.EndOffset, JointInfo.EndPart, JointInfo.CenterPart)); diff --git a/common/lc_texturemanager.cpp b/common/lc_texturemanager.cpp index 7371cd12..474b5df2 100644 --- a/common/lc_texturemanager.cpp +++ b/common/lc_texturemanager.cpp @@ -3,8 +3,6 @@ #include "lc_file.h" #include "lc_string.h" -lcTextureBuildResult::~lcTextureBuildResult() = default; - lcTextureManager::~lcTextureManager() { Clear(); diff --git a/common/lc_thumbnailmanager.cpp b/common/lc_thumbnailmanager.cpp index a9b6af24..39c06718 100644 --- a/common/lc_thumbnailmanager.cpp +++ b/common/lc_thumbnailmanager.cpp @@ -27,9 +27,6 @@ void lcThumbnailManager::Clear() mModel.reset(); mPendingGeneratedModels.clear(); - for (auto &[ThumbnailId, Thumbnail] : mThumbnails) - ReleaseRequiredPieces(Thumbnail); - mThumbnails.clear(); } @@ -41,9 +38,9 @@ void lcThumbnailManager::RefreshPieces(const std::vector& ChangedP for (std::map::value_type& Entry : mThumbnails) { const lcPartThumbnail& Thumbnail = Entry.second; - const bool RequiredChanged = std::any_of(Thumbnail.Required.begin(), Thumbnail.Required.end(), [&Changed](lcPieceData* PieceData) + const bool RequiredChanged = std::any_of(Thumbnail.RequiredPieceData.begin(), Thumbnail.RequiredPieceData.end(), [&Changed](const lcPieceDataRef& RequiredData) { - return Changed.find(PieceData) != Changed.end(); + return Changed.find(RequiredData.Get()) != Changed.end(); }); if (Changed.find(Thumbnail.PieceData) == Changed.end() && !RequiredChanged) @@ -102,8 +99,6 @@ void lcThumbnailManager::ReleaseThumbnail(lcPartThumbnailId ThumbnailId) { mPendingGeneratedModels.erase(ThumbnailId); - ReleaseRequiredPieces(Thumbnail); - mThumbnails.erase(ThumbnailIt); } } @@ -114,8 +109,9 @@ void lcThumbnailManager::RefreshRequiredPieces(lcPartThumbnail& Thumbnail) if (Thumbnail.PieceData->IsModel()) { - std::vector ModelRequired = Thumbnail.PieceData->GetModel()->GetRequiredPieceData(); - Required.insert(Required.end(), ModelRequired.begin(), ModelRequired.end()); + std::vector ModelPieceData = Thumbnail.PieceData->GetModel()->GetRequiredPieceData(); + for (const lcPieceDataRef& ModelData : ModelPieceData) + Required.push_back(ModelData.Get()); } else if (Thumbnail.PieceData->IsProject()) { @@ -123,34 +119,37 @@ void lcThumbnailManager::RefreshRequiredPieces(lcPartThumbnail& Thumbnail) if (Model) { - std::vector ModelRequired = Model->GetRequiredPieceData(); - Required.insert(Required.end(), ModelRequired.begin(), ModelRequired.end()); + std::vector ModelPieceData = Model->GetRequiredPieceData(); + for (const lcPieceDataRef& ModelData : ModelPieceData) + Required.push_back(ModelData.Get()); } } std::sort(Required.begin(), Required.end()); Required.erase(std::unique(Required.begin(), Required.end()), Required.end()); - std::unordered_set Previous(Thumbnail.Required.begin(), Thumbnail.Required.end()); + std::unordered_map Previous; + + for (size_t Index = 0; Index < Thumbnail.RequiredPieceData.size(); Index++) + Previous.emplace(Thumbnail.RequiredPieceData[Index].Get(), Index); + + std::vector RequiredPieceData; + RequiredPieceData.reserve(Required.size()); for (lcPieceData* PieceData : Required) { - if (Previous.erase(PieceData) == 0) + const auto It = Previous.find(PieceData); + + if (It != Previous.end()) + RequiredPieceData.push_back(std::move(Thumbnail.RequiredPieceData[It->second])); + else + { + RequiredPieceData.emplace_back(PieceData); mLibrary->RequestPieceDataLoad(PieceData, lcLoadPriority::Low); + } } - for (lcPieceData* PieceData : Previous) - mLibrary->ReleasePieceData(PieceData); - - Thumbnail.Required = std::move(Required); -} - -void lcThumbnailManager::ReleaseRequiredPieces(lcPartThumbnail& Thumbnail) -{ - for (lcPieceData* PieceData : Thumbnail.Required) - mLibrary->ReleasePieceData(PieceData); - - Thumbnail.Required.clear(); + Thumbnail.RequiredPieceData = std::move(RequiredPieceData); } void lcThumbnailManager::UpdateThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail) @@ -159,15 +158,15 @@ void lcThumbnailManager::UpdateThumbnail(lcPartThumbnailId ThumbnailId, lcPartTh bool Pending = false; - for (const lcPieceData* PieceData : Thumbnail.Required) + for (const lcPieceDataRef& RequiredData : Thumbnail.RequiredPieceData) { - if (PieceData->mState == lcAssetState::Failed || PieceData->mState == lcAssetState::Unloaded) + if (RequiredData->mState == lcAssetState::Failed || RequiredData->mState == lcAssetState::Unloaded) { DrawFailedThumbnail(ThumbnailId, Thumbnail); return; } - if (PieceData->mState != lcAssetState::Loaded) + if (RequiredData->mState != lcAssetState::Loaded) Pending = true; } @@ -190,7 +189,10 @@ void lcThumbnailManager::PartLoaded(lcPieceData* PieceData) std::vector Ready; for (const auto& [ThumbnailId, Thumbnail] : mThumbnails) - if (Thumbnail.Pixmap.isNull() && std::find(Thumbnail.Required.begin(), Thumbnail.Required.end(), PieceData) != Thumbnail.Required.end()) + if (Thumbnail.Pixmap.isNull() && std::any_of(Thumbnail.RequiredPieceData.begin(), Thumbnail.RequiredPieceData.end(), [PieceData](const lcPieceDataRef& RequiredData) + { + return RequiredData == PieceData; + })) Ready.push_back(ThumbnailId); for (lcPartThumbnailId ThumbnailId : Ready) @@ -207,7 +209,10 @@ void lcThumbnailManager::PartLoadFailed(lcPieceData* PieceData) std::vector Failed; for (const auto& [ThumbnailId, Thumbnail] : mThumbnails) - if (Thumbnail.Pixmap.isNull() && std::find(Thumbnail.Required.begin(), Thumbnail.Required.end(), PieceData) != Thumbnail.Required.end()) + if (Thumbnail.Pixmap.isNull() && std::any_of(Thumbnail.RequiredPieceData.begin(), Thumbnail.RequiredPieceData.end(), [PieceData](const lcPieceDataRef& RequiredData) + { + return RequiredData == PieceData; + })) Failed.push_back(ThumbnailId); for (lcPartThumbnailId ThumbnailId : Failed) @@ -296,7 +301,7 @@ void lcThumbnailManager::DrawFailedThumbnail(lcPartThumbnailId ThumbnailId, lcPa Thumbnail.Pixmap.setDevicePixelRatio(Thumbnail.DeviceScale); - ReleaseRequiredPieces(Thumbnail); + Thumbnail.RequiredPieceData.clear(); emit ThumbnailReady(ThumbnailId, Thumbnail.Pixmap); SchedulePendingThumbnails(); @@ -387,8 +392,8 @@ void lcThumbnailManager::DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThum { RefreshRequiredPieces(Thumbnail); - for (const lcPieceData* Required : Thumbnail.Required) - if (Required->IsLoading()) + for (const lcPieceDataRef& RequiredData : Thumbnail.RequiredPieceData) + if (RequiredData->IsLoading()) return; DrawFailedThumbnail(ThumbnailId, Thumbnail); @@ -441,7 +446,7 @@ void lcThumbnailManager::DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThum Thumbnail.Pixmap = QPixmap::fromImage(Image).scaled(ScaledSize, ScaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - ReleaseRequiredPieces(Thumbnail); + Thumbnail.RequiredPieceData.clear(); emit ThumbnailReady(ThumbnailId, Thumbnail.Pixmap); SchedulePendingThumbnails(); diff --git a/common/lc_thumbnailmanager.h b/common/lc_thumbnailmanager.h index a6920a98..d34579e6 100644 --- a/common/lc_thumbnailmanager.h +++ b/common/lc_thumbnailmanager.h @@ -1,12 +1,14 @@ #pragma once +#include "lc_assettypes.h" + class lcPiecesLibrary; struct lcPartThumbnail { QPixmap Pixmap; lcPieceData* PieceData; - std::vector Required; + std::vector RequiredPieceData; int ColorIndex; int Size; float DeviceScale; @@ -41,7 +43,6 @@ protected slots: protected: void RefreshRequiredPieces(lcPartThumbnail& Thumbnail); - void ReleaseRequiredPieces(lcPartThumbnail& Thumbnail); void UpdateThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail); void DrawThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail); void DrawFailedThumbnail(lcPartThumbnailId ThumbnailId, lcPartThumbnail& Thumbnail); diff --git a/common/lc_timelinewidget.cpp b/common/lc_timelinewidget.cpp index 65390fc1..7fb831f1 100644 --- a/common/lc_timelinewidget.cpp +++ b/common/lc_timelinewidget.cpp @@ -594,7 +594,7 @@ void lcTimelineWidget::PreviewSelection(QTreeWidgetItem* CurrentItem) if (!Piece) return; - lcPieceData* PieceData = Piece->mPieceData; + lcPieceData* PieceData = Piece->mPieceData.Get(); if (!PieceData) return; diff --git a/common/lc_traintrack.cpp b/common/lc_traintrack.cpp index 7c4e6017..0d9edd5c 100644 --- a/common/lc_traintrack.cpp +++ b/common/lc_traintrack.cpp @@ -76,7 +76,7 @@ void lcTrainTrackInfo::Initialize(lcPiecesLibrary* Library) for (QJsonArray::const_iterator IDIt = JsonIDs.constBegin(); IDIt != JsonIDs.constEnd(); ++IDIt) { - lcPieceData* PieceData = Library->FindPieceData(IDIt->toString().toLatin1(), nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(IDIt->toString().toLatin1(), nullptr); if (PieceData) PieceData->SetTrainTrackInfo(new lcTrainTrackInfo(Connections, Color, true)); @@ -86,7 +86,7 @@ void lcTrainTrackInfo::Initialize(lcPiecesLibrary* Library) for (QJsonArray::const_iterator IDIt = JsonIDs.constBegin(); IDIt != JsonIDs.constEnd(); ++IDIt) { - lcPieceData* PieceData = Library->FindPieceData(IDIt->toString().toLatin1(), nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(IDIt->toString().toLatin1(), nullptr); if (PieceData) PieceData->SetTrainTrackInfo(new lcTrainTrackInfo(Connections, Color, false)); @@ -102,7 +102,7 @@ void lcTrainTrackInfo::Initialize(lcPiecesLibrary* Library) size_t Group = qHash(SleepersIt.key()); QString PartId = SleepersIt->toString(); - lcPieceData* SleeperPieceData = Library->FindPieceData(PartId.toLatin1(), nullptr, false, false); + lcPieceData* SleeperPieceData = Library->FindPieceData(PartId.toLatin1(), nullptr); if (SleeperPieceData) mSleepers[Group] = SleeperPieceData; @@ -245,7 +245,7 @@ std::vector lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* Cur if (!SleeperPieceData) return Pieces; - std::optional SleeperTransform = GetConnectionTransform(CurrentPiece->mPieceData, CurrentPiece->mModelWorld, ConnectionIndex, SleeperPieceData, 0); + std::optional SleeperTransform = GetConnectionTransform(CurrentPiece->mPieceData.Get(), CurrentPiece->mModelWorld, ConnectionIndex, SleeperPieceData, 0); if (!SleeperTransform) return Pieces; @@ -260,7 +260,7 @@ std::vector lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* Cur } else { - std::optional Transform = GetConnectionTransform(CurrentPiece->mPieceData, CurrentPiece->mModelWorld, ConnectionIndex, PieceData, NewConnectionIndex); + std::optional Transform = GetConnectionTransform(CurrentPiece->mPieceData.Get(), CurrentPiece->mModelWorld, ConnectionIndex, PieceData, NewConnectionIndex); if (Transform) Pieces.emplace_back(lcInsertPieceInfo{ PieceData, Transform.value(), ColorIndex }); diff --git a/common/lc_view.cpp b/common/lc_view.cpp index 2ded0204..e1c2ab98 100644 --- a/common/lc_view.cpp +++ b/common/lc_view.cpp @@ -474,7 +474,7 @@ lcVector3 lcView::GetMoveDirection(const lcVector3& Direction) const void lcView::UpdatePiecePreview() { - lcPieceData* PreviewPieceData = gMainWindow->GetInsertPieceData(); + const lcPieceDataRef PreviewPieceData(gMainWindow->GetInsertPieceData()); std::vector InsertPieceInfo; bool IsConnection; @@ -486,7 +486,7 @@ void lcView::UpdatePiecePreview() GetActiveModel()->SetPreviewInsertPieceInfo(std::move(InsertPieceInfo)); } -std::pair, bool> lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, lcPieceData* PieceData, lcPiece* MovingPiece) const +std::pair, bool> lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, const lcPieceDataRef& PieceData, lcPiece* MovingPiece) const { lcModel* ActiveModel = GetActiveModel(); @@ -501,10 +501,10 @@ std::pair, bool> lcView::GetMouseInsertPieceInfo( while (ObjectRayTest.ObjectSection.Object) { lcPiece* MousePiece = reinterpret_cast(ObjectRayTest.ObjectSection.Object); - lcPieceData* MousePieceData = MousePiece->mPieceData; + lcPieceData* MousePieceData = MousePiece->mPieceData.Get(); const bool Retest = MousePieceData->mState != lcAssetState::Loaded || MousePieceData->IsModel() || MousePieceData->IsProject() || MousePiece->IsGeneratedMeshPending(); - lcGetPiecesLibrary()->EnsurePieceDataAssetsReady(MousePieceData); + lcGetPiecesLibrary()->EnsurePieceDataAssetsReady(MousePiece->mPieceData); if (MousePieceData->GetSynthInfo()) lcGetPiecesLibrary()->WaitForSynthMeshes({ MousePiece }); @@ -535,7 +535,7 @@ std::pair, bool> lcView::GetMouseInsertPieceInfo( lcVector3 ClosestPoint = ObjectRayTest.Start + lcNormalize(ObjectRayTest.End - ObjectRayTest.Start) * ObjectRayTest.Distance; quint32 FocusSection = MousePiece->GetFocusSection(); - std::vector TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, PieceData, MovingPiece, gMainWindow->mColorIndex, FocusSection, AllowNewPieces, ClosestPoint); + std::vector TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, PieceData.Get(), MovingPiece, gMainWindow->mColorIndex, FocusSection, AllowNewPieces, ClosestPoint); if (!TrainTracks.empty()) return { TrainTracks, true }; @@ -564,7 +564,7 @@ std::pair, bool> lcView::GetMouseInsertPieceInfo( lcMatrix44 WorldMatrix = ObjectRayTest.PieceRayTest.Transform; WorldMatrix.SetTranslation(Position); - return { { lcInsertPieceInfo{ PieceData, WorldMatrix, gMainWindow->mColorIndex } }, false }; + return { { lcInsertPieceInfo{ PieceData.Get(), WorldMatrix, gMainWindow->mColorIndex } }, false }; } std::array ClickPoints = {{ lcVector3((float)mMouseX, (float)mMouseY, 0.0f), lcVector3((float)mMouseX, (float)mMouseY, 1.0f) }}; @@ -583,7 +583,7 @@ std::pair, bool> lcView::GetMouseInsertPieceInfo( if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(0, 0, 1, BoundingBox.Min.z))) { Intersection = ActiveModel->SnapPosition(Intersection); - return { { lcInsertPieceInfo{ PieceData, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false }; + return { { lcInsertPieceInfo{ PieceData.Get(), lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false }; } lcVector3 Position; @@ -596,10 +596,10 @@ std::pair, bool> lcView::GetMouseInsertPieceInfo( if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(FrontVector, -lcDot(FrontVector, Position)))) { Intersection = ActiveModel->SnapPosition(Intersection); - return { { lcInsertPieceInfo{ PieceData, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false }; + return { { lcInsertPieceInfo{ PieceData.Get(), lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false }; } - return { { lcInsertPieceInfo{ PieceData, lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } }, false }; + return { { lcInsertPieceInfo{ PieceData.Get(), lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } }, false }; } lcVector3 lcView::GetCameraLightInsertPosition() const @@ -1619,7 +1619,7 @@ void lcView::DrawGrid() { const lcModel* PreviewModel = GetActiveModel(); - for (const lcInsertPieceInfo& PreviewInsertPieceInfo : PreviewModel->GetPreviewInsertPieceInfo()) + for (const lcPreviewInsertPieceInfo& PreviewInsertPieceInfo : PreviewModel->GetPreviewInsertPieceInfo()) { lcVector3 Points[8]; lcGetBoxCorners(PreviewInsertPieceInfo.PieceData->GetBoundingBox(), Points); diff --git a/common/lc_view.h b/common/lc_view.h index 4c124b72..938e28c8 100644 --- a/common/lc_view.h +++ b/common/lc_view.h @@ -1,5 +1,6 @@ #pragma once +#include "lc_assettypes.h" #include "lc_context.h" #include "lc_math.h" #include "lc_commands.h" @@ -288,7 +289,7 @@ public: float GetOverlayScale() const; lcVector3 GetMoveDirection(const lcVector3& Direction) const; void UpdatePiecePreview(); - std::pair, bool> GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, lcPieceData* PieceData, lcPiece* MovingPiece) const; + std::pair, bool> GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, const lcPieceDataRef& PieceData, lcPiece* MovingPiece) const; lcVector3 GetCameraLightInsertPosition() const; void GetRayUnderPointer(lcVector3& Start, lcVector3& End) const; lcObjectRayTest RayTest(bool PiecesOnly, bool IgnoreSelected) const; diff --git a/common/minifig.cpp b/common/minifig.cpp index 489d5340..ea531084 100644 --- a/common/minifig.cpp +++ b/common/minifig.cpp @@ -48,7 +48,6 @@ MinifigWizard::MinifigWizard() for (int PartIndex = 0; PartIndex < LC_MFW_NUMITEMS; PartIndex++) { - mMinifig.Parts[PartIndex] = nullptr; mMinifig.ColorIndices[PartIndex] = gDefaultColor; mMinifig.Angles[PartIndex] = 0.0f; mMinifig.Matrices[PartIndex] = lcMatrix44Identity(); @@ -57,12 +56,6 @@ MinifigWizard::MinifigWizard() MinifigWizard::~MinifigWizard() { - lcPiecesLibrary* Library = lcGetPiecesLibrary(); - - for (int i = 0; i < LC_MFW_NUMITEMS; i++) - if (mMinifig.Parts[i]) - Library->ReleasePieceData(mMinifig.Parts[i]); // todo: don't call ReleasePieceData here because it may release textures and they need a GL context current - SaveTemplates(); } @@ -152,7 +145,7 @@ void MinifigWizard::ParseSettings(lcFile& Settings) *NameEnd = 0; NameEnd++; - lcPieceData* PieceData = lcGetPiecesLibrary()->FindPieceData(NameStart, nullptr, false, false); + lcPieceData* PieceData = lcGetPiecesLibrary()->FindPieceData(NameStart, nullptr); if (!PieceData && *NameStart) continue; @@ -207,24 +200,19 @@ void MinifigWizard::LoadTemplate(const QString& TemplateName) mMinifig.Angles[PartIndex] = Template.Angles[PartIndex]; mMinifig.Matrices[PartIndex] = lcMatrix44Identity(); - if (mMinifig.Parts[PartIndex]) - Library->ReleasePieceData(mMinifig.Parts[PartIndex]); + mMinifig.PieceData[PartIndex] = Library->AcquirePieceData(Template.Parts[PartIndex].toLatin1(), nullptr, false, false); - lcPieceData* PieceData = Library->FindPieceData(Template.Parts[PartIndex].toLatin1(), nullptr, false, false); - - mMinifig.Parts[PartIndex] = PieceData; - - if (PieceData) - Library->RequestPieceDataLoad(PieceData, lcLoadPriority::Normal); + if (mMinifig.PieceData[PartIndex]) + Library->RequestPieceDataLoad(mMinifig.PieceData[PartIndex].Get(), lcLoadPriority::Normal); } - std::vector Required; + std::vector RequiredPieceData; - for (lcPieceData* PieceData : mMinifig.Parts) + for (const lcPieceDataRef& PieceData : mMinifig.PieceData) if (PieceData) - Required.push_back(PieceData); + RequiredPieceData.push_back(PieceData); - Library->WaitForPieceData(Required); + Library->WaitForPieceData(RequiredPieceData); Calculate(); } @@ -326,26 +314,26 @@ void MinifigWizard::Calculate() float HeadOffset = 0.0f; lcMatrix44 Root, Mat, Mat2; - lcPieceData** Parts = mMinifig.Parts; + const lcPieceDataRef* PieceData = mMinifig.PieceData; const float* Angles = mMinifig.Angles; lcMatrix44* Matrices = mMinifig.Matrices; - const bool DroidTorso = Parts[LC_MFW_BODY] && !qstricmp(Parts[LC_MFW_BODY]->mFileName, "30375.dat"); - const bool SkeletonTorso = Parts[LC_MFW_BODY] && !qstricmp(Parts[LC_MFW_BODY]->mFileName, "6260.dat"); + const bool DroidTorso = PieceData[LC_MFW_BODY] && !qstricmp(PieceData[LC_MFW_BODY]->mFileName, "30375.dat"); + const bool SkeletonTorso = PieceData[LC_MFW_BODY] && !qstricmp(PieceData[LC_MFW_BODY]->mFileName, "6260.dat"); - if (Parts[LC_MFW_BODY3]) + if (PieceData[LC_MFW_BODY3]) Root = lcMatrix44Translation(lcVector3(0, 0, 74.0f)); else Root = lcMatrix44Translation(lcVector3(0, 0, 72.0f)); Matrices[LC_MFW_BODY] = lcMul(mSettings[LC_MFW_BODY][GetSelectionIndex(LC_MFW_BODY)].Offset, Root); - if (Parts[LC_MFW_NECK]) + if (PieceData[LC_MFW_NECK]) { Matrices[LC_MFW_NECK] = lcMul(mSettings[LC_MFW_NECK][GetSelectionIndex(LC_MFW_NECK)].Offset, Root); HeadOffset = 0.08f; } - if (Parts[LC_MFW_HEAD]) + if (PieceData[LC_MFW_HEAD]) { Mat = lcMatrix44RotationZ(-LC_DTOR * Angles[LC_MFW_HEAD]); Mat.SetTranslation(lcVector3(0.0f, 0.0f, 24.0f + HeadOffset)); @@ -353,21 +341,21 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_HEAD] = lcMul(Mat, Root); } - if (Parts[LC_MFW_HATS]) + if (PieceData[LC_MFW_HATS]) { Mat = lcMatrix44RotationZ(-LC_DTOR * Angles[LC_MFW_HATS]); Mat = lcMul(mSettings[LC_MFW_HATS][GetSelectionIndex(LC_MFW_HATS)].Offset, Mat); Matrices[LC_MFW_HATS] = lcMul(Mat, Matrices[LC_MFW_HEAD]); } - if (Parts[LC_MFW_HATS2]) + if (PieceData[LC_MFW_HATS2]) { Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_HATS2]); Mat = lcMul(mSettings[LC_MFW_HATS2][GetSelectionIndex(LC_MFW_HATS2)].Offset, Mat); Matrices[LC_MFW_HATS2] = lcMul(Mat, Matrices[LC_MFW_HATS]); } - if (Parts[LC_MFW_RARM]) + if (PieceData[LC_MFW_RARM]) { Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_RARM]); @@ -382,7 +370,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_RARM] = lcMul(Mat, Root); } - if (Parts[LC_MFW_RHAND]) + if (PieceData[LC_MFW_RHAND]) { Mat = lcMatrix44RotationY(-LC_DTOR * Angles[LC_MFW_RHAND]); Mat2 = lcMatrix44RotationX(LC_DTOR * 45); @@ -392,7 +380,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_RHAND] = lcMul(Mat, Matrices[LC_MFW_RARM]); } - if (Parts[LC_MFW_RHANDA]) + if (PieceData[LC_MFW_RHANDA]) { Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_RHANDA]); Mat.SetTranslation(lcVector3(0, -9.25f, 0)); @@ -401,7 +389,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_RHANDA] = lcMul(Mat, Matrices[LC_MFW_RHAND]); } - if (Parts[LC_MFW_LARM]) + if (PieceData[LC_MFW_LARM]) { Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_LARM]); @@ -416,7 +404,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_LARM] = lcMul(Mat, Root); } - if (Parts[LC_MFW_LHAND]) + if (PieceData[LC_MFW_LHAND]) { Mat = lcMatrix44RotationY(-LC_DTOR * Angles[LC_MFW_LHAND]); Mat2 = lcMatrix44RotationX(LC_DTOR * 45); @@ -426,7 +414,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_LHAND] = lcMul(Mat, Matrices[LC_MFW_LARM]); } - if (Parts[LC_MFW_LHANDA]) + if (PieceData[LC_MFW_LHANDA]) { Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_LHANDA]); Mat.SetTranslation(lcVector3(0, -9.25f, 0)); @@ -435,7 +423,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_LHANDA] = lcMul(Mat, Matrices[LC_MFW_LHAND]); } - if (Parts[LC_MFW_BODY2]) + if (PieceData[LC_MFW_BODY2]) { Mat = lcMatrix44Identity(); Mat.SetTranslation(lcVector3(0, 0, -32.0f)); @@ -443,7 +431,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_BODY2] = lcMul(Mat, Root); } - if (Parts[LC_MFW_BODY3]) + if (PieceData[LC_MFW_BODY3]) { Mat = lcMatrix44Identity(); Mat.SetTranslation(lcVector3(0, 0, -32.0f)); @@ -451,7 +439,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_BODY3] = lcMul(Mat, Root); } - if (Parts[LC_MFW_RLEG]) + if (PieceData[LC_MFW_RLEG]) { Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_RLEG]); Mat.SetTranslation(lcVector3(0, 0, -44.0f)); @@ -459,7 +447,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_RLEG] = lcMul(Mat, Root); } - if (Parts[LC_MFW_RLEGA]) + if (PieceData[LC_MFW_RLEGA]) { const lcVector3 Center(-10.0f, -1.0f, -28.0f); Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_RLEGA]); @@ -470,7 +458,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_RLEGA] = lcMul(Mat, Matrices[LC_MFW_RLEG]); } - if (Parts[LC_MFW_LLEG]) + if (PieceData[LC_MFW_LLEG]) { Mat = lcMatrix44RotationX(-LC_DTOR * Angles[LC_MFW_LLEG]); Mat.SetTranslation(lcVector3(0, 0, -44.0f)); @@ -478,7 +466,7 @@ void MinifigWizard::Calculate() Matrices[LC_MFW_LLEG] = lcMul(Mat, Root); } - if (Parts[LC_MFW_LLEGA]) + if (PieceData[LC_MFW_LLEGA]) { const lcVector3 Center(10.0f, -1.0f, -28.0f); Mat = lcMatrix44RotationZ(LC_DTOR * Angles[LC_MFW_LLEGA]); @@ -497,7 +485,7 @@ int MinifigWizard::GetSelectionIndex(int Type) const const std::vector& InfoArray = mSettings[Type]; for (size_t Index = 0; Index < InfoArray.size(); Index++) - if (InfoArray[Index].PieceData == mMinifig.Parts[Type]) + if (InfoArray[Index].PieceData == mMinifig.PieceData[Type]) return (int)Index; return 0; @@ -505,15 +493,10 @@ int MinifigWizard::GetSelectionIndex(int Type) const void MinifigWizard::SetPieceData(int Type, lcPieceData* PieceData) { - lcPiecesLibrary* Library = lcGetPiecesLibrary(); + mMinifig.PieceData[Type] = lcPieceDataRef(PieceData); - if (mMinifig.Parts[Type]) - Library->ReleasePieceData(mMinifig.Parts[Type]); - - mMinifig.Parts[Type] = PieceData; - - if (mMinifig.Parts[Type]) - Library->RequestPieceDataLoad(mMinifig.Parts[Type], lcLoadPriority::Blocking); + if (mMinifig.PieceData[Type]) + lcGetPiecesLibrary()->WaitForPieceData(mMinifig.PieceData[Type]); Calculate(); } diff --git a/common/minifig.h b/common/minifig.h index 8a2608a8..a83b6668 100644 --- a/common/minifig.h +++ b/common/minifig.h @@ -1,6 +1,7 @@ #pragma once #include "lc_math.h" +#include "lc_assettypes.h" enum LC_MFW_TYPES { @@ -33,7 +34,7 @@ struct lcMinifigPieceInfo struct lcMinifig { - lcPieceData* Parts[LC_MFW_NUMITEMS]; + lcPieceDataRef PieceData[LC_MFW_NUMITEMS]; int ColorIndices[LC_MFW_NUMITEMS]; float Angles[LC_MFW_NUMITEMS]; lcMatrix44 Matrices[LC_MFW_NUMITEMS]; diff --git a/common/piece.cpp b/common/piece.cpp index fb2d72dc..4135f336 100644 --- a/common/piece.cpp +++ b/common/piece.cpp @@ -26,9 +26,14 @@ lcPiece::lcPiece() } lcPiece::lcPiece(lcPieceData* PieceData) + : lcPiece(lcPieceDataRef(PieceData)) +{ +} + +lcPiece::lcPiece(lcPieceDataRef PieceDataRef) : lcObject(lcObjectType::Piece) { - SetPieceData(PieceData, QString(), true, true); + SetPieceData(std::move(PieceDataRef), QString(), true, true); mColorIndex = gDefaultColor; mColorCode = 16; mPivotMatrix = lcMatrix44Identity(); @@ -63,30 +68,36 @@ lcPiece::~lcPiece() if (mPieceData) { lcPiecesLibrary* Library = lcGetPiecesLibrary(); - Library->ReleasePieceData(mPieceData); + mPieceData.Reset(); Library->NotifyAssetStatusChanged(); } delete mMesh; } -void lcPiece::SetPieceData(lcPieceData* PieceData, const QString& ID, bool Wait, bool UpdateSynthInfo) +void lcPiece::SetPieceData(lcPieceDataRef PieceDataRef, const QString& ID, bool Wait, bool UpdateSynthInfo) { lcPiecesLibrary* Library = lcGetPiecesLibrary(); - lcPieceData* PreviousPieceData = mPieceData; + const bool DataChanged = mPieceData != PieceDataRef; - if (PreviousPieceData != PieceData || UpdateSynthInfo) + if (DataChanged || UpdateSynthInfo) Library->CancelSynthMesh(this); - if (PreviousPieceData != PieceData) + if (DataChanged) { delete mMesh; mMesh = nullptr; } - mPieceData = PieceData; + lcPieceDataRef PreviousData(std::move(mPieceData)); + mPieceData = std::move(PieceDataRef); if (mPieceData) - Library->RequestPieceDataLoad(mPieceData, Wait ? lcLoadPriority::Blocking : lcLoadPriority::Normal); + { + if (Wait) + Library->WaitForPieceData(mPieceData); + else + Library->RequestPieceDataLoad(mPieceData.Get(), lcLoadPriority::Normal); + } if (!ID.isEmpty()) mID = ID; @@ -106,9 +117,11 @@ void lcPiece::SetPieceData(lcPieceData* PieceData, const QString& ID, bool Wait, UpdateMesh(); } - else if (PreviousPieceData && PreviousPieceData != PieceData) + else if (PreviousData && DataChanged) UpdateMesh(); + PreviousData.Reset(); + Library->NotifyAssetStatusChanged(); } @@ -117,9 +130,7 @@ bool lcPiece::SetPieceId(lcPieceData* PieceData) if (mPieceData == PieceData) return false; - lcPiecesLibrary* Library = lcGetPiecesLibrary(); - Library->ReleasePieceData(mPieceData); - SetPieceData(PieceData, QString(), true, true); + SetPieceData(lcPieceDataRef(PieceData), QString(), true, true); return true; } @@ -333,8 +344,7 @@ bool lcPiece::FileLoad(lcFile& file, Project* Project) file.ReadBuffer(name, LC_PIECE_NAME_LEN); lcstrcat(name, ".dat"); - lcPieceData* PieceData = lcGetPiecesLibrary()->FindPieceData(name, Project, true, false); - SetPieceData(PieceData, QString(), true, true); + SetPieceData(lcGetPiecesLibrary()->AcquirePieceData(name, Project, true, false), QString(), true, true); // 11 (0.77) if (version < 11) @@ -532,7 +542,7 @@ bool lcPiece::MinIntersectDist(const lcVector3& Start, const lcVector3& End, flo if (!Mesh->MinIntersectDist(Start, End, MinDistance, PieceRayTest.Plane)) return false; - PieceRayTest.PieceData = mPieceData; + PieceRayTest.PieceData = mPieceData.Get(); PieceRayTest.Transform = lcMatrix44Identity(); return true; @@ -742,7 +752,7 @@ QVariant lcPiece::GetPropertyValue(lcObjectPropertyId PropertyId) const switch (PropertyId) { case lcObjectPropertyId::PieceId: - return QVariant::fromValue(mPieceData); + return QVariant::fromValue(mPieceData.Get()); case lcObjectPropertyId::PieceColor: return GetColorIndex(); @@ -1033,16 +1043,13 @@ void lcPiece::SetHistoryState(const lcPieceHistoryState& State, const lcModel* M mPosition = State.Position; mRotation = State.Rotation; - lcPieceData* PieceData = lcGetPiecesLibrary()->FindPieceData(mID.toLatin1(), Model->GetProject(), true, true); + lcPieceDataRef RestoredData = lcGetPiecesLibrary()->AcquirePieceData(mID.toLatin1(), Model->GetProject(), true, true); - if (mPieceData != PieceData) + if (mPieceData != RestoredData) { const bool NewPiece = !mPieceData; - if (mPieceData) - lcGetPiecesLibrary()->ReleasePieceData(mPieceData); - - SetPieceData(PieceData, mID, true, false); + SetPieceData(std::move(RestoredData), mID, true, false); if (NewPiece) UpdateMesh(); @@ -1511,7 +1518,7 @@ void lcPiece::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex if (!mMesh || mPieceData->mState != lcAssetState::Loaded) mPieceData->GetModelParts(lcMul(mModelWorld, WorldMatrix), ColorIndex, ModelParts); else - ModelParts.emplace_back(lcModelPartsEntry{ lcMul(mModelWorld, WorldMatrix), mPieceData, mMesh, ColorIndex }); + ModelParts.emplace_back(lcModelPartsEntry{ lcMul(mModelWorld, WorldMatrix), mPieceData.Get(), mMesh, ColorIndex }); } const lcBoundingBox& lcPiece::GetBoundingBox() const diff --git a/common/piece.h b/common/piece.h index 9fd28731..a548fe5d 100644 --- a/common/piece.h +++ b/common/piece.h @@ -5,6 +5,7 @@ class lcPieceData; #include "object.h" #include "lc_colors.h" #include "lc_math.h" +#include "lc_assettypes.h" #define LC_MAX_CONTROL_POINTS 1000 @@ -60,6 +61,7 @@ class lcPiece : public lcObject public: lcPiece(); lcPiece(lcPieceData* PieceData); + lcPiece(lcPieceDataRef PieceDataRef); lcPiece(const lcPiece& Other); virtual ~lcPiece(); @@ -161,7 +163,7 @@ public: void Initialize(const lcMatrix44& WorldMatrix, lcStep Step); const lcBoundingBox& GetBoundingBox() const; void CompareBoundingBox(lcVector3& Min, lcVector3& Max) const; - void SetPieceData(lcPieceData* PieceData, const QString& ID, bool Wait, bool UpdateSynthInfo); + void SetPieceData(lcPieceDataRef PieceDataRef, const QString& ID, bool Wait, bool UpdateSynthInfo); bool SetPieceId(lcPieceData* PieceData); bool FileLoad(lcFile& file, Project* Project); @@ -278,7 +280,7 @@ public: } public: - lcPieceData* mPieceData = nullptr; + lcPieceDataRef mPieceData; lcMatrix44 mModelWorld; lcMatrix44 mPivotMatrix = lcMatrix44Identity(); diff --git a/common/project.cpp b/common/project.cpp index d6e0f337..dfc2acae 100644 --- a/common/project.cpp +++ b/common/project.cpp @@ -1043,7 +1043,7 @@ std::vector Project::GetLoadWarningDetails() const for (const std::unique_ptr& Model : Current->mModels) for (const std::unique_ptr& Piece : Model->GetPieces()) { - const lcPieceData* PieceData = Piece->mPieceData; + const lcPieceData* PieceData = Piece->mPieceData.Get(); if (!PieceData || !PieceData->IsProject()) continue; @@ -1259,7 +1259,7 @@ lcResult Project::Save(const QString& FileName) if (DirectoryChanged) { lcPiecesLibrary* Library = lcGetPiecesLibrary(); - std::vector LocalPieces; + std::vector LocalPieceData; std::vector ChangedPieces; for (const std::map::value_type& Entry : mPieceIndex) @@ -1269,13 +1269,12 @@ lcResult Project::Save(const QString& FileName) if (PieceData->IsModel()) continue; - Library->AddPieceDataReference(PieceData); - LocalPieces.push_back(PieceData); + LocalPieceData.push_back(lcPieceDataRef(PieceData)); } - for (lcPieceData* PieceData : LocalPieces) - if (Library->RemapProjectPieceData(PieceData, DestinationDirectory, mIsPreview)) - ChangedPieces.push_back(PieceData); + for (const lcPieceDataRef& LocalData : LocalPieceData) + if (Library->RemapProjectPieceData(LocalData.Get(), DestinationDirectory, mIsPreview)) + ChangedPieces.push_back(LocalData.Get()); for (const std::unique_ptr& Model : mModels) { @@ -1304,8 +1303,6 @@ lcResult Project::Save(const QString& FileName) gMainWindow->GetPreviewWidget()->RefreshAfterAssetChange(ChangedPieces); } - for (lcPieceData* PieceData : LocalPieces) - Library->ReleasePieceData(PieceData); } for (const std::unique_ptr& Model : mModels) @@ -1543,7 +1540,7 @@ lcResult Project::MergeInternal(const std::vector& Sources, cons for (const std::unique_ptr& Piece : Model->GetPieces()) { if (Piece->mPieceData && !Piece->mPieceData->IsModel()) - NonModelPieces.emplace(lcPiecesLibrary::NormalizePieceDataName(Piece->GetID().toLatin1().constData()), Piece->mPieceData); + NonModelPieces.emplace(lcPiecesLibrary::NormalizePieceDataName(Piece->GetID().toLatin1().constData()), Piece->mPieceData.Get()); } } @@ -1587,7 +1584,7 @@ lcResult Project::MergeInternal(const std::vector& Sources, cons { const std::string Name = lcPiecesLibrary::NormalizePieceDataName(Piece->GetID().toLatin1().constData()); - if (!RegisterSourcePiece(Name, Piece->mPieceData)) + if (!RegisterSourcePiece(Name, Piece->mPieceData.Get())) return lcUnexpected(tr("Both projects contain different pieces named '%1'. Rename one before merging.").arg(ConflictName)); } } @@ -1608,7 +1605,7 @@ lcResult Project::MergeInternal(const std::vector& Sources, cons const bool ReservedOriginal = FinalName != OriginalName && OriginalModelNames.find(Name) != OriginalModelNames.end(); const bool Duplicate = DestinationModelNames.find(Name) != DestinationModelNames.end() || NonModelPieces.find(Name) != NonModelPieces.end() || PlannedModelNames.find(Name) != PlannedModelNames.end() || ReservedOriginal || - Library->FindPieceData(FinalName.toLatin1().constData(), nullptr, false, false) != nullptr; + Library->FindPieceData(FinalName.toLatin1().constData(), nullptr) != nullptr; if (!Duplicate) { @@ -1657,7 +1654,7 @@ lcResult Project::MergeInternal(const std::vector& Sources, cons { for (const std::unique_ptr& Piece : Model->GetPieces()) { - lcPieceData* Incoming = Piece->mPieceData; + lcPieceData* Incoming = Piece->mPieceData.Get(); if (!Incoming || Incoming->IsModel()) continue; @@ -1669,8 +1666,7 @@ lcResult Project::MergeInternal(const std::vector& Sources, cons continue; const QString PieceId = Piece->GetID(); - Piece->SetPieceData(Existing, PieceId, false, false); - Library->ReleasePieceData(Incoming); + Piece->SetPieceData(lcPieceDataRef(Existing), PieceId, false, false); } } @@ -1810,20 +1806,20 @@ std::vector Project::GetModelParts() return ModelParts; } -std::vector Project::GetRequiredPieceData() const +std::vector Project::GetRequiredPieceData() const { - std::vector Required; + std::vector RequiredPieceData; for (const std::unique_ptr& Model : mModels) { - std::vector ModelRequired = Model->GetRequiredPieceData(); - Required.insert(Required.end(), ModelRequired.begin(), ModelRequired.end()); + std::vector ModelPieceData = Model->GetRequiredPieceData(); + RequiredPieceData.insert(RequiredPieceData.end(), ModelPieceData.begin(), ModelPieceData.end()); } - std::sort(Required.begin(), Required.end()); - Required.erase(std::unique(Required.begin(), Required.end()), Required.end()); + std::sort(RequiredPieceData.begin(), RequiredPieceData.end()); + RequiredPieceData.erase(std::unique(RequiredPieceData.begin(), RequiredPieceData.end()), RequiredPieceData.end()); - return Required; + return RequiredPieceData; } std::vector Project::GetRequiredSynthPieces() const @@ -1885,7 +1881,7 @@ lcResult Project::SerializeModel(const lcModel* Root, bool SelectedO if ((FilterSelection && !Piece->IsSelected()) || (LastStep && Piece->GetStepShow() > LastStep)) continue; - const lcPieceData* PieceData = Piece->mPieceData; + const lcPieceData* PieceData = Piece->mPieceData.Get(); const lcModel* Child = nullptr; if (PieceData && PieceData->IsModel()) @@ -3749,7 +3745,7 @@ lcResult Project::ExportPOVRay(const QString& FileName) lcstrcat(Src, ".dat"); - lcPieceData* PieceData = Library->FindPieceData(Src, nullptr, false, false); + lcPieceData* PieceData = Library->FindPieceData(Src, nullptr); if (!PieceData) continue; diff --git a/common/project.h b/common/project.h index 01b71fc3..70533cf1 100644 --- a/common/project.h +++ b/common/project.h @@ -1,6 +1,7 @@ #pragma once #include "lc_application.h" +#include "lc_assettypes.h" #include "lc_result.h" #define LC_HTML_SINGLEPAGE 0x01 @@ -144,7 +145,7 @@ public: bool ImportInventory(const std::vector& SetInventory, const QString& Name, const QString& Description); lcResult SaveImage(const lcImageDialogOptions& Options); - std::vector GetRequiredPieceData() const; + std::vector GetRequiredPieceData() const; std::vector GetRequiredSynthPieces() const; lcResult EnsureAssetsReady() const; lcResult ExportCurrentStep(const QString& FileName); diff --git a/docs/asset-loading.md b/docs/asset-loading.md index 5d28db96..7381a2b0 100644 --- a/docs/asset-loading.md +++ b/docs/asset-loading.md @@ -153,11 +153,16 @@ clearing stale details when none are supplied. Decoding and upload failures use the same setter. It retains decoded pixels for bounded upload retries; terminal request handling explicitly discards them when required. -`FindPieceData()` returns a borrowed identity, without acquiring a reference. Lookup -checks the supplied project's name index, then the shared library catalog, then -optionally an external file beside the project. Creating a missing identity -records `Failed`. `AcquireModelPieceData()` differs: it returns one acquired reference -for the model to release. Library membership is independent of readiness and +`FindPieceData(Name, Project)` returns a borrowed registered identity from +the supplied project's name index or the shared library catalog. It does no file +I/O, creates no identities, and acquires no reference. +`AcquirePieceData(Name, Project, CreateMissing, SearchProjectFolder)` resolves +or creates an identity and returns one scoped `lcPieceDataRef`, without ensuring mesh +readiness. It may load an external project beside the current project; creating a +missing identity records `Failed`. `lcPieceDataRef(Piece)` retains an +existing borrowed identity, and accepts null. `AcquireModelPieceData()` returns +one scoped reference for the model to adopt using `Release()`. +Library membership is independent of readiness and Part/Model/Project type. Project-local names cannot replace another project's names. Worker include resolution uses library sources, while model geometry and texture search directories are snapshotted before dispatch. @@ -328,8 +333,8 @@ sequenceDiagram participant Library as Library participant Loader as Loader / UI thread participant Worker as Qt worker + Caller->>Library: AcquirePieceData(...) returns scoped consumer reference Caller->>Library: RequestPieceDataLoad(PieceData, LoadPriority) - Library->>Library: Acquire consumer reference Library->>Loader: Queue/promote Loader->>Worker: Immutable source snapshot Worker->>Worker: Parse/generate and convert, or read cached mesh @@ -379,13 +384,44 @@ buffer upload is not part of asset readiness. ## References, cancellation, and generated geometry -`RequestPieceDataLoad()` acquires one reference even if loading fails. Always pair it -with `ReleasePieceData()`. Its asynchronous return value means the call did not -wait, not that the asset loaded. `AddPieceDataReference()` retains an identity without -starting work. `WaitForPieceData()` deduplicates its input without acquiring consumer -references; callers retain every required identity and its owner through the wait. +`lcPieceDataRef` and `lcTextureRef` alias the shared `lcAssetRef` template in +`lc_assettypes.h`. Both are copyable scoped references. +Construction from a borrowed pointer and copying acquire one reference; moving +transfers the reference without retaining it. Assignment retains the new identity +before releasing the old one. Destruction or `Reset()` releases one reference +through its owning library; `Get()` borrows and `Release()` transfers ownership +to an owner responsible for explicitly releasing it. The library has a private +adoption path for references already acquired during model creation. +Use and destroy handles on the UI thread before the library unloads. Empty handles +are safe. `==` and `!=` compare pointer identity between handles or with a +borrowed pointer in either order, including null, without changing reference counts. +Use `Get()` when passing a borrowed pointer to an API. -A part request owns an additional in-flight reference. Removing one instance +`lcPiece::SetPieceData()` accepts an `lcPieceDataRef` by value, adopts that +reference, and releases the previous identity. Passing an lvalue copies and retains; +passing a moved handle transfers the reference. Its raw-pointer constructor is a +borrowing convenience that acquires its own reference. Placed pieces and models +store their definition handles directly, as do the current selection, minifig +parts, and thumbnail dependencies. Stored insertion previews use owning +`lcPreviewInsertPieceInfo` entries, while transient `lcInsertPieceInfo` queries +remain borrowed. Project-remapping owners also retain scoped references. +`AddPieceDataReference()` is the internal retain primitive used by the scoped reference. + +`RequestPieceDataLoad()` starts or promotes work without acquiring a +consumer reference, and returns immediately for every priority, including Blocking. +Repeated requests add no consumer references. Callers retain the identity while +they need it. Use `WaitForPieceData()` or `EnsurePieceDataAssetsReady()` +for synchronous loading. Both wait APIs take handles by const reference without acquiring consumer +references. Callers retain every required identity and its owner throughout the +wait. `lcModel::GetRequiredPieceData()` and its project counterpart return +owning handle vectors, deduplicated by identity; releasing a result releases its +snapshot references. + +A part request owns an additional in-flight `lcPieceDataRef`; synth notifications also +own a scoped definition reference. Request handles are explicitly reset at the +UI-thread completion, cancellation, and notification boundaries, before shared +request storage can be destroyed on a worker thread. Moves preserve the single +loader hold without introducing additional consumer counts. Removing one instance cannot destroy an asset still used by another instance or pending work. On the last consumer release, queued work is removed; running ordinary part work is allowed to finish and its result is discarded if no consumer survives. Successful @@ -438,10 +474,10 @@ Priorities only promote: `Low < Normal < Blocking`. Ordinary thumbnail requests start Low; asynchronous model pieces and generated meshes start Normal, regardless of step. Synchronous waits promote their required requests and pending texture dependencies to Blocking. The same `lcLoadPriority` enum -is used at the loading entry point and throughout the scheduler. The library's `RequestPieceDataLoad()` -waits only for Blocking; Low and Normal return immediately. The loader's entry point -only queues or promotes requests; the library prepares readiness and starts unloaded -requirements before delegating waits. Internal queueing +is used at the loading entry point and throughout the scheduler. `RequestPieceDataLoad()` +only starts or promotes work and never waits, even with Blocking priority. Call +`WaitForPieceData()` or `EnsurePieceDataAssetsReady()` for synchronous loading. +The library prepares readiness and starts unloaded requirements before delegating waits. Internal queueing uses priority solely to rank work and does not wait. Shared textures retain the highest promotion received from any parent or direct wait. There is no consumer identity map, priority demotion, or step-based scheduling. Priority lasts until that request @@ -480,11 +516,12 @@ adds strict failure reporting. During shutdown, the loader's private | Need | API and lifetime contract | | --- | --- | -| Resolve a name | `FindPieceData()` with the intended project and explicit `CreateMissing` / `SearchProjectFolder` arguments. The returned pointer is borrowed; retain it before keeping it beyond its owner's lifetime. | -| Retain and start ordinary asynchronous work | `RequestPieceDataLoad()` with `lcLoadPriority::Low` for background work or `Normal` for model/view work. Pair every acquisition with `ReleasePieceData()`, including failures. | -| Retain and synchronously load one identity | `RequestPieceDataLoad()` with `lcLoadPriority::Blocking`; check its return value and pair the acquisition with `ReleasePieceData()`, including failures. | -| Retain without starting work | `AddPieceDataReference()`, paired with `ReleasePieceData()`. This does not make geometry ready. | -| Wait for an explicit part set | `WaitForPieceData()` (use `{ PieceData }` for one identity), check the boolean for strict consumers. Retain every identity and its owner throughout the wait; this call acquires no consumer references. | +| Find a registered identity | `FindPieceData(Name, Project)` returns a borrowed identity without I/O or creation; retain it before keeping it beyond its owner's lifetime. | +| Resolve or create a piece identity | `AcquirePieceData(Name, Project, CreateMissing, SearchProjectFolder)` returns a scoped reference without ensuring mesh readiness. | +| Start ordinary asynchronous work | Retain the identity, then call `RequestPieceDataLoad()` with `lcLoadPriority::Low` for background work or `Normal` for model/view work. Existing owners need no extra reference for another request. | +| Retain and synchronously load one identity | Keep an `lcPieceDataRef` from `AcquirePieceData()`, then call `WaitForPieceData(PieceDataRef)` and check its return value. | +| Retain without starting work | `lcPieceDataRef(Piece)` returns a scoped reference. This does not make geometry ready. | +| Wait for an explicit part set | `WaitForPieceData(PieceDataRefs)` (use `WaitForPieceData(PieceDataRef)` for one identity), check the boolean for strict consumers. Retain every identity and its owner throughout the wait; this call acquires no consumer references. | | Wait for one identity and its container geometry | `EnsurePieceDataAssetsReady()` settles the identity, then delegates recursive children, synths, and bounds to the model/project API. Retain the identity and its owner throughout. | | Wait for a model, including nested synths | `WaitForAssets()` for permissive output; `EnsureAssetsReady()` for strict preview-style failure reporting. Keep the model and its pieces alive throughout. | | Replace/wait for generated geometry | `RequestSynthMesh()` / `WaitForSynthMeshes()`. Supply live `lcPiece` instances; a shared part mesh or a retained previous generated mesh is not readiness for the replacement. | diff --git a/leocad.pro b/leocad.pro index 7f01ad40..46c08f15 100644 --- a/leocad.pro +++ b/leocad.pro @@ -185,6 +185,7 @@ SOURCES += \ common/lc_application.cpp \ common/lc_arraydialog.cpp \ common/lc_assetloader.cpp \ + common/lc_assettypes.cpp \ common/lc_blenderpreferences.cpp \ common/lc_bricklink.cpp \ common/lc_category.cpp \