From 64354806bdbfd42827c61b8b1dd9f0608f490339 Mon Sep 17 00:00:00 2001 From: Eragon-Brisingr <450614754@qq.com> Date: Tue, 30 Jun 2026 16:30:49 +0800 Subject: [PATCH] Init repo --- .../Private/ShaderLabMaterialRegistry.cpp | 23 +++++++++++-------- .../Public/ShaderLabMaterialRegistry.h | 6 ++--- .../Private/ShaderLabEditorModule.cpp | 11 ++++++++- .../ShaderLabMaterialInstanceFactory.cpp | 8 +++---- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/Source/UShaderLab/Private/ShaderLabMaterialRegistry.cpp b/Source/UShaderLab/Private/ShaderLabMaterialRegistry.cpp index 52db649..9ebc870 100644 --- a/Source/UShaderLab/Private/ShaderLabMaterialRegistry.cpp +++ b/Source/UShaderLab/Private/ShaderLabMaterialRegistry.cpp @@ -35,7 +35,7 @@ UPackage* FShaderLabMaterialRegistry::GetPackage() return Package; } -FString FShaderLabMaterialRegistry::MakeObjectName(const FString& ShaderName) +FName FShaderLabMaterialRegistry::MakeObjectName(const FString& ShaderName) { FString Out; Out.Reserve(ShaderName.Len()); @@ -47,12 +47,12 @@ FString FShaderLabMaterialRegistry::MakeObjectName(const FString& ShaderName) { Out = TEXT("Unnamed"); } - return Out; + return FName(*Out); } FString FShaderLabMaterialRegistry::MakeObjectPath(const FString& ShaderName) { - return FString::Printf(TEXT("%s.%s"), GShaderLabPackageName, *MakeObjectName(ShaderName)); + return FString::Printf(TEXT("%s.%s"), GShaderLabPackageName, *MakeObjectName(ShaderName).ToString()); } UMaterial* FShaderLabMaterialRegistry::FindMaterial(const FString& ShaderName) const @@ -61,28 +61,29 @@ UMaterial* FShaderLabMaterialRegistry::FindMaterial(const FString& ShaderName) c { return nullptr; } - return FindObject(Package, *MakeObjectName(ShaderName)); + return FindObject(Package, *MakeObjectName(ShaderName).ToString()); } UMaterial* FShaderLabMaterialRegistry::RegisterFromModel(const FShaderLabModel& Model) { UPackage* Pkg = GetPackage(); - const FString ObjName = MakeObjectName(Model.ShaderName); + const FName ObjName = MakeObjectName(Model.ShaderName); // Contract: a material's identity is its file name. Two different source files that sanitize to the // same object name are a hard error (would otherwise silently clobber each other / break MIC refs). + // The key is an FName, so the collision check is case-insensitive — matching UObject name semantics. if (const FShaderLabModel* Existing = RegisteredModels.Find(ObjName)) { if (!FPaths::IsSamePath(Existing->SourceFilePath, Model.SourceFilePath)) { UE_LOG(LogShaderLabRegistry, Error, TEXT("ShaderLab: duplicate material name '%s' from '%s' and '%s' — rename one file."), - *ObjName, *Existing->SourceFilePath, *Model.SourceFilePath); + *ObjName.ToString(), *Existing->SourceFilePath, *Model.SourceFilePath); return nullptr; } } - UMaterial* Material = FindObject(Pkg, *ObjName); + UMaterial* Material = FindObject(Pkg, *ObjName.ToString()); const bool bNewlyCreated = (Material == nullptr); if (bNewlyCreated) { @@ -90,16 +91,18 @@ UMaterial* FShaderLabMaterialRegistry::RegisterFromModel(const FShaderLabModel& // (RF_Standalone alone is not a GC root in a cooked runtime), which removes its entry from // the IoStore script-object table (RemoveUnreachableScriptObjects) and makes cooked Material // Instances that reference it resolve their Parent to null -> fall back to the default material. - Material = NewObject(Pkg, FName(*ObjName), RF_Public | RF_Standalone | RF_MarkAsRootSet); + Material = NewObject(Pkg, ObjName, RF_Public | RF_Standalone | RF_MarkAsRootSet); } RegisteredModels.FindOrAdd(ObjName) = Model; // Register with the loading system so imports to "/Script/UShaderLab." from other - // packages resolve to this in-memory object even if requested mid async-load. + // packages resolve to this in-memory object — required for cooked builds, where the linker + // resolves a Material Instance's parent import through this registration (without it the parent + // resolves to null and the instance falls back to the default material). NotifyRegistrationEvent( FName(GShaderLabPackageName), - FName(*ObjName), + ObjName, ENotifyRegistrationType::NRT_NoExportObject, ENotifyRegistrationPhase::NRP_Finished, nullptr, diff --git a/Source/UShaderLab/Public/ShaderLabMaterialRegistry.h b/Source/UShaderLab/Public/ShaderLabMaterialRegistry.h index fa94065..29be3d2 100644 --- a/Source/UShaderLab/Public/ShaderLabMaterialRegistry.h +++ b/Source/UShaderLab/Public/ShaderLabMaterialRegistry.h @@ -31,7 +31,7 @@ public: UPackage* GetPackage(); /** Deterministic, UObject-safe object name for a shader name like "ShaderLab/RustyMetal". */ - static FString MakeObjectName(const FString& ShaderName); + static FName MakeObjectName(const FString& ShaderName); /** Full object path, e.g. "/Script/UShaderLab.ShaderLab_RustyMetal". */ static FString MakeObjectPath(const FString& ShaderName); @@ -48,10 +48,10 @@ public: FOnShaderLabBuildMaterial& OnBuildMaterial() { return BuildMaterialDelegate; } /** Models registered so far, keyed by object name (used by the instance factory picker and the example/validation commandlets). */ - const TMap& GetRegisteredModels() const { return RegisteredModels; } + const TMap& GetRegisteredModels() const { return RegisteredModels; } private: UPackage* Package = nullptr; FOnShaderLabBuildMaterial BuildMaterialDelegate; - TMap RegisteredModels; + TMap RegisteredModels; }; diff --git a/Source/UShaderLabEditor/Private/ShaderLabEditorModule.cpp b/Source/UShaderLabEditor/Private/ShaderLabEditorModule.cpp index 7175bce..03d8372 100644 --- a/Source/UShaderLabEditor/Private/ShaderLabEditorModule.cpp +++ b/Source/UShaderLabEditor/Private/ShaderLabEditorModule.cpp @@ -4,6 +4,7 @@ #include "Engine/Engine.h" #include "IDirectoryWatcher.h" #include "Interfaces/IPluginManager.h" +#include "MaterialShared.h" #include "Materials/Material.h" #include "Misc/Paths.h" #include "Modules/ModuleManager.h" @@ -31,7 +32,15 @@ namespace } return; } - // Recompile for rendering / editor preview (async). + + // Recompile the base AND propagate to everything that depends on it. A bare PostEditChange + // recompiles only the base shell; the instances carry their own static-permutation shader maps + // (and the level components render those), so on a hot-reload they must be recompiled and + // re-pushed too. FMaterialUpdateContext does exactly that on scope exit: recompile the listed + // material, then recache every dependent instance and refresh the components/viewports using it. + FMaterialUpdateContext UpdateContext; + UpdateContext.AddMaterial(&Material); + Material.PreEditChange(nullptr); Material.PostEditChange(); } } diff --git a/Source/UShaderLabEditor/Private/ShaderLabMaterialInstanceFactory.cpp b/Source/UShaderLabEditor/Private/ShaderLabMaterialInstanceFactory.cpp index 3465989..2af81d3 100644 --- a/Source/UShaderLabEditor/Private/ShaderLabMaterialInstanceFactory.cpp +++ b/Source/UShaderLabEditor/Private/ShaderLabMaterialInstanceFactory.cpp @@ -30,12 +30,12 @@ namespace // Collect the registered in-memory base materials (sorted by name for a stable layout). TArray Bases; - TArray Names; + TArray Names; Registry.GetRegisteredModels().GenerateKeyArray(Names); - Names.Sort(); - for (const FString& Name : Names) + Names.Sort([](const FName& A, const FName& B) { return A.Compare(B) < 0; }); + for (const FName& Name : Names) { - if (UMaterial* Base = Registry.FindMaterial(Name)) + if (UMaterial* Base = Registry.FindMaterial(Name.ToString())) { Bases.Add(Base); }