DreamShaderLang
Generation

Asset Paths

How Name= and Root= become an Unreal package path, an object path, and a file on disk — including the Plugin.X roots and the plugin-source-root default.

Asset paths

DreamShader » Generation » Asset paths

The rule that turns a block's Name and Root header attributes into an Unreal package path, an object path, and a file on disk.

Applies toShader, ShaderFunction, ShaderLayer, ShaderLayerBlend — one resolver, identical for all four
Kindheader attributes
SinceRoot="Plugin.X" — 1.2.0; the plugin-source-root default — 1.6.0

Synopsis

Shader        (Name = "<name-path>" [, Root = "<root>"]) { … }
ShaderFunction(Name = "<name-path>" [, Root = "<root>"]) { … }

<name-path> := [<folder> /] … <leaf>
<root>      := "" | Game | Plugin.<PluginName> | Plugins.<PluginName>
             | Plugin / <PluginName> | Plugins / <PluginName>
             | / <MountRoot> [/ <folder>] …
             | <folder> [/ <folder>] …

Name is required: a block without one, or with one that is empty, is DSH2242. Root is optional and defaults to the empty string. Attribute keys are matched case-insensitively, so name= and ROOT= both resolve.

(since 2.0.0) A 1.x block keeps exactly this destination (legacy rule L10): its Name= is never placed under the source file's folder, as the name of a .dss product is. A destination that does not resolve fails the product with DSH8200, whose message quotes the asset layer's code and text — see Diagnostics.

Resolution order

  1. Root → root package path. Trim; \ → /; remember whether the string began with /; strip leading and trailing /; split on /. An empty result at any point yields /Game.
  2. Name → folders + leaf. Trim; \ → /; strip all leading and trailing /; split on /. The last segment is the asset name; every preceding segment is a folder appended to the root.
  3. Every segment passes through ObjectTools::SanitizeObjectName. Empty segments are skipped silently.
  4. PackageName = <root>/<folders…>/<Leaf> and ObjectPath = <PackageName>.<Leaf>.
  5. PackageName is validated with FPackageName::IsValidObjectPath.

Root= dispatch

Dispatch is on the first segment, compared case-insensitively.

First segmentExtra conditionRoot packageFolders taken from segment
(absent, empty, /, or all-whitespace)—/Game—
Game—/Game1
Plugin.<Name>—the plugin's mount point1
Plugins.<Name>—the plugin's mount point1
Plugina second segment existsthe plugin named by segment 12
Pluginsa second segment existsthe plugin named by segment 12
anything elsethe Root string began with //<segment 0>, verbatim1
anything elseno leading //Game, and segment 0 becomes a folder0

Branch — omitted or empty

RootNamePackageObject path
(omitted)M_Flat/Game/M_Flat/Game/M_Flat.M_Flat
""Materials/M_Flat/Game/Materials/M_Flat/Game/Materials/M_Flat.M_Flat
"/"M_Flat/Game/M_Flat/Game/M_Flat.M_Flat

The plugin source root default

The table above is the project root's answer. A source file that lives under a plugin source root — <Plugin>/DShader, see Source files — defaults instead to that plugin's mount point, exactly as if it had written Root="Plugin.<PluginName>".

Source fileRootNamePackage
<Project>/DShader/M.dsm(omitted)M_Flat/Game/M_Flat
Plugins/MoonToon/DShader/M.dsm(omitted)M_Flat/MoonToon/M_Flat
Plugins/MoonToon/DShader/M.dsm"/"M_Flat/Game/M_Flat
Plugins/MoonToon/DShader/M.dsmGame/LegacyM_Flat/Game/Legacy/M_Flat

Only an absent or whitespace-only Root is defaulted, so Root="/" (or any explicit spelling) is how a plugin-root file opts back into /Game. The default is applied per block: a file may leave its Shader block to the default and pin one ShaderFunction to /Game.

[!NOTE] The default is skipped, and the block falls back to /Game, when the owning plugin cannot host generated content — it is not a project plugin under <Project>/Plugins, is disabled, declares no CanContainContent, has no Content directory, or is unmounted. (since 2.0.0) The fallback is silent: nothing is logged. An unresolvable inferred Root is never emitted as an error, because the author never wrote one.

The package asset root

(after 2.1.2) A source a package builds — one under its Assets folder, or an asset header — defaults to the package's asset root, whichever source root holds the package, and the plugin default above does not apply to it:

Source fileRootName / exportPackage
DShader/Packages/@scope/lib/Assets/MF_A.dss(omitted)MF_A/Game/DreamShaderPackages/scope/lib/MF_A
DShader/Packages/@scope/lib/Assets/Noise/MF_B.dss(omitted)MF_B/Game/DreamShaderPackages/scope/lib/Noise/MF_B
DShader/Packages/lib/Assets/MF_C.dsf(omitted)MF_C/Game/DreamShaderPackages/lib/MF_C
Plugins/MoonToon/DShader/Packages/lib/Assets/MF_D.dss(omitted)MF_D/MoonToon/DreamShaderPackages/lib/MF_D

