DreamShaderLang
Getting Started

Project Layout

How DShader/ is organized, which directories the plugin owns, and the package rules that decide what gets compiled.

DreamShaderLang sources live in one directory under the project root — DShader by default, set by Project Settings ▸ DreamPlugin ▸ Dream Shader ▸ Paths ▸ Source Directory. A relative path resolves against the project directory.

The plugin creates the source directory, its Packages subdirectory and the generated-shader directory at module startup, on the first editor launch, whether or not you have authored anything.

The tree

M_Sample.dsm
F_Tint.dsf
Common.dsh
Color.dsh
dreamshader.package.json
Noise.dsh
DreamShader.code-workspace
dreamshader.lock.json

Directory roles

DirectoryOwnerContents
DShader/youThe source root. Any layout below it works; the ones here are conventions, not rules.
DShader/Materials/youMaterial .dsm files, usually one Shader each.
DShader/Functions/youReusable .dsf function files.
DShader/Shared/youProject-local .dsh headers.
DShader/VirtualFunctions/the editorWhere Create Virtual Function writes a declaration for an existing UMaterialFunction.
DShader/Decompiled/the editorWhere Export DSM / Export DSF write, under Materials, Functions, Layers and LayerBlends.
DShader/Packages/the extensionInstalled shared libraries. Always the literal Packages subdirectory of the source root; not separately configurable.
DShader/DreamShader.code-workspacethe pluginRewritten from scratch on every Open Dream Shader Workspace.
DShader/dreamshader.lock.jsonthe extensionRecords installed package revisions. Neither read nor written by the plugin.
Intermediate/DreamShader/GeneratedShaders/the pluginThe generated .ush helper includes, mounted at /DreamShaderGenerated.

DreamShader.code-workspace is serialized from a fixed object on every invocation — it is never read, merged or preserved. Hand-added launch, tasks, extensions or extra settings entries are destroyed. Keep per-user configuration in DShader/.vscode/settings.json, which the command does not touch.

Naming conventions

KindConvention
Material fileM_*.dsm
Material function fileF_*.dsf
Shared headernamed by domain — Texture.dsh, Color.dsh
Package entryLibrary/<Name>.dsh

Extensions are compared case-insensitively, so M_Water.DSM is a material file. Identifiers are a different matter: non-ASCII characters in namespace and function names are replaced when the plugin sanitizes them into HLSL and asset identifiers, so keep those ASCII.

Imports

Prefer a small number of stable entry imports per material:

import "Shared/Common.dsh";
import "@typedreammoon/dream-noise/Library/Noise.dsh";

A specifier is tried against three roots in order: the importing file's own directory, the source root, then the packages root. The first candidate that exists and stays inside its own root wins — a file inside a package cannot climb out with ../. A specifier with no extension gets .dsh appended, so import "…/Noise" can never resolve to a .dsf. Full rules: Imports and Namespaces.

Keeping imports shallow also keeps rebuilds honest: the source hash covers the whole inlined import closure, so a header edit invalidates every file that pulls it in.

What is compiled, and what is not

The plugin has two enumerators, and they exclude different things.

EnumeratorExtensionsExcludesUsed by
Full source enumeration.dsm, .dsh, .dsfeverything under DShader/Packagesstartup in-memory generation, the commandlet's compile -All, cook, the Gen page list, VirtualFunction sync
Material source enumeration.dsm, .dsfonly .dsm files under DShader/PackagesRecompile DSM, the auto-compile queue, the dependency graph

The Packages exclusion is complete for .dsm but only partial for .dsf. A .dsf shipped inside a package is compiled in an interactive editor session — the file watcher and Recompile DSM pick it up — and is not compiled by compile -All, by cook, or by the Gen page. A package that ships .dsf function assets therefore works locally and silently produces nothing in a headless build. Ship library code as .dsh headers, or copy the .dsf out of Packages into your own source tree.

Examples/**/*.dsm inside a package is never compiled by any path and never appears in the Gen page. To use an example, copy it out of DShader/Packages into DShader/.

Package .dsh headers are fully importable, but are never scanned for VirtualFunction declarations — a VirtualFunction shipped in a package is never validated against its asset.

Source control

ItemCommit?
.dsm / .dsf / .dshyes — this is the material logic
dreamshader.lock.jsonyes, if the team installs packages
Config/DefaultEngine.iniyes — the project settings live there and are shared
DShader/Packages/team's choice: vendored, or reinstalled from the lock file
Generated .uasset filesusually no — with the default backend the editor writes none anyway
Intermediate/DreamShader/no

Standardize Source Directory across the team so imports resolve identically on every machine.

Next

On this page