DreamShaderLang
Generation

In-memory Materials

Why a compiled DreamShader material does not show up in the Content Browser, what the hidden base material is, and how to put it on disk.

You save a .dsm, the log says the material was generated, and the Content Browser shows nothing. Nothing is broken. This page explains what actually happened, and it is worth reading before you file a bug.

AspectValue
Applies toShader blocks under the ThinCustom backend (the project default) and under the Graph backend
Emitted classUDreamShaderMaterialInstance (ThinCustom) or UMaterial (Graph)
Markerthe containing package still carries PKG_NewlyCreated
Sincesince 1.5.0 — the ThinCustom backend and the Content Browser visibility toggle

What a DreamShader material is

Under the default backend a Shader block does not produce a UMaterial asset. It produces two objects:

UDreamShaderMaterialInstance          "M_Emissive"            <- the asset you reference
  └─ UMaterial (subobject, hidden)    "MB_DreamThinBase_M_Emissive"
       └─ the generated node graph

The node graph — every Properties node, every Graph statement, every Outputs binding — is built on the hidden base. The instance is a thin wrapper carrying the parameter values, the provenance metadata, and the compiled shader map. Everything in Settings lands on the base, which is why reading BlendMode off the generated instance shows an inherited value.

MemberVisibilityMeaning
SourceFilePathread-only in the details panel, category DreamShaderthe .dsm this instance was generated from
SourceHashread-only in the details panel, category DreamShaderthe source hash — see Regeneration
Parentstandardthe hidden base material

Two overrides give the class its behaviour, and both are load-bearing.

OverrideResultConsequence
HasOverridenBaseProperties()true exactly when the parent is a UMaterialthe root instance owns its own static permutation and shader map; a child UMaterialInstanceConstant parented to it falls through to stock behaviour and shares that shader map, so many colour or parameter variants cost one compile
IsAsset()false while the package is PKG_NewlyCreated and Show In-Memory Materials In Content Browser is offmemory-only materials are hidden from the Content Browser, asset pickers, and save pickers

The second override is the whole answer to "where did my material go".

The hidden base

ModeBase object nameOuterObject flags
memory-onlyMB_DreamThinBase_<sanitized Name>the transient packageRF_Public, RF_Standalone, RF_Transient
persistedMB_DreamThinBase_<instance leaf name>the instance object itselfRF_Public, RF_Standalone

<sanitized Name> is the block's whole logical Name with every character outside [A-Za-z0-9_] replaced by _ and runs of underscores collapsed, so Shader(Name="Mat/Test") yields MB_DreamThinBase_Mat_Test. The sanitization is not cosmetic: a / inside an FName reads as a subobject separator, which would break base reuse and leak a fresh base on every regeneration.

In persist mode the base is a subobject of the instance, so it serializes into the instance's own package as a plain export. One asset, one .uasset, no MB_DreamThinBase_* sibling in the Content Browser, and no cross-package parent import to lose at cook time. Because a non-package outer already makes IsAsset() false, the base is invisible in both modes.

An instance saved by a pre-1.5.0 build, whose parent lives in a separate MB_* package, is not reused. Regeneration creates a fresh subobject base and leaves the old sibling package orphaned. The orphan is harmless and can be deleted.

Why nothing appears in the Content Browser

Interactive compiles are memory-only by design. The .dsm file is the authoring surface, and a generated .uasset sitting on disk would shadow it — you would end up with two sources of truth and no way to tell which one won. Every trigger except cook, the commandlet, and an explicit Materialize generates in memory; see what triggers a build.

While a material is memory-only:

  • IsAsset() returns false, so it appears in no Content Browser view and no asset picker.
  • Its package is marked non-dirty at the end of generation, so Save All and the exit prompt cannot silently persist it.
  • The material is nonetheless fully usable — through the Material Content Browser, the live preview, and by anything holding a live pointer to it.

The visibility toggle

AspectValue
SettingShow In-Memory Materials In Content Browser, category Compiler
Defaultoff
MenuTools ▸ DreamShader ▸ Show In-Memory Materials
Also onthe DreamShader Project page, as a checkbox

Toggling writes the project config and immediately broadcasts asset-created / asset-deleted events for every live UDreamShaderMaterialInstance whose package is still PKG_NewlyCreated, so tiles appear and disappear without a rescan. The confirmation reads:

Showing {Count} in-memory material(s) in the Content Browser and asset pickers.
Hidden {Count} in-memory material(s) from the Content Browser and asset pickers.

While the toggle is on, a memory-only material looks like an ordinary tile — and an explicit Save on it writes a real .uasset to disk. That saved asset then shadows in-memory regeneration for that path. Use Materialize rather than Save when you actually want the file.

Materializing to disk

Materialize re-runs generation for the material's own source file with persistence on and forcing enabled, then reloads the object at its resolved path.

