mirror of
https://github.com/leozide/leocad.git
synced 2026-10-26 22:10:32 +00:00
Unify asset loading priorities and clarify wait APIs
Replace load flags and the loader's separate priority enum with Low, Normal, and Blocking priorities. Blocking loads wait for completion; asynchronous loads and internal scheduling retain promotion-only behavior. Acquire consumer references in the library, reuse loader loading for stud-style rebuilds, and remove QueuePiece, single-piece readiness wrappers, and the library texture readiness wrapper. Rename scoped readiness APIs to WaitFor functions and document their scheduling and lifetime contracts. Validated with a Debug build and a temporary lifecycle harness covering priority promotion, blocking loads, reference cleanup, selective style reloads, cancellation, pause/resume, and shutdown.
This commit is contained in:
1 parent
2051eeed42
commit
3ec8b274b1
12 files changed
+117
-133
No files matched your search
+22
-36
@@ -26,36 +26,27 @@ lcAssetLoader::~lcAssetLoader()
|
||||
CancelAndDrain();
|
||||
}
|
||||
|
||||
bool lcAssetLoader::LoadPieceInfo(PieceInfo* Info, lcPieceLoadFlags Flags)
|
||||
bool lcAssetLoader::LoadPieceInfo(PieceInfo* Info, lcPieceLoadPriority LoadPriority)
|
||||
{
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
mLibrary->AddPieceReference(Info);
|
||||
|
||||
if (Info->IsProject() || (Info->IsModel() && !Info->GetModel()->HasDirectGeometry()))
|
||||
Info->mState = lcPieceInfoState::Loaded;
|
||||
else if (Info->mState == lcPieceInfoState::Unloaded)
|
||||
EnqueuePiece(Info, Flags.testFlag(lcPieceLoadFlag::Visible) ? Priority::Visible : Priority::Background);
|
||||
else if (Flags.testFlag(lcPieceLoadFlag::Visible))
|
||||
EnqueuePiece(Info, LoadPriority);
|
||||
else if (LoadPriority != lcPieceLoadPriority::Low)
|
||||
{
|
||||
mQueueMutex.lock();
|
||||
const auto It = mRequests.find(Info);
|
||||
if (It != mRequests.end())
|
||||
PromoteRequestPriorityLocked(It->second, Priority::Visible);
|
||||
PromoteRequestPriorityLocked(It->second, LoadPriority);
|
||||
mQueueMutex.unlock();
|
||||
}
|
||||
|
||||
return !Flags.testFlag(lcPieceLoadFlag::Wait) || WaitForRequest(Info);
|
||||
return LoadPriority != lcPieceLoadPriority::Blocking || WaitForRequest(Info);
|
||||
}
|
||||
|
||||
void lcAssetLoader::QueuePiece(PieceInfo* Info)
|
||||
{
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
EnqueuePiece(Info, Priority::Background);
|
||||
}
|
||||
|
||||
void lcAssetLoader::EnqueuePiece(PieceInfo* Info, Priority LoadPriority)
|
||||
void lcAssetLoader::EnqueuePiece(PieceInfo* Info, lcPieceLoadPriority LoadPriority)
|
||||
{
|
||||
QMutexLocker QueueLock(&mQueueMutex);
|
||||
|
||||
@@ -105,7 +96,7 @@ void lcAssetLoader::EnqueuePiece(PieceInfo* Info, Priority LoadPriority)
|
||||
emit mLibrary->AssetRequestsChanged();
|
||||
}
|
||||
|
||||
void lcAssetLoader::PromoteRequestPriorityLocked(const std::shared_ptr<PieceRequest>& RequestedPiece, Priority LoadPriority)
|
||||
void lcAssetLoader::PromoteRequestPriorityLocked(const std::shared_ptr<PieceRequest>& RequestedPiece, lcPieceLoadPriority LoadPriority)
|
||||
{
|
||||
RequestedPiece->LoadPriority = std::max(RequestedPiece->LoadPriority, LoadPriority);
|
||||
|
||||
@@ -140,7 +131,7 @@ void lcAssetLoader::QueueSynthMesh(lcPiece* Piece)
|
||||
RequestedSynth->Piece = Piece;
|
||||
RequestedSynth->SynthInfo = SynthInfo;
|
||||
RequestedSynth->ControlPoints = Piece->GetControlPoints();
|
||||
RequestedSynth->LoadPriority = Priority::Visible;
|
||||
RequestedSynth->LoadPriority = lcPieceLoadPriority::Normal;
|
||||
RequestedSynth->EnqueuedAt = QDateTime::currentMSecsSinceEpoch();
|
||||
RequestedSynth->Generation = mGeneration;
|
||||
|
||||
@@ -159,7 +150,7 @@ void lcAssetLoader::QueueSynthMesh(lcPiece* Piece)
|
||||
emit mLibrary->AssetRequestsChanged();
|
||||
}
|
||||
|
||||
void lcAssetLoader::QueueTexture(lcTexture* Texture, Priority LoadPriority)
|
||||
void lcAssetLoader::QueueTexture(lcTexture* Texture, lcPieceLoadPriority LoadPriority)
|
||||
{
|
||||
QMutexLocker QueueLock(&mQueueMutex);
|
||||
|
||||
@@ -234,7 +225,7 @@ void lcAssetLoader::RunWorker()
|
||||
const auto Score = [Now](const auto& Item)
|
||||
{
|
||||
// Each ten seconds of waiting promotes one priority level.
|
||||
return qMin<qint64>(static_cast<qint64>(Priority::Blocking),
|
||||
return qMin<qint64>(static_cast<qint64>(lcPieceLoadPriority::Blocking),
|
||||
static_cast<qint64>(Item->LoadPriority) + qMax<qint64>(0, Now - Item->EnqueuedAt) / 10000);
|
||||
};
|
||||
|
||||
@@ -661,7 +652,7 @@ void lcAssetLoader::ResolveMeshColors(lcLibraryMeshData& MeshData, std::vector<b
|
||||
}
|
||||
|
||||
bool lcAssetLoader::ResolveAndQueueMeshTextures(lcMesh& Mesh, const std::vector<QString>& SearchDirectories,
|
||||
const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData, Priority LoadPriority, QString& Error)
|
||||
const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData, lcPieceLoadPriority LoadPriority, QString& Error)
|
||||
{
|
||||
for (lcMeshLod& Lod : Mesh.mLods)
|
||||
for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++)
|
||||
@@ -1056,7 +1047,7 @@ bool lcAssetLoader::WaitForRequest(PieceInfo* Info)
|
||||
{
|
||||
RequestedPiece = It->second;
|
||||
|
||||
PromoteRequestPriorityLocked(RequestedPiece, Priority::Blocking);
|
||||
PromoteRequestPriorityLocked(RequestedPiece, lcPieceLoadPriority::Blocking);
|
||||
}
|
||||
StartWorkersLocked();
|
||||
}
|
||||
@@ -1088,16 +1079,11 @@ bool lcAssetLoader::WaitForRequest(PieceInfo* Info)
|
||||
return RequestedPiece->Succeeded;
|
||||
}
|
||||
|
||||
bool lcAssetLoader::EnsurePieceReady(PieceInfo* Info)
|
||||
{
|
||||
return EnsurePiecesReady({ Info });
|
||||
}
|
||||
|
||||
bool lcAssetLoader::EnsurePiecesReady(const std::vector<PieceInfo*>& Parts)
|
||||
bool lcAssetLoader::WaitForPieces(const std::vector<PieceInfo*>& Pieces)
|
||||
{
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
std::vector<PieceInfo*> Required = Parts;
|
||||
std::vector<PieceInfo*> Required = Pieces;
|
||||
std::sort(Required.begin(), Required.end());
|
||||
|
||||
Required.erase(std::unique(Required.begin(), Required.end()), Required.end());
|
||||
@@ -1113,7 +1099,7 @@ bool lcAssetLoader::EnsurePiecesReady(const std::vector<PieceInfo*>& Parts)
|
||||
if (Info->IsProject() || (Info->IsModel() && !Info->GetModel()->HasDirectGeometry()))
|
||||
Info->mState = lcPieceInfoState::Loaded;
|
||||
else
|
||||
EnqueuePiece(Info, Priority::Blocking);
|
||||
EnqueuePiece(Info, lcPieceLoadPriority::Blocking);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1126,7 +1112,7 @@ bool lcAssetLoader::EnsurePiecesReady(const std::vector<PieceInfo*>& Parts)
|
||||
if (It == mRequests.end())
|
||||
continue;
|
||||
|
||||
PromoteRequestPriorityLocked(It->second, Priority::Blocking);
|
||||
PromoteRequestPriorityLocked(It->second, lcPieceLoadPriority::Blocking);
|
||||
}
|
||||
|
||||
StartWorkersLocked();
|
||||
@@ -1143,7 +1129,7 @@ bool lcAssetLoader::EnsurePiecesReady(const std::vector<PieceInfo*>& Parts)
|
||||
return Ready;
|
||||
}
|
||||
|
||||
bool lcAssetLoader::EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces)
|
||||
bool lcAssetLoader::WaitForSynthMeshes(const std::vector<lcPiece*>& Pieces)
|
||||
{
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
@@ -1160,7 +1146,7 @@ bool lcAssetLoader::EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces)
|
||||
continue;
|
||||
|
||||
const std::shared_ptr<SynthRequest>& RequestedSynth = It->second;
|
||||
RequestedSynth->LoadPriority = Priority::Blocking;
|
||||
RequestedSynth->LoadPriority = lcPieceLoadPriority::Blocking;
|
||||
Required.push_back(RequestedSynth);
|
||||
|
||||
if (RequestedSynth->StagedMesh)
|
||||
@@ -1170,7 +1156,7 @@ bool lcAssetLoader::EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces)
|
||||
const TextureRequestMap::iterator TextureIt = mTextureRequests.find(Lod.Sections[SectionIdx].Texture);
|
||||
|
||||
if (TextureIt != mTextureRequests.end())
|
||||
TextureIt->second->LoadPriority = Priority::Blocking;
|
||||
TextureIt->second->LoadPriority = lcPieceLoadPriority::Blocking;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1208,14 +1194,14 @@ bool lcAssetLoader::EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lcAssetLoader::EnsureTextureReady(lcTexture* Texture)
|
||||
bool lcAssetLoader::WaitForTexture(lcTexture* Texture)
|
||||
{
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
if (Texture->IsReady())
|
||||
return true;
|
||||
|
||||
QueueTexture(Texture, Priority::Blocking);
|
||||
QueueTexture(Texture, lcPieceLoadPriority::Blocking);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@@ -1295,7 +1281,7 @@ void lcAssetLoader::RebuildModelPiece(PieceInfo* Info)
|
||||
|
||||
Info->mState = lcPieceInfoState::Unloaded;
|
||||
|
||||
EnqueuePiece(Info, Priority::Visible);
|
||||
EnqueuePiece(Info, lcPieceLoadPriority::Normal);
|
||||
}
|
||||
|
||||
void lcAssetLoader::InvalidatePiece(PieceInfo* Info)
|
||||
|
||||
+29
-27
@@ -59,25 +59,35 @@ public:
|
||||
lcAssetLoader(lcPiecesLibrary* Library);
|
||||
~lcAssetLoader() override;
|
||||
|
||||
// Start using a piece: acquire a reference and load it if needed. Release it with ReleasePieceInfo().
|
||||
// Visible raises its loading priority. Wait blocks until loading settles and returns whether it succeeded;
|
||||
// without Wait, this returns true immediately, even if the piece is not ready yet.
|
||||
bool LoadPieceInfo(PieceInfo* Info, lcPieceLoadFlags Flags);
|
||||
|
||||
// Queue a background build for a piece the caller already owns, without acquiring a consumer reference or waiting.
|
||||
// Reuse any existing request, keeping its priority.
|
||||
void QueuePiece(PieceInfo* Info);
|
||||
// Load a piece the library has already acquired for the caller; this adds no consumer reference.
|
||||
// Low and Normal return immediately; true does not mean the piece is ready yet.
|
||||
// Blocking promotes urgent work, waits until loading settles, and returns whether it succeeded.
|
||||
bool LoadPieceInfo(PieceInfo* Info, lcPieceLoadPriority LoadPriority);
|
||||
|
||||
// Build this instance's generated mesh from its current control points in the background.
|
||||
// Cancel any older request and keep the previous mesh visible while the replacement is built.
|
||||
void QueueSynthMesh(lcPiece* Piece);
|
||||
|
||||
bool EnsurePieceReady(PieceInfo* Info);
|
||||
bool EnsurePiecesReady(const std::vector<PieceInfo*>& Pieces);
|
||||
bool EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces);
|
||||
bool EnsureTextureReady(lcTexture* Texture);
|
||||
// Start or promote loading as needed, then wait. Returns true if all pieces are ready.
|
||||
// Waits for these identities only, without traversing container children or generated meshes.
|
||||
bool WaitForPieces(const std::vector<PieceInfo*>& Pieces);
|
||||
|
||||
// Promote and wait for queued generations. Returns true if every required generated mesh is current.
|
||||
// Call QueueSynthMesh() to start a generation; the caller keeps the instances alive through the wait.
|
||||
bool WaitForSynthMeshes(const std::vector<lcPiece*>& Pieces);
|
||||
|
||||
// Start or promote decoding as needed, then wait through upload and retries. The caller retains the texture.
|
||||
bool WaitForTexture(lcTexture* Texture);
|
||||
|
||||
// Drain all pending work, including unrelated assets. Completion does not imply success.
|
||||
void WaitForLoadQueue();
|
||||
|
||||
// True while piece, texture, or generated-mesh requests or worker completions remain unsettled.
|
||||
// Includes paused work and upload retries; deferred notifications alone do not count.
|
||||
bool HasPendingWork();
|
||||
|
||||
// Returns this instance's recorded generation error, or an empty string if none is recorded.
|
||||
// An empty string does not establish readiness; check the generated mesh separately.
|
||||
QString GetSynthMeshError(const lcPiece* Piece) const;
|
||||
|
||||
void RebuildModelPiece(PieceInfo* Info);
|
||||
@@ -90,14 +100,6 @@ public:
|
||||
void CancelAndDrain();
|
||||
|
||||
private:
|
||||
enum class Priority
|
||||
{
|
||||
// Requests only promote; the priority lasts until completion or cancellation.
|
||||
Background, // Ordinary queued asset work.
|
||||
Visible, // Assets requested for the visible model.
|
||||
Blocking // Assets needed by a synchronous wait.
|
||||
};
|
||||
|
||||
// Owns one PieceInfo load hold through completion and deferred notification.
|
||||
// Parsing and conversion are separate worker phases; textures settle before publication.
|
||||
struct PieceRequest
|
||||
@@ -105,7 +107,7 @@ private:
|
||||
PieceInfo* Info;
|
||||
quint64 Generation;
|
||||
lcPieceSourceSnapshot Source;
|
||||
Priority LoadPriority;
|
||||
lcPieceLoadPriority LoadPriority;
|
||||
qint64 EnqueuedAt;
|
||||
bool Running = false; // A worker phase is outstanding, including its completion.
|
||||
bool Obsolete = false; // Its result must not publish, even if the build succeeds.
|
||||
@@ -127,7 +129,7 @@ private:
|
||||
lcTexture* Texture;
|
||||
quint64 Generation;
|
||||
lcFileSource Source;
|
||||
Priority LoadPriority;
|
||||
lcPieceLoadPriority LoadPriority;
|
||||
qint64 EnqueuedAt;
|
||||
bool Running = false;
|
||||
int UploadAttempts = 0;
|
||||
@@ -141,7 +143,7 @@ private:
|
||||
lcPiece* Piece;
|
||||
std::shared_ptr<const lcSynthInfo> SynthInfo;
|
||||
std::vector<lcPieceControlPoint> ControlPoints;
|
||||
Priority LoadPriority;
|
||||
lcPieceLoadPriority LoadPriority;
|
||||
qint64 EnqueuedAt;
|
||||
quint64 Generation;
|
||||
bool Running = false;
|
||||
@@ -188,10 +190,10 @@ private:
|
||||
|
||||
// Enqueue and promote requests. Workers read copied sources and private build data.
|
||||
// Acquires mQueueMutex internally; callers must not already hold it.
|
||||
void EnqueuePiece(PieceInfo* Info, Priority LoadPriority);
|
||||
void EnqueuePiece(PieceInfo* Info, lcPieceLoadPriority LoadPriority);
|
||||
// Caller holds mQueueMutex.
|
||||
void PromoteRequestPriorityLocked(const std::shared_ptr<PieceRequest>& RequestedPiece, Priority LoadPriority);
|
||||
void QueueTexture(lcTexture* Texture, Priority LoadPriority);
|
||||
void PromoteRequestPriorityLocked(const std::shared_ptr<PieceRequest>& RequestedPiece, lcPieceLoadPriority LoadPriority);
|
||||
void QueueTexture(lcTexture* Texture, lcPieceLoadPriority LoadPriority);
|
||||
// Caller holds mQueueMutex.
|
||||
void StartWorkersLocked();
|
||||
void RunWorker();
|
||||
@@ -203,7 +205,7 @@ private:
|
||||
void CompleteTexture(Completion& Completed);
|
||||
static void ResolveMeshColors(lcLibraryMeshData& MeshData, std::vector<bool>& ColorTranslucency, int& DefaultColorIndex);
|
||||
bool ResolveAndQueueMeshTextures(lcMesh& Mesh, const std::vector<QString>& SearchDirectories,
|
||||
const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData, Priority LoadPriority, QString& Error);
|
||||
const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData, lcPieceLoadPriority LoadPriority, QString& Error);
|
||||
static bool MeshTexturesSettled(const lcMesh& Mesh, QString& Error);
|
||||
static QString TextureFailureMessage(const lcTexture* Texture);
|
||||
void UploadTextureRequest(const std::shared_ptr<TextureRequest>& RequestedTexture);
|
||||
|
||||
+14
-22
@@ -526,7 +526,7 @@ lcTexture* lcPiecesLibrary::FindTexture(const char* TextureName, Project* Curren
|
||||
if (!Texture)
|
||||
return nullptr;
|
||||
|
||||
if (EnsureTextureReady(Texture))
|
||||
if (mAssetLoader->WaitForTexture(Texture))
|
||||
return Texture;
|
||||
|
||||
ReleaseTexture(Texture);
|
||||
@@ -1650,9 +1650,13 @@ bool lcPiecesLibrary::SaveArchiveCacheIndex(const QString& FileName)
|
||||
return WriteArchiveCacheFile(FileName, IndexFile);
|
||||
}
|
||||
|
||||
bool lcPiecesLibrary::LoadPieceInfo(PieceInfo* Info, lcPieceLoadFlags Flags)
|
||||
bool lcPiecesLibrary::LoadPieceInfo(PieceInfo* Info, lcPieceLoadPriority LoadPriority)
|
||||
{
|
||||
return mAssetLoader->LoadPieceInfo(Info, Flags);
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
AddPieceReference(Info);
|
||||
|
||||
return mAssetLoader->LoadPieceInfo(Info, LoadPriority);
|
||||
}
|
||||
|
||||
void lcPiecesLibrary::NotifyConsumersChanged()
|
||||
@@ -1660,14 +1664,9 @@ void lcPiecesLibrary::NotifyConsumersChanged()
|
||||
emit AssetRequestsChanged();
|
||||
}
|
||||
|
||||
bool lcPiecesLibrary::EnsurePieceReady(PieceInfo* Info)
|
||||
{
|
||||
return mAssetLoader->EnsurePieceReady(Info);
|
||||
}
|
||||
|
||||
bool lcPiecesLibrary::EnsurePieceAssetsReady(PieceInfo* Info)
|
||||
{
|
||||
const bool PieceReady = EnsurePieceReady(Info);
|
||||
const bool PieceReady = WaitForPieces({ Info });
|
||||
|
||||
if (Info->IsModel())
|
||||
return static_cast<bool>(Info->GetModel()->EnsureAssetsReady()) && PieceReady;
|
||||
@@ -1683,9 +1682,9 @@ bool lcPiecesLibrary::EnsurePieceAssetsReady(PieceInfo* Info)
|
||||
return PieceReady;
|
||||
}
|
||||
|
||||
bool lcPiecesLibrary::EnsurePiecesReady(const std::vector<PieceInfo*>& Parts)
|
||||
bool lcPiecesLibrary::WaitForPieces(const std::vector<PieceInfo*>& Pieces)
|
||||
{
|
||||
return mAssetLoader->EnsurePiecesReady(Parts);
|
||||
return mAssetLoader->WaitForPieces(Pieces);
|
||||
}
|
||||
|
||||
void lcPiecesLibrary::QueueSynthMesh(lcPiece* Piece)
|
||||
@@ -1699,9 +1698,9 @@ void lcPiecesLibrary::CancelSynthMesh(lcPiece* Piece)
|
||||
mAssetLoader->CancelSynthMesh(Piece);
|
||||
}
|
||||
|
||||
bool lcPiecesLibrary::EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces)
|
||||
bool lcPiecesLibrary::WaitForSynthMeshes(const std::vector<lcPiece*>& Pieces)
|
||||
{
|
||||
return mAssetLoader->EnsureSynthMeshesReady(Pieces);
|
||||
return mAssetLoader->WaitForSynthMeshes(Pieces);
|
||||
}
|
||||
|
||||
QString lcPiecesLibrary::GetSynthMeshError(const lcPiece* Piece) const
|
||||
@@ -1725,11 +1724,6 @@ void lcPiecesLibrary::InvalidatePiece(PieceInfo* Info)
|
||||
}
|
||||
}
|
||||
|
||||
bool lcPiecesLibrary::EnsureTextureReady(lcTexture* Texture)
|
||||
{
|
||||
return mAssetLoader->EnsureTextureReady(Texture);
|
||||
}
|
||||
|
||||
void lcPiecesLibrary::ReleasePieceInfo(PieceInfo* Info)
|
||||
{
|
||||
int Remaining;
|
||||
@@ -2327,17 +2321,15 @@ void lcPiecesLibrary::SetStudStyle(lcStudStyle StudStyle, bool Reload, bool Stud
|
||||
|
||||
if (Info->IsModel())
|
||||
{
|
||||
lcModel* Model = Info->GetModel();
|
||||
Info->SetModel(Model);
|
||||
Info->ReleaseMesh();
|
||||
Info->mState = lcPieceInfoState::Unloaded;
|
||||
|
||||
mAssetLoader->QueuePiece(Info);
|
||||
mAssetLoader->LoadPieceInfo(Info, lcPieceLoadPriority::Low);
|
||||
}
|
||||
else if (!Info->IsProject())
|
||||
{
|
||||
Info->Unload();
|
||||
mAssetLoader->QueuePiece(Info);
|
||||
mAssetLoader->LoadPieceInfo(Info, lcPieceLoadPriority::Low);
|
||||
}
|
||||
|
||||
mBuffersDirty = true;
|
||||
|
||||
+13
-16
@@ -75,16 +75,15 @@ enum class lcPrimitiveState
|
||||
Loaded
|
||||
};
|
||||
|
||||
enum class lcPieceLoadFlag
|
||||
// Scheduling urgency. LoadPieceInfo() also waits when Blocking is requested.
|
||||
// Queued requests only promote and keep their priority until completion or cancellation.
|
||||
enum class lcPieceLoadPriority
|
||||
{
|
||||
None = 0,
|
||||
Wait = 1 << 0,
|
||||
Visible = 1 << 1
|
||||
Low, // Background work, such as thumbnails and settings rebuilds.
|
||||
Normal, // Visible or interactive work.
|
||||
Blocking // Assets required by a synchronous operation.
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(lcPieceLoadFlags, lcPieceLoadFlag)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(lcPieceLoadFlags)
|
||||
|
||||
class lcLibraryPrimitive
|
||||
{
|
||||
public:
|
||||
@@ -191,26 +190,24 @@ public:
|
||||
|
||||
// Asset scheduling, waits, invalidation, and consumer releases below run on the UI thread.
|
||||
// Acquires one reference even on failure; always pair with ReleasePieceInfo().
|
||||
// Without Wait, true does not establish readiness. See docs/asset-loading.md.
|
||||
bool LoadPieceInfo(PieceInfo* Info, lcPieceLoadFlags Flags);
|
||||
// Blocking waits and reports success; Low and Normal return immediately. See docs/asset-loading.md.
|
||||
bool LoadPieceInfo(PieceInfo* Info, lcPieceLoadPriority LoadPriority);
|
||||
void NotifyConsumersChanged();
|
||||
// Wait for these identities only, with temporary holds; false means failure/cancellation.
|
||||
// Start or promote loading as needed, then wait with temporary holds; false means failure/cancellation.
|
||||
// Does not traverse container children or wait for per-instance synth geometry.
|
||||
bool EnsurePieceReady(PieceInfo* Info);
|
||||
bool EnsurePiecesReady(const std::vector<PieceInfo*>& Parts);
|
||||
bool WaitForPieces(const std::vector<PieceInfo*>& Pieces);
|
||||
// Also settles container children/synths and updates bounds; caller retains the identity.
|
||||
bool EnsurePieceAssetsReady(PieceInfo* Info);
|
||||
// Replaces any pending generation for this instance; snapshots control points for workers.
|
||||
void QueueSynthMesh(lcPiece* Piece);
|
||||
void CancelSynthMesh(lcPiece* Piece);
|
||||
// Caller keeps the instances alive through the wait; false means missing generated geometry.
|
||||
bool EnsureSynthMeshesReady(const std::vector<lcPiece*>& Pieces);
|
||||
// Promote and wait for queued generations; false means missing or outdated generated geometry.
|
||||
// Call QueueSynthMesh() to start a generation; keep the instances alive through the wait.
|
||||
bool WaitForSynthMeshes(const std::vector<lcPiece*>& Pieces);
|
||||
QString GetSynthMeshError(const lcPiece* Piece) const;
|
||||
void RebuildModelPiece(PieceInfo* Info);
|
||||
// Obsoletes pending work and resets readiness without acquiring a reference.
|
||||
void InvalidatePiece(PieceInfo* Info);
|
||||
// Waits through GPU upload; caller retains the texture, and false means not ready.
|
||||
bool EnsureTextureReady(lcTexture* Texture);
|
||||
// Releases one reference and lets the loader cancel unused queued work.
|
||||
void ReleasePieceInfo(PieceInfo* Info);
|
||||
// Lifetime hold only: does not start loading or establish readiness.
|
||||
|
||||
@@ -2024,7 +2024,7 @@ void lcMainWindow::SetCurrentPieceInfo(PieceInfo* Info)
|
||||
mCurrentPieceInfo = Info;
|
||||
|
||||
if (mCurrentPieceInfo)
|
||||
Library->LoadPieceInfo(mCurrentPieceInfo, lcPieceLoadFlag::Visible);
|
||||
Library->LoadPieceInfo(mCurrentPieceInfo, lcPieceLoadPriority::Normal);
|
||||
}
|
||||
|
||||
lcVector3 lcMainWindow::GetTransformAmount()
|
||||
|
||||
+7
-7
@@ -393,7 +393,7 @@ void lcModel::CreatePieceInfo(Project* Project)
|
||||
// until its source lines have been parsed and QueueMeshBuild() runs.
|
||||
if (!Project->DefersModelMeshRequests())
|
||||
{
|
||||
Library->LoadPieceInfo(mPieceInfo, mIsPreview ? lcPieceLoadFlag::Visible : lcPieceLoadFlag::Wait | lcPieceLoadFlag::Visible);
|
||||
Library->LoadPieceInfo(mPieceInfo, mIsPreview ? lcPieceLoadPriority::Normal : lcPieceLoadPriority::Blocking);
|
||||
Library->ReleasePieceInfo(mPieceInfo);
|
||||
}
|
||||
|
||||
@@ -518,8 +518,8 @@ void lcModel::WaitForAssets() const
|
||||
const std::vector<PieceInfo*> Required = GetRequiredPieces();
|
||||
lcPiecesLibrary* Library = lcGetPiecesLibrary();
|
||||
|
||||
Library->EnsurePiecesReady(Required);
|
||||
Library->EnsureSynthMeshesReady(GetRequiredSynthPieces());
|
||||
Library->WaitForPieces(Required);
|
||||
Library->WaitForSynthMeshes(GetRequiredSynthPieces());
|
||||
|
||||
// Temporary minifigure and thumbnail models have no container piece to update.
|
||||
if (mPieceInfo)
|
||||
@@ -1369,7 +1369,7 @@ bool lcModel::LoadInventory(const std::vector<lcSetInventoryItem>& SetInventory)
|
||||
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
|
||||
Required.push_back(Piece->mPieceInfo);
|
||||
|
||||
if (!Library->EnsurePiecesReady(Required))
|
||||
if (!Library->WaitForPieces(Required))
|
||||
return false;
|
||||
|
||||
Library->mBuffersDirty = true;
|
||||
@@ -3125,7 +3125,7 @@ lcPiece* lcModel::AddPiece(PieceInfo* Info, quint32 Section)
|
||||
lcGetPiecesLibrary()->EnsurePieceAssetsReady(Last->mPieceInfo);
|
||||
|
||||
if (Last->mPieceInfo->GetSynthInfo())
|
||||
lcGetPiecesLibrary()->EnsureSynthMeshesReady({ Last });
|
||||
lcGetPiecesLibrary()->WaitForSynthMeshes({ Last });
|
||||
}
|
||||
|
||||
const lcBoundingBox& PieceInfoBoundingBox = Info->GetBoundingBox();
|
||||
@@ -3418,11 +3418,11 @@ void lcModel::SetPreviewInsertPieceInfo(std::vector<lcInsertPieceInfo>&& Preview
|
||||
|
||||
for (lcInsertPieceInfo& InfoTransform : PreviewInsertPieceInfo)
|
||||
{
|
||||
Library->LoadPieceInfo(InfoTransform.Info, lcPieceLoadFlag::Visible);
|
||||
Library->LoadPieceInfo(InfoTransform.Info, lcPieceLoadPriority::Normal);
|
||||
Required.push_back(InfoTransform.Info);
|
||||
}
|
||||
|
||||
Library->EnsurePiecesReady(Required);
|
||||
Library->WaitForPieces(Required);
|
||||
|
||||
for (lcInsertPieceInfo& PreviewPieceInfoTransform : mPreviewInsertPieceInfo)
|
||||
Library->ReleasePieceInfo(PreviewPieceInfoTransform.Info);
|
||||
|
||||
@@ -111,7 +111,7 @@ void lcPreviewDockWidget::RefreshModel(const lcModel* Model)
|
||||
if (Info->mState != lcPieceInfoState::Unloaded)
|
||||
continue;
|
||||
|
||||
Library->LoadPieceInfo(Info, lcPieceLoadFlag::Visible);
|
||||
Library->LoadPieceInfo(Info, lcPieceLoadPriority::Normal);
|
||||
Library->ReleasePieceInfo(Info);
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ bool lcPreview::SetCurrentPiece(PieceInfo* Info, int ColorCode)
|
||||
}
|
||||
|
||||
lcPiecesLibrary* Library = lcGetPiecesLibrary();
|
||||
bool Ready = Library->LoadPieceInfo(Info, lcPieceLoadFlag::Wait | lcPieceLoadFlag::Visible);
|
||||
bool Ready = Library->LoadPieceInfo(Info, lcPieceLoadPriority::Blocking);
|
||||
|
||||
if (Ready && Info->IsModel())
|
||||
Ready = static_cast<bool>(Info->GetModel()->EnsureAssetsReady());
|
||||
|
||||
@@ -136,7 +136,7 @@ void lcThumbnailManager::RefreshRequiredPieces(lcPartThumbnail& Thumbnail)
|
||||
for (PieceInfo* Info : Required)
|
||||
{
|
||||
if (Previous.erase(Info) == 0)
|
||||
mLibrary->LoadPieceInfo(Info, lcPieceLoadFlag::None);
|
||||
mLibrary->LoadPieceInfo(Info, lcPieceLoadPriority::Low);
|
||||
}
|
||||
|
||||
for (PieceInfo* Info : Previous)
|
||||
|
||||
+2
-2
@@ -493,7 +493,7 @@ std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(
|
||||
lcGetPiecesLibrary()->EnsurePieceAssetsReady(Info);
|
||||
|
||||
if (MovingPiece && Info->GetSynthInfo())
|
||||
lcGetPiecesLibrary()->EnsureSynthMeshesReady({ MovingPiece });
|
||||
lcGetPiecesLibrary()->WaitForSynthMeshes({ MovingPiece });
|
||||
|
||||
lcObjectRayTest ObjectRayTest = RayTest(true, IgnoreSelected);
|
||||
|
||||
@@ -507,7 +507,7 @@ std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(
|
||||
lcGetPiecesLibrary()->EnsurePieceAssetsReady(MouseInfo);
|
||||
|
||||
if (MouseInfo->GetSynthInfo())
|
||||
lcGetPiecesLibrary()->EnsureSynthMeshesReady({ MousePiece });
|
||||
lcGetPiecesLibrary()->WaitForSynthMeshes({ MousePiece });
|
||||
|
||||
if (!Retest)
|
||||
break;
|
||||
|
||||
+3
-3
@@ -215,7 +215,7 @@ void MinifigWizard::LoadTemplate(const QString& TemplateName)
|
||||
mMinifig.Parts[PartIndex] = Info;
|
||||
|
||||
if (Info)
|
||||
Library->LoadPieceInfo(Info, lcPieceLoadFlag::Visible);
|
||||
Library->LoadPieceInfo(Info, lcPieceLoadPriority::Normal);
|
||||
}
|
||||
|
||||
std::vector<PieceInfo*> Required;
|
||||
@@ -224,7 +224,7 @@ void MinifigWizard::LoadTemplate(const QString& TemplateName)
|
||||
if (Info)
|
||||
Required.push_back(Info);
|
||||
|
||||
Library->EnsurePiecesReady(Required);
|
||||
Library->WaitForPieces(Required);
|
||||
|
||||
Calculate();
|
||||
}
|
||||
@@ -513,7 +513,7 @@ void MinifigWizard::SetPieceInfo(int Type, PieceInfo* Info)
|
||||
mMinifig.Parts[Type] = Info;
|
||||
|
||||
if (mMinifig.Parts[Type])
|
||||
Library->LoadPieceInfo(mMinifig.Parts[Type], lcPieceLoadFlag::Wait | lcPieceLoadFlag::Visible);
|
||||
Library->LoadPieceInfo(mMinifig.Parts[Type], lcPieceLoadPriority::Blocking);
|
||||
|
||||
Calculate();
|
||||
}
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ void lcPiece::SetPieceInfo(PieceInfo* Info, const QString& ID, bool Wait, bool U
|
||||
|
||||
mPieceInfo = Info;
|
||||
if (mPieceInfo)
|
||||
Library->LoadPieceInfo(mPieceInfo, Wait ? lcPieceLoadFlag::Wait | lcPieceLoadFlag::Visible : lcPieceLoadFlag::Visible);
|
||||
Library->LoadPieceInfo(mPieceInfo, Wait ? lcPieceLoadPriority::Blocking : lcPieceLoadPriority::Normal);
|
||||
|
||||
if (!ID.isEmpty())
|
||||
mID = ID;
|
||||
|
||||
+22
-15
@@ -289,8 +289,9 @@ sequenceDiagram
|
||||
participant Library as Library
|
||||
participant Loader as Loader / UI thread
|
||||
participant Worker as Qt worker
|
||||
Caller->>Library: LoadPieceInfo(Info, Flags)
|
||||
Library->>Loader: Acquire reference and queue/promote
|
||||
Caller->>Library: LoadPieceInfo(Info, LoadPriority)
|
||||
Library->>Library: Acquire consumer reference
|
||||
Library->>Loader: Queue/promote
|
||||
Loader->>Worker: Immutable source snapshot
|
||||
Worker-->>Loader: Private mesh data or cached mesh
|
||||
Loader->>Loader: Resolve global colors
|
||||
@@ -338,7 +339,7 @@ buffer upload is not part of asset readiness.
|
||||
`LoadPieceInfo()` acquires one reference even if loading fails. Always pair it
|
||||
with `ReleasePieceInfo()`. Its asynchronous return value means the call did not
|
||||
wait, not that the asset loaded. `AddPieceReference()` retains an identity without
|
||||
starting work. `EnsurePiecesReady()` deduplicates its input, temporarily holds
|
||||
starting work. `WaitForPieces()` deduplicates its input, temporarily holds
|
||||
each part, and releases those holds before returning.
|
||||
|
||||
A part request owns an additional in-flight reference. Removing one instance
|
||||
@@ -384,10 +385,13 @@ ownership.
|
||||
|
||||
## Scheduling and waits
|
||||
|
||||
Priorities only promote: `Background < Visible < Blocking`. Ordinary thumbnail
|
||||
requests start Background; asynchronous model pieces and generated meshes start
|
||||
Visible, regardless of step. Synchronous waits promote their required requests
|
||||
and pending texture dependencies to Blocking. Shared textures retain the highest
|
||||
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 `lcPieceLoadPriority` enum
|
||||
is used at the loading entry point and throughout the scheduler. `LoadPieceInfo()`
|
||||
waits only for Blocking; Low and Normal return immediately. 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
|
||||
completes or is cancelled, not merely until the requesting view closes.
|
||||
@@ -398,10 +402,12 @@ level per ten seconds of waiting, capped at Blocking, with oldest work winning
|
||||
ties. Completion processing uses promoted priorities. Acquiring a pending asset
|
||||
shares its request rather than submitting another build.
|
||||
|
||||
`EnsurePieceReady()` / `EnsurePiecesReady()` wait for the supplied identities,
|
||||
not the entire library, and return false for failed or cancelled requirements.
|
||||
They do not expand model containers themselves. `EnsureSynthMeshesReady()` waits
|
||||
for the specified instances; `EnsureTextureReady()` waits through upload.
|
||||
`WaitForPieces()` starts or promotes loading as needed, then waits for the supplied
|
||||
identities, not the entire library. It returns false for failed or cancelled
|
||||
requirements and does not expand model containers itself. `WaitForSynthMeshes()`
|
||||
promotes and waits for queued generations of the specified instances; callers
|
||||
start generations with `QueueSynthMesh()`. The loader's `WaitForTexture()` starts
|
||||
or promotes decoding as needed and waits through upload and retries.
|
||||
`lcModel::WaitForAssets()` combines recursive part and synth waits and updates
|
||||
bounds, but deliberately ignores their failure results. `EnsureAssetsReady()`
|
||||
adds strict failure reporting. `WaitForLoadQueue()` drains all pending work and
|
||||
@@ -412,13 +418,14 @@ workers, including unrelated assets; it is not a scoped success check.
|
||||
| Need | API and lifetime contract |
|
||||
| --- | --- |
|
||||
| Resolve a name | `FindPiece()` 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 | `LoadPieceInfo()` without `lcPieceLoadFlag::Wait`; add `Visible` for model/view work. Pair every acquisition with `ReleasePieceInfo()`, including failures. |
|
||||
| Retain and start ordinary asynchronous work | `LoadPieceInfo()` with `lcPieceLoadPriority::Low` for background work or `Normal` for model/view work. Pair every acquisition with `ReleasePieceInfo()`, including failures. |
|
||||
| Retain and synchronously load one identity | `LoadPieceInfo()` with `lcPieceLoadPriority::Blocking`; check its return value and pair the acquisition with `ReleasePieceInfo()`, including failures. |
|
||||
| Retain without starting work | `AddPieceReference()`, paired with `ReleasePieceInfo()`. This does not make geometry ready. |
|
||||
| Wait for an explicit part set | `EnsurePiecesReady()` (or `EnsurePieceReady()`), check the boolean for strict consumers. Temporary holds end before return, so the caller still needs ownership if it will use the assets afterward. |
|
||||
| Wait for an explicit part set | `WaitForPieces()` (use `{ Info }` for one identity), check the boolean for strict consumers. Temporary holds end before return, so the caller still needs ownership if it will use the assets afterward. |
|
||||
| Wait for one identity and its container geometry | `EnsurePieceAssetsReady()` 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 | `QueueSynthMesh()` / `EnsureSynthMeshesReady()`. Supply live `lcPiece` instances; a shared part mesh or a retained previous generated mesh is not readiness for the replacement. |
|
||||
| Acquire a texture directly | `FindTexture()` waits for readiness and returns an acquired texture or null. `FindTextureDeferred()` acquires without waiting; `EnsureTextureReady()` waits through GPU upload. Retain the texture during a direct wait and pair acquisition with `ReleaseTexture()`. |
|
||||
| Replace/wait for generated geometry | `QueueSynthMesh()` / `WaitForSynthMeshes()`. Supply live `lcPiece` instances; a shared part mesh or a retained previous generated mesh is not readiness for the replacement. |
|
||||
| Acquire a texture directly | `FindTexture()` waits for readiness and returns an acquired texture or null. `FindTextureDeferred()` acquires without waiting. The loader handles GPU upload readiness internally. Retain the texture during a direct wait and pair acquisition with `ReleaseTexture()`. |
|
||||
| Synchronize an entire library operation | `WaitForLoadQueue()`; reserve this for operations that truly need all work drained, such as settings changes. Ordinary previews must use scoped waits. |
|
||||
|
||||
Scoped waits may process unrelated completions while pumping publication, but
|
||||
|
||||
Reference in new issue
Block a user