A 2.0 source adds its folder under Assets, as a project source adds its folder under the source root; a 1.x Name= says every folder it wants. An explicit Root still wins. See Packages ▸ Package assets.

The default is applied in the one place a product becomes an asset path, so the compile, product resolution (the Material Content Browser, dsc list-generated) and the preview renderer all name the same asset.

Branch — Game

RootNamePackage
GameM_Flat/Game/M_Flat
/GameMaterials/M_Flat/Game/Materials/M_Flat
Game/MaterialsM_Flat/Game/Materials/M_Flat
game/materialsM_Flat/Game/Materials/M_Flat

The comparison is case-insensitive, but the emitted root is always the literal /Game.

Branch — Plugin.<Name> / Plugins.<Name>

The dotted form names the plugin in a single segment; everything after it is folders.

RootNamePackageOn disk
Plugin.MoonToonMat/Test/MoonToon/Mat/Test<Project>/Plugins/MoonToon/Content/Mat/Test.uasset
Plugins.MoonToonTest/MoonToon/Test<Project>/Plugins/MoonToon/Content/Test.uasset
Plugin.MoonToon/SharedTest/MoonToon/Shared/Test<Project>/Plugins/MoonToon/Content/Shared/Test.uasset

Branch — Plugin/<Name> / Plugins/<Name>

The slash form spends two segments on the plugin reference.

RootNamePackage
Plugin/MoonToonTest/MoonToon/Test
Plugins/MoonToonMat/Test/MoonToon/Mat/Test
Plugins/MoonToon/SharedTest/MoonToon/Shared/Test

[!WARNING] Root="Plugin" and Root="Plugins" with no second segment do not name a plugin. They fall through to the last dispatch row and are treated as ordinary folder names, producing /Game/Plugin and /Game/Plugins. No diagnostic is emitted.

Branch — explicit mount root

A Root that begins with / and is not one of the forms above is taken verbatim as a mount point.

RootNamePackage
/MyMountTest/MyMount/Test
/MyMount/SubTest/MyMount/Sub/Test
/EngineTest/Engine/Test

The first segment must survive SanitizeObjectName unchanged, otherwise the destination fails with DSH8097. The mount point itself is not checked for existence here — an unmounted root fails later, at the IsValidObjectPath gate.

Branch — bare relative path

Any other Root without a leading / becomes folders under /Game.

RootNamePackage
FooTest/Game/Foo/Test
Foo/BarTest/Game/Foo/Bar/Test
Foo/BarDeep/Test/Game/Foo/Bar/Deep/Test

The difference between this branch and the previous one is exactly the leading slash: Root="Foo/Bar" is /Game/Foo/Bar, Root="/Foo/Bar" is /Foo/Bar.

Plugin-root requirements

Both plugin forms resolve through the same validator. Every gate below must pass, in this order; each has its own code, which the product's DSH8200 quotes.

#RequirementCode when it fails
1The plugin name is non-empty and unchanged by SanitizeObjectNameDSH8095 (Plugin.X), DSH8096 (Plugin/X)
2A plugin with that name is known to the plugin managerDSH8089
3It is a project plugin, and its base directory is under the project's Plugins directoryDSH8090
4It is enabledDSH8091
5It can contain contentDSH8092
6Its Content directory exists on diskDSH8093
7Its content is mounted (UE 5.6+ only)DSH8094

Gate 7 does not exist on UE 5.3 – 5.5; on those engines an unmounted plugin is caught later by the object-path validation instead.

The root package path is the plugin's own mounted asset path — normalized to \ → /, no trailing slash, a forced leading slash. If that degenerates to empty or /, /<PluginName> is used.

[!NOTE] Engine plugins and marketplace plugins installed under the engine directory are rejected by gate 3 even when they are enabled and mounted. Only plugins physically under <Project>/Plugins are accepted. To write into an engine-side mount, use the explicit mount-root branch (Root="/SomeMount"), which skips the plugin validator entirely.

A cook does not abort over such a source. Its products are the plugin's own — the plugin ships the assets its sources describe — and no host project can generate into that mount or even edit the file (plugin roots are read-only), so a generation failure there is logged and the cook goes on. Only the project's own sources, and those of its own plugins under <Project>/Plugins, can fail a cook. See Cook behaviour.

Per asset kind

The path resolution is identical for all four block kinds. What differs is the class created and, for the function kinds, the material-function usage stamped on it.

Source blockAsset classMaterial function usage
Shader — Graph backendUMaterial—
Shader — ThinCustom backend (since 1.5.0)UDreamShaderMaterialInstance plus a hidden UMaterial subobject—
ShaderFunctionUMaterialFunctionDefault
ShaderLayer (since 1.3.0)UMaterialFunctionMaterialLayerMaterialLayer
ShaderLayerBlend (since 1.3.0)UMaterialFunctionMaterialLayerBlendMaterialLayerBlend

When an asset already exists at the resolved path, the class must match:

KindMatch rule
ShaderFunctionexact class match — a UMaterialFunctionMaterialLayer at that path is rejected
ShaderLayer, ShaderLayerBlendIsA the expected class
Shader — Graphthe existing object must be a UMaterial
Shader — ThinCustomthe existing object must be a UDreamShaderMaterialInstance