SurfaceAction
Material Content Browser, Gen pagethe Materialize button — "Write this memory-only material (and its base) to disk."
Content Browser context menuthe DreamShader materialize action
Implicitcreating a child material instance of a memory-only parent materializes the parent first

Creating a child instance must materialize first, because a transient base cannot be a parent import. The default destination for the child is <parent directory>/<Instance Subfolder> — the Material Instance Subfolder project setting, default Instances; when it is empty the child is created beside the parent. The child is named MI_<parent leaf>, uniquified.

A material that is already persisted is returned unchanged.

MessageCause
This material is memory-only and has no DreamShader source file to materialize from.the object is not a UDreamShaderMaterialInstance, or its SourceFilePath is empty
Failed to materialize the material to disk: {Message}the regeneration that materializes it failed
Materialized the material but could not reload it at {ObjectPath}.generation succeeded but the object could not be loaded back

Shadowed by a saved asset

If a memory-only compile targets a package path that already exists on disk, generation still succeeds, but logs a warning:

In-memory material mode: '{PackageName}' already exists as a saved asset, which shadows in-memory
regeneration. Delete the saved asset to make it fully in-memory.

Two commands address this:

CommandEffect
Tools ▸ DreamShader ▸ Clean Persisted Generated Assetsdeletes saved assets that carry DreamShader provenance metadata, with a confirmation dialog listing every one. Hand-authored assets are never touched. Empty case: No persisted DreamShader-generated assets found.
Tools ▸ DreamShader ▸ Clean Generated Shadersdeletes every *.ush under the generated-shader directory and queues a full recompile

The cleaner scans /Game recursively for UMaterial, UMaterialFunction and UDreamShaderMaterialInstance assets whose package exists on disk and whose metadata carries a non-empty DreamShader.SourceFile.

Changing the Default Compiler Backend setting regenerates everything in memory and then warns if any saved generated assets remain:

{Count} previously generated asset(s) are still saved on disk and shadow the in-memory materials.
Run Tools > DreamShader > Clean Persisted Generated Assets to remove them.

Cook behaviour

Cooking is where the assets become real.

AspectBehaviour
Detectionthe process is a cook when the -run= value contains Cook
Who generatesthe cook director only — a process launched with -cookworker skips generation and loads what the director saved
Whenon post-engine-init, after engine subsystems exist but before the commandlet's Main
Whatevery project DreamShader source file except .dsh, generated forced and persisted
On failurethe cook aborts

Cook log lines:

DreamShader cook: generating {Count} source file(s) as persistent assets...
  [Cook] {Message}
  [Cook] Failed: {Message}
DreamShader cook asset generation complete.

A single generation failure ends the cook with a fatal log entry: DreamShader cook generation failed for {Count} source file(s); aborting the cook. See the [Cook] Failed entries above. Compile every source cleanly in the editor, or run the commandlet, before you cook.

Generation runs on post-engine-init rather than at module startup because the material editing library it depends on needs editor subsystems that do not exist during module load. Restricting it to the director avoids every worker racing to save the same packages.

Notes

  • IsMemoryOnly is decided by exactly one thing: the package still carries PKG_NewlyCreated. Nothing else distinguishes an in-memory material from a persisted one.
  • Memory-only materials keep whatever node positions the construction pass produced — automatic layout is skipped in memory-only mode, so an in-memory graph looks messier than a materialized one.
  • Provenance metadata is stamped on a ThinCustom instance in both modes, and additionally on the base in persist mode. Graph-backend materials and material functions are stamped only when persisted.
  • A generated instance is deliberately not RF_Transactional: material instances do not support undo/redo without desynchronizing the shader map.
  • None of this changes the source-hash short circuit; a memory-only compile still skips work when the hash is unchanged.

Worked example

Shader(Name="Materials/M_Emissive")
{
    Properties { ScalarParameter Intensity = 2.0 [Slider(0, 10)]; }
    Settings   { Backend = "ThinCustom"; ShadingModel = "Unlit"; }
    Outputs    { vec3 Color; Base.EmissiveColor = Color; }
    Graph      { Color = vec3(1.0, 0.4, 0.1) * Intensity; }
}

Saving that file in the editor produces, in memory only:

package        /Game/Materials/M_Emissive          (PKG_NewlyCreated, not dirty, not on disk)
  object       M_Emissive                          UDreamShaderMaterialInstance
    subobject  MB_DreamThinBase_Materials_M_Emissive   UMaterial, holds the node graph

After Materialize:

on disk        <Project>/Content/Materials/M_Emissive.uasset
  export       M_Emissive                          UDreamShaderMaterialInstance
  export       MB_DreamThinBase_M_Emissive         UMaterial (hidden, same package)

Note the base's name changes between the two modes: the sanitized full Name in memory, the instance's leaf name on disk.

Where next

On this page