Simplify texture sources and remove unused loading paths

The direct texture-loading overloads and synchronous library/manager chain
have no callers. Remove them and the filename image-loading overload so
texture requests use the existing loader path.

Replace the texture's separate source fields with a shared lcFileSource
value in its own header. Keep source identity comparisons unchanged,
including content-based embedded texture checks during project merging.
The loader copies the source directly into its request, eliminating the
library and manager snapshot forwarding methods. Preserve the library
header's regrouping and update the asset-loading documentation.

Validated the debug build, file ownership/cursor/error checks, loader
failure and retry behavior, 800 concurrent archive reads, and matching
embedded/disk texture renders.
This commit is contained in:
Leonardo Zide committed 2026-10-12 12:15:48 -07:00
1 parent 1098c2de75
commit 05ba97dd88
14 files changed
+73 -198

No files matched your search

-12
View File
@@ -199,15 +199,3 @@ bool Image::FileLoad(lcFile& File)
return true;
}
bool Image::FileLoad(const QString& FileName)
{
QImage Image;
if (!Image.load(FileName))
return false;
CopyFromQImage(Image, *this);
return true;
}
-1
View File
@@ -29,7 +29,6 @@ public:
// Reads disk or memory files from the current position, preserving the cursor.
bool FileLoad(lcFile& File);
bool FileLoad(const QString& FileName);
void Resize(int Width, int Height);
void ResizePow2();
+1 -1
View File
@@ -508,7 +508,7 @@ void lcAssetLoader::QueueTexture(lcTexture* Texture, Priority LoadPriority)
RequestedTexture->Texture = Texture;
RequestedTexture->Generation = mGeneration;
RequestedTexture->Source = mLibrary->SnapshotTextureSource(Texture);
RequestedTexture->Source = Texture->mSource;
RequestedTexture->LoadPriority = LoadPriority;
RequestedTexture->EnqueuedAt = QDateTime::currentMSecsSinceEpoch();
+1 -1
View File
@@ -118,7 +118,7 @@ private:
{
lcTexture* Texture;
quint64 Generation;
lcFileSourceSnapshot Source;
lcFileSource Source;
Priority LoadPriority;
qint64 EnqueuedAt;
bool Running = false;
+23
View File
@@ -0,0 +1,23 @@
#pragma once
struct lcEmbeddedData;
// A resolved disk path, archive entry, or embedded file. Copying it does not
// snapshot file contents; workers open their own file from the copied source.
struct lcFileSource
{
bool operator==(const lcFileSource& Other) const
{
return EmbeddedData == Other.EmbeddedData && FilePath == Other.FilePath && ArchiveType == Other.ArchiveType && ArchiveIndex == Other.ArchiveIndex;
}
bool operator!=(const lcFileSource& Other) const
{
return !(*this == Other);
}
std::shared_ptr<const lcEmbeddedData> EmbeddedData;
QString FilePath;
int ArchiveType = -1;
int ArchiveIndex = -1;
};
+5 -29
View File
@@ -585,7 +585,7 @@ QString lcPiecesLibrary::FindProjectTextureFile(const QString& ProjectPath, cons
return QString();
}
lcFileSourceSnapshot lcPiecesLibrary::ResolveTextureSource(const char* TextureName, const std::vector<QString>& SearchDirectories, const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData, QString& ProjectPath)
lcFileSource lcPiecesLibrary::ResolveTextureSource(const char* TextureName, const std::vector<QString>& SearchDirectories, const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData, QString& ProjectPath)
{
ProjectPath.clear();
@@ -595,7 +595,7 @@ lcFileSourceSnapshot lcPiecesLibrary::ResolveTextureSource(const char* TextureNa
QMutexLocker LoadLock(&mLoadMutex);
const bool ArchiveLibrary = static_cast<bool>(mZipFiles[static_cast<int>(lcZipFileType::Official)]);
lcFileSourceSnapshot Source;
lcFileSource Source;
const QString Name = QString::fromLatin1(TextureName);
const QString Candidates[] = { QLatin1String("textures/") + Name, Name };
const QString Folders[] = { QStringLiteral("p/"), QStringLiteral("parts/"), QStringLiteral("models/"), QStringLiteral("unofficial/p/"), QStringLiteral("unofficial/parts/") };
@@ -682,17 +682,12 @@ lcFileSourceSnapshot lcPiecesLibrary::ResolveTextureSource(const char* TextureNa
lcTexture* lcPiecesLibrary::FindTextureDeferred(const char* TextureName, const std::vector<QString>& SearchDirectories, const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData)
{
QString ProjectPath;
const lcFileSourceSnapshot Source = ResolveTextureSource(TextureName, SearchDirectories, EmbeddedData, ProjectPath);
const lcFileSource Source = ResolveTextureSource(TextureName, SearchDirectories, EmbeddedData, ProjectPath);
return mTextureManager->AcquireTexture(TextureName, Source, ProjectPath);
}
lcFileSourceSnapshot lcPiecesLibrary::SnapshotTextureSource(const lcTexture* Texture) const
{
return mTextureManager->SnapshotTextureSource(Texture);
}
lcTextureBuildResult lcPiecesLibrary::BuildTextureData(const lcFileSourceSnapshot& Source)
lcTextureBuildResult lcPiecesLibrary::BuildTextureData(const lcFileSource& Source)
{
const lcResult<std::unique_ptr<lcFile>> File = OpenFile(Source);
@@ -712,7 +707,7 @@ lcTextureBuildResult lcPiecesLibrary::BuildTextureData(const lcFileSourceSnapsho
return Result;
}
lcResult<std::unique_ptr<lcFile>> lcPiecesLibrary::OpenFile(const lcFileSourceSnapshot& Source)
lcResult<std::unique_ptr<lcFile>> lcPiecesLibrary::OpenFile(const lcFileSource& Source)
{
if (Source.EmbeddedData)
{
@@ -2275,25 +2270,6 @@ void lcPiecesLibrary::UnloadUnusedParts()
}
}
bool lcPiecesLibrary::LoadTexture(lcTexture* Texture)
{
const lcFileSourceSnapshot Source = SnapshotTextureSource(Texture);
const lcResult<std::unique_ptr<lcFile>> File = OpenFile(Source);
if (!File)
{
Texture->SetFailed(lcTextureLoadError::DecodeFailed, File.error());
return false;
}
const bool Loaded = mTextureManager->LoadTexture(Texture, *File.value());
if (!Loaded && Source.EmbeddedData)
Texture->SetFailed(Texture->mLoadFailure, tr("Could not decode embedded texture '%1'.").arg(Source.EmbeddedData->FileName));
return Loaded;
}
void lcPiecesLibrary::AddTextureReference(lcTexture* Texture)
{
mTextureManager->AddTextureReference(Texture);
+13 -22
View File
@@ -4,6 +4,7 @@
#include "lc_math.h"
#include "lc_meshloader.h"
#include "lc_result.h"
#include "lc_filesource.h"
class PieceInfo;
class lcPiece;
@@ -20,15 +21,6 @@ struct lcPartBuildResult;
struct lcEmbeddedData;
struct lcTextureBuildResult;
// Copied before dispatch; opening a source never consults live project state.
struct lcFileSourceSnapshot
{
std::shared_ptr<const lcEmbeddedData> EmbeddedData;
QString FilePath;
int ArchiveType = -1;
int ArchiveIndex = -1;
};
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
using lcLibraryLoadMutex = QRecursiveMutex;
#else
@@ -243,7 +235,6 @@ public:
lcTexture* FindTexture(const char* TextureName, Project* CurrentProject, bool SearchProjectFolder);
// UI-thread lookup/acquisition only, without decoding or upload; caller releases the reference.
lcTexture* FindTextureDeferred(const char* TextureName, const std::vector<QString>& SearchDirectories, const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData);
bool LoadTexture(lcTexture* Texture);
void ReleaseTexture(lcTexture* Texture);
bool PieceInCategory(PieceInfo* Info, const char* CategoryKeywords) const;
@@ -257,7 +248,7 @@ public:
// Opens a resolved source on the reading thread, positioned at zero.
// Caller owns the file; archive replacement requires draining workers first.
lcResult<std::unique_ptr<lcFile>> OpenFile(const lcFileSourceSnapshot& Source);
lcResult<std::unique_ptr<lcFile>> OpenFile(const lcFileSource& Source);
void GetPrimitiveFile(lcLibraryPrimitive* Primitive, std::function<void(lcFile& File)> Callback);
void GetPieceFile(const char* FileName, std::function<void(lcFile& File)> Callback);
@@ -305,14 +296,6 @@ signals:
void GeneratedMeshSettled(PieceInfo* Info);
void ColorsLoaded();
private:
lcFileSourceSnapshot SnapshotTextureSource(const lcTexture* Texture) const;
// Worker decode service: returns private pixels/errors without GL upload or publication.
lcTextureBuildResult BuildTextureData(const lcFileSourceSnapshot& Source);
void AddTextureReference(lcTexture* Texture);
static QString FindProjectTextureFile(const QString& ProjectPath, const QString& TextureName);
lcFileSourceSnapshot ResolveTextureSource(const char* TextureName, const std::vector<QString>& SearchDirectories, const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData, QString& ProjectPath);
protected:
friend class lcAssetLoader;
@@ -341,11 +324,15 @@ protected:
void UnloadPieceInfo(PieceInfo* Info);
void RegisterProjectPiece(Project* Project, const std::string& Name, PieceInfo* Info);
// Worker decode service: returns private pixels/errors without GL upload or publication.
lcTextureBuildResult BuildTextureData(const lcFileSource& Source);
void AddTextureReference(lcTexture* Texture);
static QString FindProjectTextureFile(const QString& ProjectPath, const QString& TextureName);
lcFileSource ResolveTextureSource(const char* TextureName, const std::vector<QString>& SearchDirectories, const std::map<QString, std::shared_ptr<const lcEmbeddedData>>& EmbeddedData, QString& ProjectPath);
void ReleaseBuffers();
std::vector<std::unique_ptr<lcLibrarySource>> mSources;
std::map<QString, QString> mTextureDiskEntries;
std::map<QString, int> mTextureArchiveEntries[static_cast<int>(lcZipFileType::Count)];
// Each project maps its local filenames to PieceInfo pointers. This map tracks the
// project for each local PieceInfo, or nullptr after that project is destroyed.
std::unordered_map<PieceInfo*, Project*> mProjectPieces;
@@ -367,9 +354,13 @@ protected:
lcStudStyle mStudStyle;
bool mStudCylinderColorEnabled;
std::unique_ptr<lcTextureManager> mTextureManager;
std::unique_ptr<lcAssetLoader> mAssetLoader;
std::unique_ptr<lcThumbnailManager> mThumbnailManager;
std::unique_ptr<lcTextureManager> mTextureManager;
std::map<QString, QString> mTextureDiskEntries;
std::map<QString, int> mTextureArchiveEntries[static_cast<int>(lcZipFileType::Count)];
std::unique_ptr<lcMesh> mLoadingMesh;
bool mStreamingBuffersPending = false;
bool mBufferRepackScheduled = false;
+1 -59
View File
@@ -1,33 +1,11 @@
#include "lc_global.h"
#include "lc_texture.h"
#include "lc_application.h"
#include "lc_library.h"
#include "lc_file.h"
#include "lc_context.h"
#include "image.h"
#include "lc_glextensions.h"
#include "lc_string.h"
lcTexture* gGridTexture;
lcTexture* lcLoadTexture(const QString& FileName, int Flags)
{
lcTexture* Texture = new lcTexture();
if (!Texture->Load(FileName, Flags))
{
delete Texture;
Texture = nullptr;
}
else
{
lcstrcpy(Texture->mName, QFileInfo(FileName).baseName().toLatin1());
Texture->mFilePath = FileName;
Texture->SetTemporary(true);
}
return Texture;
}
lcTexture::lcTexture(int Flags)
: mFlags(Flags)
{
@@ -160,42 +138,6 @@ void lcTexture::CreateGridTexture()
Upload(Context);
}
bool lcTexture::Load()
{
return lcGetPiecesLibrary()->LoadTexture(this);
}
bool lcTexture::Load(const QString& FileName, int Flags)
{
Image Image;
if (!Image.FileLoad(FileName))
{
SetFailed(lcTextureLoadError::DecodeFailed, QString());
return false;
}
SetImage(std::move(Image), Flags);
return true;
}
bool lcTexture::Load(lcMemFile& File, int Flags)
{
Image Image;
if (!Image.FileLoad(File))
{
SetFailed(lcTextureLoadError::DecodeFailed, QString());
return false;
}
SetImage(std::move(Image), Flags);
return true;
}
void lcTexture::SetImage(Image&& Image, int Flags)
{
mImages.clear();
+7 -16
View File
@@ -16,6 +16,7 @@
#define LC_TEXTURE_NAME_LEN 256
#include "image.h"
#include "lc_filesource.h"
enum class lcTextureState
{
@@ -37,8 +38,6 @@ enum class lcTextureLoadError
UploadFailed
};
struct lcEmbeddedData;
class lcTexture
{
public:
@@ -52,8 +51,6 @@ public:
void CreateGridTexture();
bool Load(const QString& FileName, int Flags = 0);
bool Load(lcMemFile& File, int Flags = 0);
void SetImage(Image&& Image, int Flags = 0);
void SetImage(std::vector<Image>&& Images, int Flags = 0);
void AdoptDecodedImage(Image&& Image, int Flags = 0);
@@ -74,11 +71,6 @@ public:
mRefCount.ref();
}
int GetRefCount() const
{
return mRefCount.loadRelaxed();
}
// Reference bookkeeping belongs to lcTextureManager.
bool Release()
{
@@ -90,6 +82,11 @@ public:
return InUse;
}
int GetRefCount() const
{
return mRefCount.loadRelaxed();
}
void SetTemporary(bool Temporary)
{
mTemporary = Temporary;
@@ -128,18 +125,14 @@ public:
int mWidth;
int mHeight;
char mName[LC_TEXTURE_NAME_LEN] = {};
std::shared_ptr<const lcEmbeddedData> mEmbeddedData;
QString mFilePath; // Absolute PNG path for disk-backed textures; empty for archive or embedded textures.
lcFileSource mSource;
QString mProjectPath; // Directory used to scope temporary textures to their project.
int mArchiveType = -1;
int mArchiveIndex = -1;
GLuint mTexture = 0;
lcTextureState mState = lcTextureState::Unrequested;
lcTextureLoadError mLoadFailure = lcTextureLoadError::None;
QString mLoadFailureDetails;
protected:
bool Load();
bool LoadImages();
bool mTemporary = false;
@@ -148,6 +141,4 @@ protected:
int mFlags = 0;
};
lcTexture* lcLoadTexture(const QString& FileName, int Flags);
extern lcTexture* gGridTexture;
+3 -34
View File
@@ -1,6 +1,5 @@
#include "lc_global.h"
#include "lc_texturemanager.h"
#include "lc_library.h"
#include "lc_file.h"
#include "lc_string.h"
@@ -28,7 +27,7 @@ void lcTextureManager::AddTextureReference(lcTexture* Texture)
Texture->AddRef();
}
lcTexture* lcTextureManager::AcquireTexture(const char* TextureName, const lcFileSourceSnapshot& Source, const QString& ProjectPath)
lcTexture* lcTextureManager::AcquireTexture(const char* TextureName, const lcFileSource& Source, const QString& ProjectPath)
{
if (!TextureName[0] || strlen(TextureName) >= LC_TEXTURE_NAME_LEN)
return nullptr;
@@ -40,7 +39,7 @@ lcTexture* lcTextureManager::AcquireTexture(const char* TextureName, const lcFil
for (lcTexture* Texture : mTextures)
{
if (Texture->mEmbeddedData != Source.EmbeddedData || Texture->mFilePath != Source.FilePath || Texture->mArchiveType != Source.ArchiveType || Texture->mArchiveIndex != Source.ArchiveIndex)
if (Texture->mSource != Source)
continue;
Texture->AddRef();
@@ -51,11 +50,8 @@ lcTexture* lcTextureManager::AcquireTexture(const char* TextureName, const lcFil
lcTexture* Texture = new lcTexture(LC_TEXTURE_MIPMAPS);
lcstrcpy(Texture->mName, TextureName);
Texture->mEmbeddedData = Source.EmbeddedData;
Texture->mFilePath = Source.FilePath;
Texture->mSource = Source;
Texture->mProjectPath = ProjectPath;
Texture->mArchiveType = Source.ArchiveType;
Texture->mArchiveIndex = Source.ArchiveIndex;
Texture->SetTemporary(bool(Source.EmbeddedData) || !ProjectPath.isEmpty());
Texture->AddRef();
@@ -64,18 +60,6 @@ lcTexture* lcTextureManager::AcquireTexture(const char* TextureName, const lcFil
return Texture;
}
lcFileSourceSnapshot lcTextureManager::SnapshotTextureSource(const lcTexture* Texture) const
{
lcFileSourceSnapshot Source;
Source.EmbeddedData = Texture->mEmbeddedData;
Source.FilePath = Texture->mFilePath;
Source.ArchiveType = Texture->mArchiveType;
Source.ArchiveIndex = Texture->mArchiveIndex;
return Source;
}
lcTextureBuildResult lcTextureManager::BuildTextureData(lcFile& File)
{
lcTextureBuildResult Result = DecodeTextureData(File);
@@ -101,21 +85,6 @@ lcTextureBuildResult lcTextureManager::DecodeTextureData(lcFile& File)
return Result;
}
bool lcTextureManager::LoadTexture(lcTexture* Texture, lcFile& File)
{
lcTextureBuildResult Result = DecodeTextureData(File);
if (!Result.DecodedImage)
{
Texture->SetFailed(Result.Error, std::move(Result.ErrorDetails));
return false;
}
Texture->SetImage(std::move(*Result.DecodedImage));
return true;
}
void lcTextureManager::ReleaseTexture(lcTexture* Texture)
{
QMutexLocker Lock(&mMutex);
+1 -7
View File
@@ -3,7 +3,6 @@
#include "lc_texture.h"
class lcFile;
struct lcFileSourceSnapshot;
struct lcTextureBuildResult
{
@@ -37,19 +36,14 @@ public:
void Clear();
// UI-thread acquisition only; each returned reference must be released.
lcTexture* AcquireTexture(const char* Name, const lcFileSourceSnapshot& Source, const QString& ProjectPath);
lcTexture* AcquireTexture(const char* Name, const lcFileSource& Source, const QString& ProjectPath);
void AddTextureReference(lcTexture* Texture);
void ReleaseTexture(lcTexture* Texture);
lcFileSourceSnapshot SnapshotTextureSource(const lcTexture* Texture) const;
// Worker-safe private CPU build; never publishes or uploads the result.
lcTextureBuildResult BuildTextureData(lcFile& File);
// Existing synchronous path; SetImage retains its thread-dependent upload behavior.
bool LoadTexture(lcTexture* Texture, lcFile& File);
private:
lcTextureBuildResult DecodeTextureData(lcFile& File);
+3 -4
View File
@@ -1458,11 +1458,10 @@ lcResult<void> Project::ValidateMergedTextureBindings(const Project& Source, con
lcTexture* After = Library->FindTextureDeferred(Name.c_str(), AfterDirectories, MergedData);
bool Same = After != nullptr;
if (Same && Before->mEmbeddedData && After->mEmbeddedData)
Same = SameEmbeddedData(*Before->mEmbeddedData, *After->mEmbeddedData);
if (Same && Before->mSource.EmbeddedData && After->mSource.EmbeddedData)
Same = SameEmbeddedData(*Before->mSource.EmbeddedData, *After->mSource.EmbeddedData);
else if (Same)
Same = Before->mEmbeddedData == After->mEmbeddedData && Before->mFilePath == After->mFilePath &&
Before->mArchiveType == After->mArchiveType && Before->mArchiveIndex == After->mArchiveIndex;
Same = Before->mSource == After->mSource;
Library->ReleaseTexture(Before);
+14 -12
View File
@@ -93,16 +93,17 @@ indexed library files in the library, then asks the manager to acquire an identi
for that resolved source. Resolution preserves the prefixed search order and
canonicalizes disk paths. The project directory is passed separately to acquisition
so the manager preserves temporary-texture metadata without searching files itself.
Library texture APIs forward reference changes, snapshots, decoding, and the
existing synchronous loading path to the manager. `FindTexture()` still gathers
the explicit project's context and waits through the loader. Texture requests,
Library texture APIs forward reference changes and decoding to the manager.
`FindTexture()` still gathers the explicit project's context and waits through
the loader. Texture requests,
priority promotion, cancellation, upload/retries, and part dependency settlement
remain in `lcAssetLoader`; its in-flight texture references are acquired through
the manager. `lcFileSourceSnapshot` lives in `lc_library.h`; texture build results live
in `lc_texturemanager.h`. File snapshots contain no project/texture metadata.
The library's `SnapshotTextureSource()`, `BuildTextureData()`, and
`AddTextureReference()` are private loader services, accessible through the
existing `lcAssetLoader` friendship.
the manager. `lcFileSource` lives in `lc_filesource.h`; texture build results live
in `lc_texturemanager.h`. `lcTexture::mSource` stores this value; copies for workers do not snapshot file
contents. Texture names, project directories, and temporary flags remain separate.
The loader copies `lcTexture::mSource` into each texture request. The library's
`BuildTextureData()` and `AddTextureReference()` are internal loader services,
accessible through the existing `lcAssetLoader` friendship.
The library calls `OpenFile()` on the reading thread and passes the opened
`lcFile&` to the texture manager for decoding. `BuildTextureData()` continues to
@@ -126,12 +127,13 @@ memory files through `QImage::loadFromData()`, without an additional device adap
or disk-to-memory copy. Disk cursor restoration runs on success and failure;
memory decoding leaves the cursor unchanged and validates the remaining buffer
length. Qt handles disk reads, seeking, EOF, and I/O errors. Files/cursors are
private to each request, not shared between workers. The filename-based image
entry point retains its existing behavior.
private to each request, not shared between workers. Texture readiness waits use
the same loader worker path; unused direct texture-loading overloads and their
synchronous library/manager forwarding chain have been removed.
`lcTexture::SetFailed()` assigns readiness and failure diagnostics together,
clearing stale details when none are supplied. Synchronous loading and loader
failures use the same setter. It retains decoded pixels for bounded upload
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.
`FindPiece()` returns a borrowed identity, without acquiring a reference. Lookup
+1
View File
@@ -283,6 +283,7 @@ HEADERS += \
common/lc_edgecolordialog.h \
common/lc_editgroupsdialog.h \
common/lc_file.h \
common/lc_filesource.h \
common/lc_filter.h \
common/lc_findreplacewidget.h \
common/lc_glextensions.h \