Enhance .usl compile error display

This commit is contained in:
Eragon-Brisingr
2026-07-01 18:23:46 +08:00
parent c21efdc64e
commit 0d7db515ae
6 changed files with 141 additions and 19 deletions

View File

@@ -1,9 +1,11 @@
// Copyright FlecsProj. All Rights Reserved.
#include "DirectoryWatcherModule.h"
#include "Editor.h"
#include "Engine/Engine.h"
#include "IDirectoryWatcher.h"
#include "Interfaces/IPluginManager.h"
#include "MaterialEditingLibrary.h"
#include "MaterialShared.h"
#include "Materials/Material.h"
#include "Misc/Paths.h"
@@ -23,14 +25,36 @@ namespace
/** Editor-side reaction to a base material needing build: rebuild graph + trigger compile. */
void BuildAndCompile(UMaterial& Material, const FShaderLabModel& Model)
{
TArray<FString> Errors;
if (!FShaderLabGraphBuilder::BuildInto(Material, Model, Errors))
// Absolute, forward-slashed source path for the poison node's `#line` mapping (matches the graph
// builder's own convention so compiler errors click through to the .usl).
FString SrcPath = FPaths::ConvertRelativePathToFull(Model.SourceFilePath);
SrcPath.ReplaceInline(TEXT("\\"), TEXT("/"));
if (Model.LoadErrors.Num() > 0)
{
for (const FString& E : Errors)
// Parse-stage failure: the .usl never produced a valid model. Route the diagnostics through
// the shader compiler as a poison material so they surface in the MIC editor / cook, not just
// the log.
for (const FString& E : Model.LoadErrors)
{
UE_LOG(LogShaderLabEditor, Error, TEXT("ShaderLab build '%s': %s"), *Model.ShaderName, *E);
UE_LOG(LogShaderLabEditor, Error, TEXT("ShaderLab parse '%s': %s"), *Model.ShaderName, *E);
}
FShaderLabGraphBuilder::BuildPoisonInto(Material, Model.LoadErrors, SrcPath);
}
else
{
TArray<FString> Errors;
if (!FShaderLabGraphBuilder::BuildInto(Material, Model, Errors))
{
for (const FString& E : Errors)
{
UE_LOG(LogShaderLabEditor, Error, TEXT("ShaderLab build '%s': %s"), *Model.ShaderName, *E);
}
// BuildInto already cleared the graph before failing — don't leave the base silently empty.
// Replace it with a poison material so the build errors compile-fail with the same
// visibility as an HLSL error, instead of vanishing into the log.
FShaderLabGraphBuilder::BuildPoisonInto(Material, Errors, SrcPath);
}
return;
}
// Recompile the base AND propagate to everything that depends on it. A bare PostEditChange
@@ -38,10 +62,23 @@ namespace
// (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();
{
FMaterialUpdateContext UpdateContext;
UpdateContext.AddMaterial(&Material);
Material.PreEditChange(nullptr);
Material.PostEditChange();
}
// Refresh any open Material Instance editors built on this base so newly added/removed parameters
// (scalars, textures, static-bool switches) appear without reopening the editor. RegenerateArrays
// pulls the parameter set from the freshly rebuilt parent; mirrors what the stock material editor
// does after RecompileMaterial. Must run after PostEditChange so the parent's parameters are current.
// Guarded on GEditor: this handler also runs during cook/headless registration, where there is no
// interactive editor (and RebuildMaterialInstanceEditors dereferences GEditor).
if (GEditor)
{
UMaterialEditingLibrary::RebuildMaterialInstanceEditors(&Material);
}
}
}
@@ -76,15 +113,6 @@ public:
StartWatchingSources();
// Note: ShaderLab base materials are deliberately NOT surfaced to the Content Browser — they are
// hidden templates. Users create UShaderLabMaterialInstanceConstant instances via the factory
// (Content Browser ▸ Material ▸ ShaderLab Material Instance), which picks a base directly from
// the registry. The instances are the only assets users interact with.
//
// There is NO cook-time hook: the .usl sources are staged into packaged builds (a wildcard
// RuntimeDependency in ShaderLab.Build.cs) and the cooked runtime re-parses them directly, so
// nothing special needs to be written at cook.
UE_LOG(LogShaderLabEditor, Log, TEXT("ShaderLabEditor module started."));
}