Custom Pass pipelines

(since 2.1.0) A .dsp has no Name=, no Root= and no /// @name. Its one product, a UDreamPassPipeline, is named after the file and placed by the 2.0 default: the file's folder relative to its source root, under /Game — or under the plugin's mount point for a file under a plugin's source root, by the same plugin source root default and with the same fallback to /Game. The render target of every exported buffer lands in the pipeline's folder, named <Pipeline>_<Buffer> through SanitizeObjectName.

Source filePipelineRender target of an exported buffer Mask
<Project>/DShader/Passes/CP_Highlight.dsp/Game/Passes/CP_Highlight/Game/Passes/CP_Highlight_Mask
<Project>/DShader/CP_Wind.dsp/Game/CP_Wind/Game/CP_Wind_Mask
Plugins/MoonToon/DShader/Passes/CP_Toon.dsp/MoonToon/Passes/CP_Toon/MoonToon/Passes/CP_Toon_Mask
At the pathRefused when
the pipeline'sthe object there is not a UDreamPassPipeline (DSH8302), or it is a saved asset DreamShader did not generate (DSH8303)
a render target'sthe object there is not a UTextureRenderTarget2D, or it is a saved render target DreamShader did not make (DSH8313)

The only way to choose where a pipeline lands is where the .dsp lives, which is also why a decompiled .dsp cannot keep its asset's path from elsewhere (DSH9224). Moving or renaming the file builds a new pipeline at the new path; the old one, and its render targets, are left where they are, and its HLSL slots stay taken until dsc pass-registry -Gc collects them.

On-disk mapping

Package rootOn-disk directory
/Game/…<Project>/Content/…
/<PluginName>/…<Project>/Plugins/<PluginName>/Content/…
/<MountRoot>/…wherever that mount is registered

The file is <directory>/<Leaf>.uasset. A Graph-backend material and every function, layer and blend asset are saved by every build that may write; a ThinCustom product only once it is Materialized — see Ephemeral materials.

Notes

  • Name may contain folders; Root is only a prefix. Name="A/B/C" under Root="Game" yields /Game/A/B/C, and the asset is named C.
  • A duplicate attribute key overwrites the earlier one. Shader(Name="A", Name="B") resolves to B, with the warning DSH2244 (since 2.0.0; silent through 1.x).
  • Every segment is sanitized independently. Characters SanitizeObjectName rejects are replaced, so Name="My Mat" resolves to a leaf named My_Mat without a diagnostic.
  • Empty segments are dropped: Name="A//B" is A/B, and Root="Game//Sub" is /Game/Sub.
  • The resolver is also what the Content Browser status column and the Materialize action use, so a path that fails here also shows as unresolvable in the Material Content Browser.

Diagnostics

The asset layer's codes reach the compile result inside the emitter's: a destination that does not resolve is DSH8200, an asset that cannot be created or reused is DSH8201 (Graph material), DSH8203 (ThinCustom instance) or DSH8202 (function, layer, blend), and each message quotes the code below.

CodeRaised when
DSH2242a Shader / ShaderFunction / ShaderLayer / ShaderLayerBlend header has no Name, or an empty one
DSH2244(warning) an attribute key is written twice; the later value wins
DSH8098, DSH8099Name is empty after trimming and slash-stripping
DSH8100the leaf segment is empty after sanitization
DSH8088a non-empty folder segment of Name or of Root sanitizes away to nothing
—the assembled path fails IsValidObjectPath; DSH8200 quotes the engine's reason, or says the path is not a valid object path, with no code of its own
DSH8095, DSH8096plugin name empty, or altered by sanitization
DSH8097explicit mount root altered by sanitization
DSH8089–DSH8094a plugin gate — see Plugin-root requirements
DSH8104, DSH8108, DSH8113package creation failed (material, instance, function)
DSH8105, DSH8109, DSH8114object creation failed (material, instance, function)
DSH8102Graph backend, wrong class at the path
DSH8106ThinCustom backend, wrong class at the path
DSH8111function kind, an object that is not a material function at the path
DSH8110function kind, wrong material-function subclass
DSH8103, DSH8107ownership guard on a material (Graph, ThinCustom) — see Regeneration
DSH8112ownership guard on a material function

Example

Shader(Name="Mat/Test", Root="Plugin.MoonToon")
{
    Properties { vec3 Tint = vec3(1.0, 0.2, 0.2); }
    Settings   { Domain = "UI"; ShadingModel = "Unlit"; }
    Outputs    { vec3 Color; Base.EmissiveColor = Color; }
    Graph      { Color = Tint; }
}

Resolved destination:

Root      "Plugin.MoonToon"  ->  /MoonToon                       (plugin mount point)
Name      "Mat/Test"         ->  folders "Mat", leaf "Test"
package                          /MoonToon/Mat/Test
object path                      /MoonToon/Mat/Test.Test
on disk                          <Project>/Plugins/MoonToon/Content/Mat/Test.uasset

See also

On this page