mirror of
https://github.com/leozide/leocad.git
synced 2026-10-26 22:10:32 +00:00
Build piece and synth meshes in one worker job
Color finalization no longer requires a UI-thread handoff between parsing and mesh conversion. Have the library and synth build services return converted private meshes, removing intermediate request/result mesh data and conversion requeue paths. Fall back to source parsing immediately when a cached mesh cannot be decoded, eliminating SkipCache and the UI retry branch. Simplify paused staging cleanup and document the combined worker lifecycle; section finalization and publication remain on the UI thread.
This commit is contained in:
1 parent
9f7e4d3a41
commit
4129788f95
7 files changed
+107
-183
No files matched your search
@@ -14,7 +14,7 @@ constexpr QEvent::Type lcAssetLoaderCompletionEvent = static_cast<QEvent::Type>(
|
||||
|
||||
lcPieceBuildResult::~lcPieceBuildResult() = default;
|
||||
|
||||
// Normal part flow: enqueue -> build -> resolve colors -> convert -> settle textures -> publish -> notify.
|
||||
// Normal part flow: enqueue -> build and convert -> finalize sections -> settle textures -> publish -> notify.
|
||||
// Workers own private build data. Publication and deferred notifications stay on the UI thread.
|
||||
lcAssetLoader::lcAssetLoader(lcPiecesLibrary* Library)
|
||||
: mLibrary(Library)
|
||||
@@ -265,16 +265,7 @@ void lcAssetLoader::RunWorker()
|
||||
{
|
||||
Completed.RequestedPiece = RequestedPiece;
|
||||
|
||||
if (RequestedPiece->MeshData)
|
||||
{
|
||||
Completed.PieceResult.Mesh.reset(RequestedPiece->MeshData->CreateMesh(nullptr));
|
||||
RequestedPiece->MeshData.reset();
|
||||
|
||||
if (!Completed.PieceResult.Mesh)
|
||||
Completed.PieceResult.Error = tr("Could not prepare piece %1 for rendering.").arg(RequestedPiece->Source.FileName);
|
||||
}
|
||||
else
|
||||
Completed.PieceResult = mLibrary->BuildPieceData(RequestedPiece->Source);
|
||||
Completed.PieceResult = mLibrary->BuildPieceData(RequestedPiece->Source);
|
||||
}
|
||||
else if (RequestedTexture)
|
||||
{
|
||||
@@ -285,15 +276,7 @@ void lcAssetLoader::RunWorker()
|
||||
{
|
||||
Completed.RequestedSynth = RequestedSynth;
|
||||
|
||||
if (RequestedSynth->Cancelled.load(std::memory_order_relaxed))
|
||||
RequestedSynth->MeshData.reset();
|
||||
else if (RequestedSynth->MeshData)
|
||||
{
|
||||
Completed.SynthMesh.reset(RequestedSynth->MeshData->CreateMesh(&RequestedSynth->Cancelled));
|
||||
RequestedSynth->MeshData.reset();
|
||||
}
|
||||
else
|
||||
Completed.SynthMeshData = RequestedSynth->SynthInfo->BuildMeshData(RequestedSynth->ControlPoints, RequestedSynth->Cancelled);
|
||||
Completed.SynthMesh = RequestedSynth->SynthInfo->BuildMesh(RequestedSynth->ControlPoints, RequestedSynth->Cancelled);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -459,42 +442,13 @@ void lcAssetLoader::ProcessPieceCompletion(Completion& Completed)
|
||||
RequestedPiece->Error = Completed.PieceResult.Error;
|
||||
RequestedPiece->EmptyGeometry = Completed.PieceResult.EmptyGeometry;
|
||||
|
||||
if (RequestedPiece->Error.isEmpty())
|
||||
if (RequestedPiece->Error.isEmpty() && Completed.PieceResult.Mesh)
|
||||
{
|
||||
if (Completed.PieceResult.FromCache && !Completed.PieceResult.Mesh)
|
||||
{
|
||||
// Rebuild a corrupt cache privately on a worker, then use the normal commit path.
|
||||
RequestedPiece->Source.SkipCache = true;
|
||||
Info->mState = lcPieceInfoState::Queued;
|
||||
RequestedPiece->StagedMesh = std::move(Completed.PieceResult.Mesh);
|
||||
|
||||
QMutexLocker QueueLock(&mQueueMutex);
|
||||
RequestedPiece->StagedMesh->FinalizeSections();
|
||||
|
||||
mPieceQueue.push_back(RequestedPiece);
|
||||
StartWorkersLocked();
|
||||
|
||||
return;
|
||||
}
|
||||
else if (Completed.PieceResult.MeshData)
|
||||
{
|
||||
// Queue the expensive geometry conversion; colors are finalized after it completes.
|
||||
RequestedPiece->MeshData = std::move(Completed.PieceResult.MeshData);
|
||||
Info->mState = lcPieceInfoState::Queued;
|
||||
|
||||
QMutexLocker QueueLock(&mQueueMutex);
|
||||
|
||||
mPieceQueue.push_back(RequestedPiece);
|
||||
StartWorkersLocked();
|
||||
|
||||
return;
|
||||
}
|
||||
else if (Completed.PieceResult.Mesh)
|
||||
{
|
||||
RequestedPiece->StagedMesh = std::move(Completed.PieceResult.Mesh);
|
||||
|
||||
RequestedPiece->StagedMesh->FinalizeSections();
|
||||
|
||||
RequestedPiece->SaveCache = !Completed.PieceResult.FromCache;
|
||||
}
|
||||
RequestedPiece->SaveCache = !Completed.PieceResult.FromCache;
|
||||
}
|
||||
|
||||
// A private mesh cannot be published until all its declared textures settle.
|
||||
@@ -535,19 +489,6 @@ void lcAssetLoader::ProcessSynthCompletion(Completion& Completed)
|
||||
return;
|
||||
}
|
||||
|
||||
if (Completed.SynthMeshData)
|
||||
{
|
||||
// Queue the expensive geometry conversion; colors are finalized after it completes.
|
||||
RequestedSynth->MeshData = std::move(Completed.SynthMeshData);
|
||||
|
||||
QMutexLocker QueueLock(&mQueueMutex);
|
||||
|
||||
mSynthQueue.push_back(RequestedSynth);
|
||||
StartWorkersLocked();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Completed.SynthMesh)
|
||||
{
|
||||
RequestedSynth->Error = tr("Could not generate geometry for piece '%1'.").arg(QString::fromLatin1(RequestedSynth->Piece->mPieceInfo->mFileName));
|
||||
@@ -1314,7 +1255,7 @@ void lcAssetLoader::CancelPieceLoad(PieceInfo* Info)
|
||||
|
||||
void lcAssetLoader::CancelSynthMesh(lcPiece* Piece)
|
||||
{
|
||||
// Remove queued work immediately; running synth phases notice cancellation cooperatively.
|
||||
// Remove queued work immediately; running synth builds notice cancellation cooperatively.
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
mSynthPieces.erase(Piece);
|
||||
@@ -1428,18 +1369,11 @@ void lcAssetLoader::PauseQueuedWork()
|
||||
{
|
||||
const std::shared_ptr<PieceRequest>& RequestedPiece = Entry.second;
|
||||
|
||||
if (RequestedPiece->MeshData)
|
||||
{
|
||||
RequestedPiece->MeshData.reset();
|
||||
RequestedPiece->Info->mState = lcPieceInfoState::Queued;
|
||||
}
|
||||
|
||||
if (!RequestedPiece->StagedMesh)
|
||||
continue;
|
||||
|
||||
RequestedPiece->StagedMesh.reset();
|
||||
RequestedPiece->Info->mState = lcPieceInfoState::Queued;
|
||||
RequestedPiece->AwaitingCompletion = false;
|
||||
mPieceQueue.push_back(RequestedPiece);
|
||||
}
|
||||
|
||||
@@ -1448,10 +1382,9 @@ void lcAssetLoader::PauseQueuedWork()
|
||||
{
|
||||
const std::shared_ptr<SynthRequest>& RequestedSynth = Entry.second;
|
||||
|
||||
if (!RequestedSynth->StagedMesh && !RequestedSynth->MeshData)
|
||||
if (!RequestedSynth->StagedMesh)
|
||||
continue;
|
||||
|
||||
RequestedSynth->MeshData.reset();
|
||||
RequestedSynth->StagedMesh.reset();
|
||||
|
||||
if (std::find(mSynthQueue.begin(), mSynthQueue.end(), RequestedSynth) == mSynthQueue.end())
|
||||
@@ -1466,12 +1399,7 @@ void lcAssetLoader::ResumeQueuedWork()
|
||||
QMutexLocker QueueLock(&mQueueMutex);
|
||||
|
||||
for (const std::shared_ptr<PieceRequest>& RequestedPiece : mPieceQueue)
|
||||
{
|
||||
const bool SkipCache = RequestedPiece->Source.SkipCache;
|
||||
|
||||
RequestedPiece->Source = mLibrary->SnapshotPieceSource(RequestedPiece->Info);
|
||||
RequestedPiece->Source.SkipCache = SkipCache;
|
||||
}
|
||||
|
||||
mPaused = false;
|
||||
StartWorkersLocked();
|
||||
|
||||
+10
-10
@@ -8,7 +8,6 @@ class PieceInfo;
|
||||
class lcPiece;
|
||||
class lcMesh;
|
||||
class lcSynthInfo;
|
||||
class lcLibraryMeshData;
|
||||
class lcTexture;
|
||||
enum class lcTextureLoadError;
|
||||
|
||||
@@ -22,7 +21,6 @@ struct lcPieceSourceSnapshot
|
||||
std::map<QString, std::shared_ptr<const lcEmbeddedData>> EmbeddedData;
|
||||
QStringList InlineMeshLines;
|
||||
bool InlineModel = false;
|
||||
bool SkipCache = false;
|
||||
lcZipFileType ZipFileType = lcZipFileType::Count;
|
||||
int ZipFileIndex = -1;
|
||||
lcStudStyle StudStyle = lcStudStyle::Plain;
|
||||
@@ -40,7 +38,6 @@ struct lcPieceBuildResult
|
||||
lcPieceBuildResult(const lcPieceBuildResult&) = delete;
|
||||
lcPieceBuildResult& operator=(const lcPieceBuildResult&) = delete;
|
||||
|
||||
std::unique_ptr<lcLibraryMeshData> MeshData;
|
||||
std::unique_ptr<lcMesh> Mesh;
|
||||
bool FromCache = false;
|
||||
bool EmptyGeometry = false;
|
||||
@@ -100,18 +97,17 @@ public:
|
||||
|
||||
private:
|
||||
// Owns one PieceInfo load hold through completion and deferred notification.
|
||||
// Parsing and conversion are separate worker phases; textures settle before publication.
|
||||
// One worker job builds and converts geometry; textures settle before publication.
|
||||
struct PieceRequest
|
||||
{
|
||||
PieceInfo* Info;
|
||||
lcPieceSourceSnapshot Source;
|
||||
lcPieceLoadPriority LoadPriority;
|
||||
qint64 EnqueuedAt;
|
||||
bool AwaitingCompletion = false; // A worker phase is outstanding, including its completion.
|
||||
bool AwaitingCompletion = false; // A worker job is outstanding, including its completion.
|
||||
bool Obsolete = false; // Its result must not publish, even if the build succeeds.
|
||||
bool Terminal = false; // The wait has settled; this does not imply success.
|
||||
bool Succeeded = false; // Published successfully, including valid empty geometry.
|
||||
std::unique_ptr<lcLibraryMeshData> MeshData; // Present when the next worker phase is mesh conversion.
|
||||
std::unique_ptr<lcMesh> StagedMesh;
|
||||
bool EmptyGeometry = false;
|
||||
bool SaveCache = false;
|
||||
@@ -142,7 +138,6 @@ private:
|
||||
bool AwaitingCompletion = false;
|
||||
std::atomic_bool Cancelled = false;
|
||||
bool Terminal = false;
|
||||
std::unique_ptr<lcLibraryMeshData> MeshData; // Present when the next worker phase is mesh conversion.
|
||||
std::unique_ptr<lcMesh> StagedMesh;
|
||||
QString Error;
|
||||
};
|
||||
@@ -159,7 +154,6 @@ private:
|
||||
std::shared_ptr<TextureRequest> RequestedTexture;
|
||||
lcTextureBuildResult TextureResult;
|
||||
std::shared_ptr<SynthRequest> RequestedSynth;
|
||||
std::unique_ptr<lcLibraryMeshData> SynthMeshData;
|
||||
std::unique_ptr<lcMesh> SynthMesh;
|
||||
};
|
||||
|
||||
@@ -187,7 +181,7 @@ private:
|
||||
void StartWorkersLocked();
|
||||
void RunWorker();
|
||||
|
||||
// UI-thread completion handling: colors, conversion, texture dependencies, publication.
|
||||
// UI-thread completion handling: colors, texture dependencies, publication.
|
||||
bool ProcessCompletions();
|
||||
void ProcessPieceCompletion(Completion& Completed);
|
||||
void ProcessSynthCompletion(Completion& Completed);
|
||||
@@ -214,24 +208,30 @@ private:
|
||||
void CancelAndDrain();
|
||||
|
||||
lcPiecesLibrary* const mLibrary;
|
||||
|
||||
// Protects scheduling maps/queues, worker counts, and result handoff.
|
||||
// Private build data passes between worker and UI phases through completions.
|
||||
QMutex mQueueMutex;
|
||||
QWaitCondition mResultReady;
|
||||
|
||||
PieceRequestMap mPieceRequests;
|
||||
TextureRequestMap mTextureRequests;
|
||||
SynthRequestMap mSynthRequests;
|
||||
|
||||
// UI-thread diagnostics and instance tracking; these do not own the pieces.
|
||||
std::unordered_map<const lcPiece*, QString> mSynthErrors;
|
||||
std::unordered_set<lcPiece*> mSynthPieces;
|
||||
// Pending worker phases remain here while paused; mPaused prevents dispatch.
|
||||
|
||||
std::deque<std::shared_ptr<PieceRequest>> mPieceQueue;
|
||||
std::deque<std::shared_ptr<TextureRequest>> mTextureQueue;
|
||||
std::deque<std::shared_ptr<SynthRequest>> mSynthQueue;
|
||||
|
||||
std::deque<Completion> mCompletions;
|
||||
|
||||
// UI-thread only; public signals are deferred until outside synchronous waits.
|
||||
std::deque<PieceNotification> mPieceNotifications;
|
||||
std::deque<SynthNotification> mSynthNotifications;
|
||||
|
||||
std::vector<QFuture<void>> mFutures;
|
||||
int mActiveWorkers = 0;
|
||||
bool mPaused = false;
|
||||
|
||||
+68
-70
@@ -1823,10 +1823,34 @@ lcPieceBuildResult lcPiecesLibrary::BuildPieceData(const lcPieceSourceSnapshot&
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (!Source.InlineModel && Source.ZipFileType != lcZipFileType::Count && mZipFiles[static_cast<int>(Source.ZipFileType)])
|
||||
{
|
||||
lcMemFile CacheData;
|
||||
|
||||
if (ReadArchiveCacheFile(Source.CachePath, CacheData))
|
||||
{
|
||||
qint32 Flags;
|
||||
|
||||
if (CacheData.ReadBuffer(&Flags, sizeof(Flags)) == sizeof(Flags) && Flags == MeshCacheSettingsKey(Source.StudStyle, Source.StudCylinderColorEnabled))
|
||||
{
|
||||
Result.Mesh.reset(new lcMesh);
|
||||
|
||||
if (Result.Mesh->FileLoad(CacheData))
|
||||
{
|
||||
Result.FromCache = true;
|
||||
return Result;
|
||||
}
|
||||
|
||||
Result.Mesh.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lcLibraryMeshData MeshData;
|
||||
lcMeshLoader MeshLoader(MeshData, nullptr, lcMeshLoaderFlag::Optimize | lcMeshLoaderFlag::RequireAllIncludes);
|
||||
|
||||
if (Source.InlineModel)
|
||||
{
|
||||
Result.MeshData.reset(new lcLibraryMeshData);
|
||||
|
||||
lcMemFile PieceFile;
|
||||
|
||||
for (const QString& Line : Source.InlineMeshLines)
|
||||
@@ -1839,97 +1863,71 @@ lcPieceBuildResult lcPiecesLibrary::BuildPieceData(const lcPieceSourceSnapshot&
|
||||
|
||||
PieceFile.Seek(0, SEEK_SET);
|
||||
|
||||
lcMeshLoader MeshLoader(*Result.MeshData, nullptr, lcMeshLoaderFlag::Optimize | lcMeshLoaderFlag::RequireAllIncludes);
|
||||
|
||||
if (!MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED))
|
||||
{
|
||||
Result.MeshData.reset();
|
||||
Result.Error = tr("Could not load model geometry %1.").arg(Source.FileName);
|
||||
|
||||
if (!MeshLoader.GetError().isEmpty())
|
||||
Result.Error += QLatin1Char('\n') + MeshLoader.GetError();
|
||||
|
||||
return Result;
|
||||
}
|
||||
else if (Result.MeshData->IsEmpty())
|
||||
|
||||
if (MeshData.IsEmpty())
|
||||
{
|
||||
Result.MeshData.reset();
|
||||
Result.EmptyGeometry = true;
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (Result.MeshData)
|
||||
Result.MeshData->SetMeshLoader(nullptr);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (!Source.SkipCache && Source.ZipFileType != lcZipFileType::Count && mZipFiles[static_cast<int>(Source.ZipFileType)])
|
||||
{
|
||||
lcMemFile CacheData;
|
||||
|
||||
if (ReadArchiveCacheFile(Source.CachePath, CacheData))
|
||||
{
|
||||
qint32 Flags;
|
||||
|
||||
if (CacheData.ReadBuffer(&Flags, sizeof(Flags)) == sizeof(Flags) && Flags == MeshCacheSettingsKey(Source.StudStyle, Source.StudCylinderColorEnabled))
|
||||
{
|
||||
Result.FromCache = true;
|
||||
Result.Mesh.reset(new lcMesh);
|
||||
|
||||
if (!Result.Mesh->FileLoad(CacheData))
|
||||
Result.Mesh.reset();
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result.MeshData.reset(new lcLibraryMeshData);
|
||||
|
||||
lcMeshLoader MeshLoader(*Result.MeshData, nullptr, lcMeshLoaderFlag::Optimize | lcMeshLoaderFlag::RequireAllIncludes);
|
||||
|
||||
bool Loaded = false;
|
||||
|
||||
if (Source.ZipFileType != lcZipFileType::Count && mZipFiles[static_cast<int>(Source.ZipFileType)])
|
||||
{
|
||||
lcMemFile PieceFile;
|
||||
|
||||
if (mZipFiles[static_cast<int>(Source.ZipFileType)]->ExtractFile(Source.ZipFileIndex, PieceFile))
|
||||
Loaded = MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED);
|
||||
}
|
||||
else
|
||||
{
|
||||
lcDiskFile PieceFile;
|
||||
bool Loaded = false;
|
||||
|
||||
PieceFile.SetFileName(QDir(Source.LibraryDirectory).absoluteFilePath(QStringLiteral("parts/") + Source.FileName));
|
||||
|
||||
if (PieceFile.Open(QIODevice::ReadOnly))
|
||||
Loaded = MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED);
|
||||
|
||||
if (mHasUnofficial && !Loaded)
|
||||
if (Source.ZipFileType != lcZipFileType::Count && mZipFiles[static_cast<int>(Source.ZipFileType)])
|
||||
{
|
||||
Result.MeshData->Clear();
|
||||
lcMemFile PieceFile;
|
||||
|
||||
PieceFile.SetFileName(QDir(Source.LibraryDirectory).absoluteFilePath(QStringLiteral("unofficial/parts/") + Source.FileName));
|
||||
if (mZipFiles[static_cast<int>(Source.ZipFileType)]->ExtractFile(Source.ZipFileIndex, PieceFile))
|
||||
Loaded = MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED);
|
||||
}
|
||||
else
|
||||
{
|
||||
lcDiskFile PieceFile;
|
||||
|
||||
PieceFile.SetFileName(QDir(Source.LibraryDirectory).absoluteFilePath(QStringLiteral("parts/") + Source.FileName));
|
||||
|
||||
if (PieceFile.Open(QIODevice::ReadOnly))
|
||||
Loaded = MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED);
|
||||
|
||||
if (mHasUnofficial && !Loaded)
|
||||
{
|
||||
MeshData.Clear();
|
||||
|
||||
PieceFile.SetFileName(QDir(Source.LibraryDirectory).absoluteFilePath(QStringLiteral("unofficial/parts/") + Source.FileName));
|
||||
|
||||
if (PieceFile.Open(QIODevice::ReadOnly))
|
||||
Loaded = MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED);
|
||||
}
|
||||
}
|
||||
|
||||
if (mCancelLoading.load())
|
||||
Result.Error = tr("Library loading was cancelled.");
|
||||
else if (!Loaded)
|
||||
{
|
||||
Result.Error = tr("Could not load part %1.").arg(Source.FileName);
|
||||
|
||||
if (!MeshLoader.GetError().isEmpty())
|
||||
Result.Error += QLatin1Char('\n') + MeshLoader.GetError();
|
||||
}
|
||||
|
||||
if (!Result.Error.isEmpty())
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (mCancelLoading.load())
|
||||
Result.Error = tr("Library loading was cancelled.");
|
||||
else if (!Loaded)
|
||||
{
|
||||
Result.Error = tr("Could not load part %1.").arg(Source.FileName);
|
||||
Result.Mesh.reset(MeshData.CreateMesh(nullptr));
|
||||
|
||||
if (!MeshLoader.GetError().isEmpty())
|
||||
Result.Error += QLatin1Char('\n') + MeshLoader.GetError();
|
||||
}
|
||||
|
||||
if (!Result.Error.isEmpty())
|
||||
Result.MeshData.reset();
|
||||
|
||||
if (Result.MeshData)
|
||||
Result.MeshData->SetMeshLoader(nullptr);
|
||||
if (!Result.Mesh)
|
||||
Result.Error = tr("Could not prepare piece %1 for rendering.").arg(Source.FileName);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
+1
-1
@@ -222,7 +222,7 @@ public:
|
||||
void ClearPieceLoadError(const PieceInfo* Info);
|
||||
bool LoadBuiltinPieces();
|
||||
lcPieceSourceSnapshot SnapshotPieceSource(const PieceInfo* Info) const;
|
||||
// Worker build service: returns private CPU data/errors without publishing asset state.
|
||||
// Worker build service: returns a converted private mesh or an error without publishing asset state.
|
||||
lcPieceBuildResult BuildPieceData(const lcPieceSourceSnapshot& Source);
|
||||
void SaveBuiltPieceCache(const lcPieceSourceSnapshot& Source, lcMesh& Mesh);
|
||||
|
||||
|
||||
+7
-9
@@ -4,6 +4,7 @@
|
||||
#include "lc_application.h"
|
||||
#include "lc_file.h"
|
||||
#include "lc_meshloader.h"
|
||||
#include "lc_mesh.h"
|
||||
#include "pieceinf.h"
|
||||
#include <locale.h>
|
||||
|
||||
@@ -1591,7 +1592,7 @@ void lcSynthInfoUniversalJoint::AddParts(lcMemFile& File, lcLibraryMeshData&, co
|
||||
File.WriteBuffer(Line, strlen(Line));
|
||||
}
|
||||
|
||||
std::unique_ptr<lcLibraryMeshData> lcSynthInfo::BuildMeshData(const std::vector<lcPieceControlPoint>& ControlPoints, const std::atomic_bool& Cancelled) const
|
||||
std::unique_ptr<lcMesh> lcSynthInfo::BuildMesh(const std::vector<lcPieceControlPoint>& ControlPoints, const std::atomic_bool& Cancelled) const
|
||||
{
|
||||
if (Cancelled.load(std::memory_order_relaxed))
|
||||
return nullptr;
|
||||
@@ -1603,10 +1604,10 @@ std::unique_ptr<lcLibraryMeshData> lcSynthInfo::BuildMeshData(const std::vector<
|
||||
if (Cancelled.load(std::memory_order_relaxed))
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<lcLibraryMeshData> MeshData(new lcLibraryMeshData);
|
||||
lcLibraryMeshData MeshData;
|
||||
lcMemFile File; // todo: rewrite this to pass the parts directly
|
||||
|
||||
AddParts(File, *MeshData, Sections, &Cancelled);
|
||||
AddParts(File, MeshData, Sections, &Cancelled);
|
||||
|
||||
if (Cancelled.load(std::memory_order_relaxed))
|
||||
return nullptr;
|
||||
@@ -1614,14 +1615,11 @@ std::unique_ptr<lcLibraryMeshData> lcSynthInfo::BuildMeshData(const std::vector<
|
||||
File.WriteU8(0);
|
||||
File.Seek(0, SEEK_SET);
|
||||
|
||||
lcMeshLoader MeshLoader(*MeshData, nullptr, lcMeshLoaderFlag::RequireAllIncludes);
|
||||
lcMeshLoader MeshLoader(MeshData, nullptr, lcMeshLoaderFlag::RequireAllIncludes);
|
||||
MeshLoader.SetCancellationFlag(&Cancelled);
|
||||
|
||||
if (MeshLoader.LoadMesh(File, LC_MESHDATA_SHARED) && !MeshData->IsEmpty() && !Cancelled.load(std::memory_order_relaxed))
|
||||
{
|
||||
MeshData->SetMeshLoader(nullptr);
|
||||
return MeshData;
|
||||
}
|
||||
if (MeshLoader.LoadMesh(File, LC_MESHDATA_SHARED) && !MeshData.IsEmpty() && !Cancelled.load(std::memory_order_relaxed))
|
||||
return std::unique_ptr<lcMesh>(MeshData.CreateMesh(&Cancelled));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@
|
||||
#include "piece.h"
|
||||
|
||||
class lcLibraryMeshData;
|
||||
class lcMesh;
|
||||
|
||||
class lcSynthInfo
|
||||
{
|
||||
@@ -39,7 +40,7 @@ public:
|
||||
virtual void GetDefaultControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const = 0;
|
||||
virtual void VerifyControlPoints(std::vector<lcPieceControlPoint>& ControlPoints) const = 0;
|
||||
int InsertControlPoint(std::vector<lcPieceControlPoint>& ControlPoints, const lcVector3& Start, const lcVector3& End) const;
|
||||
std::unique_ptr<lcLibraryMeshData> BuildMeshData(const std::vector<lcPieceControlPoint>& ControlPoints, const std::atomic_bool& Cancelled) const;
|
||||
std::unique_ptr<lcMesh> BuildMesh(const std::vector<lcPieceControlPoint>& ControlPoints, const std::atomic_bool& Cancelled) const;
|
||||
|
||||
protected:
|
||||
using SectionCallbackFunc = std::function<void(const lcVector3& CurvePoint, quint32 SegmentIndex, float t)>;
|
||||
|
||||
+10
-11
@@ -229,13 +229,13 @@ flowchart LR
|
||||
end
|
||||
```
|
||||
|
||||
The diagram shows the normal paths. Mesh parsing and conversion are separate
|
||||
worker phases, so a part can return to `Queued` between phases. Invalidation or
|
||||
unloading resets readiness; cancellation and stale-result disposal also have
|
||||
request-level terminal flags, independent of the published asset state.
|
||||
Queued piece and synth requests use the presence of `MeshData` to select
|
||||
conversion; an empty pointer selects parsing or generation. `AwaitingCompletion` tracks
|
||||
outstanding worker phases through completion processing.
|
||||
The diagram shows the normal paths. Parsing or synth generation and geometry
|
||||
conversion run consecutively in one worker job. Intermediate `lcLibraryMeshData`
|
||||
stays local to the library's `BuildPieceData()` or the synth definition's `BuildMesh()`;
|
||||
both return a private converted mesh for UI-thread finalization.
|
||||
Invalidation or unloading resets readiness; cancellation and stale-result disposal
|
||||
also have request-level terminal flags, independent of the published asset state.
|
||||
`AwaitingCompletion` tracks an outstanding worker job through completion processing.
|
||||
|
||||
A part becomes `Loaded` after its private mesh is committed and all declared
|
||||
textures are ready. Valid empty geometry may also settle successfully. A missing
|
||||
@@ -296,8 +296,7 @@ sequenceDiagram
|
||||
Library->>Library: Acquire consumer reference
|
||||
Library->>Loader: Queue/promote
|
||||
Loader->>Worker: Immutable source snapshot
|
||||
Worker-->>Loader: Private mesh data or cached mesh
|
||||
Loader->>Worker: Convert source mesh data
|
||||
Worker->>Worker: Parse/generate and convert, or read cached mesh
|
||||
Worker-->>Loader: Private mesh
|
||||
Loader->>Loader: Set section colors, update flags, and sort sections
|
||||
Loader->>Loader: Resolve textures and queue decode if needed
|
||||
@@ -316,7 +315,7 @@ indices. Fresh pieces, synths, and cached meshes all use
|
||||
`FinalizeSections()` on the UI thread to resolve color indices, refresh
|
||||
solid/translucent/default flags, and sort sections using the current palette.
|
||||
Workers build geometry without reading global colors or keeping palette snapshots.
|
||||
A corrupt mesh cache is rebuilt privately. Failed builds are not saved
|
||||
A corrupt mesh cache falls back to source parsing in the same worker job. Failed builds are not saved
|
||||
as successful mesh caches. Temporary disk textures are scoped by their resolved
|
||||
file identity, so same-name PNGs in different project directories remain distinct.
|
||||
|
||||
@@ -540,7 +539,7 @@ The preferences dialog saves a changed color-config path before this setter runs
|
||||
so simultaneous stud/color changes load the new configuration once. Color-only
|
||||
changes call `LoadColors()` directly.
|
||||
|
||||
Stud-style changes pause dispatch, finish running phases, discard staging built
|
||||
Stud-style changes pause dispatch, finish running jobs, discard staging built
|
||||
with old settings, refresh sources, and rebuild affected published meshes and
|
||||
generated geometry. The existing style-change path resumes and drains work.
|
||||
Published ordinary meshes are rebuilt selectively using `HasStyleStud`, including
|
||||
|
||||
Reference in new issue
Block a user