DreamShaderLang
插件参考C++ API

DreamShaderModule.h

DreamShaderModule.h

DreamShaderModule.h

DreamShader » C++ API » DreamShaderModule.h

运行时模块入口点、插件的日志类别,以及其他所有模块所依赖的路径、标识符和文件分类辅助工具。

定义于头文件 DreamShaderModule.h。

模块DreamShader(Runtime)
包含#include "DreamShaderModule.h"
命名空间UE::DreamShader(自由函数)· 全局作用域(LogDreamShader、FDreamShaderModule)
导出宏DREAMSHADER_API
引入CoreMinimal.h、DreamShaderVersionCompat.h、Modules/ModuleManager.h

概要

#include "CoreMinimal.h"
#include "DreamShaderVersionCompat.h"
#include "Modules/ModuleManager.h"

class IPlugin;

DREAMSHADER_API DECLARE_LOG_CATEGORY_EXTERN(LogDreamShader, Log, All);

namespace UE::DreamShader
{
    struct FDreamShaderSourceRoot
    {
        FString Directory;          // absolute, normalized, no trailing slash
        FString PackagesDirectory;  // <Directory>/Packages
        FString DisplayName;        // "Project", or the owning plugin's name
        FString PluginName;         // empty for the project root
        bool bIsProjectRoot = false;
        bool bIsProjectPlugin = false; // true when the plugin is one this project generates into
        bool bWritable = false;     // true for the project root only
    };

    DREAMSHADER_API const TArray<FDreamShaderSourceRoot>& GetSourceShaderRoots();
    DREAMSHADER_API const FDreamShaderSourceRoot* FindSourceRootForFile(const FString& InPath);
    DREAMSHADER_API bool IsWritableSourceFilePath(const FString& InPath);
    DREAMSHADER_API bool IsProjectContentPlugin(const IPlugin& InPlugin);
    DREAMSHADER_API void RefreshSourceShaderRoots();
    DREAMSHADER_API bool IsPathUnderSourceDirectory(const FString& InPath, const FString& InDirectory);

    DREAMSHADER_API FString GetSourceShaderDirectory();
    DREAMSHADER_API FString GetPackageShaderDirectory();
    DREAMSHADER_API FString GetGeneratedShaderDirectory();
    DREAMSHADER_API FString GetGeneratedShaderVirtualDirectory();
    DREAMSHADER_API FString SanitizeIdentifier(const FString& InText);
    DREAMSHADER_API FString NormalizeSourceFilePath(const FString& InPath);
    DREAMSHADER_API bool IsDreamShaderMaterialFile(const FString& InPath);
    DREAMSHADER_API bool IsDreamShaderHeaderFile(const FString& InPath);
    DREAMSHADER_API bool IsDreamShaderFunctionFile(const FString& InPath);
    DREAMSHADER_API bool IsDreamShaderLang2File(const FString& InPath);
    DREAMSHADER_API bool IsDreamShaderInstanceFile(const FString& InPath);
    DREAMSHADER_API bool IsDreamShaderPipelineFile(const FString& InPath);
    DREAMSHADER_API bool IsDreamShaderSourceFile(const FString& InPath);
}

class DREAMSHADER_API FDreamShaderModule : public IModuleInterface
{
public:
    virtual void StartupModule() override;
    virtual void ShutdownModule() override;
};

LogDreamShader

方面值
声明DREAMSHADER_API DECLARE_LOG_CATEGORY_EXTERN(LogDreamShader, Log, All)
默认运行时详细级别Log
编译时最大详细级别All
定义于DreamShader(Runtime)
使用方DreamShader、DreamShaderCompiler 和 DreamShaderEditor。不包括 DreamShaderLang:它仅依赖 Core 且从不记录日志,而是通过 FLangDiagnosticSink 进行报告。也不包括 DreamShaderPass,它有自己的 LogDreamPass

编译器和编辑器记录的每条消息——输出到日志的诊断信息、生成结果、cook 进度、桥接和 SQLite 警告——都经过这同一个类别。可通过配置文件或控制台提升其级别:

[Core.Log]
LogDreamShader=Verbose

目录函数

FString GetSourceShaderDirectory();
FString GetPackageShaderDirectory();
FString GetGeneratedShaderDirectory();
FString GetGeneratedShaderVirtualDirectory();
函数返回值设置未配置时的值
GetSourceShaderDirectory()绝对、规范化的项目源根目录——即相对于项目目录解析的 UDreamShaderSettings::SourceDirectory.Path。所有编辑器端写入的目标;插件根目录位于源根目录下<Project>/DShader
GetPackageShaderDirectory()项目根目录的 <source root>/Packages,始终由 GetSourceShaderDirectory() 派生;没有单独的设置项<Project>/DShader/Packages
GetGeneratedShaderDirectory()当前为虚拟挂载点 /DreamShaderGenerated 注册的真实目录;仅当不存在此类映射时,才回退到配置的 GeneratedShaderDirectory<Project>/Intermediate/DreamShader/GeneratedShaders
GetGeneratedShaderVirtualDirectory()编译时常量 TEXT("/DreamShaderGenerated")不适用

配置路径的解析顺序:

步骤规则
1去除配置路径的首尾空白。如果为空,则替换为硬编码的默认值。
2如果结果为相对路径,则与 FPaths::ProjectDir() 合并。
3FPaths::NormalizeFilename,然后 FPaths::MakeStandardFilename。

这三个配置路径保存在一个内部缓存中。当缓存尚未初始化或设置对象当前可读时——即 UObjectInitialized() && !GExitPurge && !IsEngineExitRequested()——会刷新缓存。一旦 UObject 系统启动,此条件始终为真,因此设置会在每次调用时重新读取,会话期间在项目设置中的更改会立即生效。在 UObject 存在之前以及退出清理期间,getter 不会访问 GetDefault<UDreamShaderSettings>():它们直接从引擎 ini 中的 [/Script/DreamShader.DreamShaderSettings] 读取 SourceDirectory 和 GeneratedShaderDirectory——即设置对象将要加载的值——并在某个键缺失时回退到默认值。模块在 PostConfigInit 启动,早于设置对象可能存在的时机,这正是原因所在。

[!WARNING] GetGeneratedShaderDirectory() 并不简单等同于 Generated Shader Directory 设置。 StartupModule 仅在 /DreamShaderGenerated 映射不存在时注册它,并且从不 重新指向它。因此,在会话中途更改该设置会改变 StartupModule 本应 挂载的位置,并改变内部缓存值,但 GetGeneratedShaderDirectory() 仍会 返回启动时挂载的目录。重启编辑器以移动挂载点。参见 生成的 HLSL。

[!NOTE] 该缓存是一个普通静态变量,没有锁。应将这四个函数视为游戏线程 API,尽管 它们本质上并不与任何线程绑定。

源根目录

(自 1.6.0 起)

const TArray<FDreamShaderSourceRoot>& GetSourceShaderRoots();
const FDreamShaderSourceRoot* FindSourceRootForFile(const FString& InPath);
bool IsWritableSourceFilePath(const FString& InPath);
bool IsProjectContentPlugin(const IPlugin& InPlugin);
void RefreshSourceShaderRoots();
bool IsPathUnderSourceDirectory(const FString& InPath, const FString& InDirectory);

源根目录是一个目录树,源文件在其下被发现,也是导入解析的单位:文件的导入仅针对其自身的根目录及该根目录的 Packages 文件夹进行解析。

函数行为
GetSourceShaderRoots()首先是项目根目录,然后——当 Scan Plugin Source Directories 开启时——为每个启用了 DShader 文件夹的已启用插件提供一个根目录。与较早的根目录重叠(一个包含另一个)的插件根目录会被丢弃,并发出 LogDreamShader 警告。已缓存;在项目源目录或扫描开关更改时重建。该引用会在下次重建时失效,因此请在游戏线程上使用它,并且不要保留它
FindSourceRootForFile(InPath)包含 InPath 的根目录,若其不属于任何根目录则为 nullptr
IsWritableSourceFilePath(InPath)对于插件根目录下的文件返回 false;对于项目根目录下的文件以及不属于任何根目录的路径(显式命名的临时源文件,例如命令件 -Source)返回 true
IsProjectContentPlugin(InPlugin)当插件是项目插件(EPluginType::Project)且其基础目录位于 <Project>/Plugins 下时返回 true——与第 3 关对 Plugin.<Name> 根目录所问的相同问题。对于引擎或市场插件返回 false,其挂载点不允许任何宿主项目生成到其中
RefreshSourceShaderRoots()丢弃缓存。仅在插件于会话中途挂载或卸载时需要
IsPathUnderSourceDirectory(InPath, InDirectory)当路径是目录本身或其下级时返回 true;两者都用 NormalizeSourceFilePath 规范化并进行不区分大小写的比较

只有项目根目录可写:那些就地重写源文件的编辑器功能(VirtualFunction 同步、资产重命名同步)绝不会触碰插件根目录。

FDreamShaderSourceRoot::bIsProjectPlugin 携带关于插件根目录的第二个独立答案—— 即其产物是否属于项目应构建的内容,通过 IsProjectContentPlugin 计算。cook 会对生成失败的源文件读取此信息:在非项目插件的插件下,失败会被报告且 cook 继续进行,因为这些资产随该插件一同发布。参见 Cook 行为。

SanitizeIdentifier

DREAMSHADER_API FString SanitizeIdentifier(const FString& InText);

将任意文本重写为可用作 HLSL 或资产标识符的内容。纯函数;不触碰任何引擎状态;可从任何线程调用。

步骤规则
1将 [A-Za-z0-9_] 之外的每个字符替换为 _。
2如果结果为空,则返回 DreamShaderSymbol。
3如果结果仅由下划线组成,则返回 DreamShaderSymbol。
4如果第一个字符不在 [A-Za-z_] 中——即它是数字——则前置 _。
5从右向左扫描,将每一段连续的下划线折叠为单个 _。
输入结果
Noise::PerlinNoise_Perlin
2Fast_2Fast
a ba_b
___DreamShaderSymbol
""DreamShaderSymbol
Ünlit_nlit

[!WARNING] 字符分类是基于原始 ASCII 范围进行的。非 ASCII 字母会被替换,而不是 保留——Ünlit 会变成 _nlit,而不是 Ünlit。在命名空间和函数 名称中请使用 ASCII 标识符。

插件内部的调用方:发射器中的瞬态 ThinCustom 基础材质名称(MB_DreamThinBase_<package>)、输出命名重路由名称(DS_<Name>_<Index>),以及实例工厂和溯源操作中新建实例的 .dsi 文件对应的 MI_<Name> 词干。DreamShaderLang 无法链接该模块,因此旧版前端(将 A::B 展平为 A_B)和自定义 HLSL 构建器(DreamShaderFn_<Name>)各自保留同一规则的副本 (自 2.0.0 起)。

NormalizeSourceFilePath

DREAMSHADER_API FString NormalizeSourceFilePath(const FString& InPath);

依次应用 FPaths::ConvertRelativePathToFull、FPaths::NormalizeFilename,然后 FPaths::MakeStandardFilename。结果为使用 / 分隔符的绝对路径。

这是源文件的唯一规范键。桥接模块、诊断存储、编译器和命令行工具在比较路径前都会先将其规范化,因此此函数生成的路径与插件内部存储的路径比较时相等。

四种规范化函数的比较

有四个名称相似但行为不同的函数。它们不可互换。

函数头文件操作典型对象
UE::DreamShader::NormalizeSourceFilePathDreamShaderModule.h绝对路径,/ 分隔符.dsm / .dsf / .dsh 文件路径
UE::DreamShader::SanitizeIdentifierDreamShaderModule.h非 [A-Za-z0-9_] → _,数字开头修正,连续下划线折叠HLSL 或资产标识符
UE::DreamShader::NormalizeSettingKeyDreamShaderTypes.h去除首尾空白,然后转为小写 — 仅此而已Settings / Options 键
UDreamShaderSettings::NormalizeMappingKeyDreamShaderSettings.h去除首尾空白、转小写,然后去除所有空格、_ 和 -ShadingModel / BlendMode / Domain 值别名

后两个是最常被混淆的一对。NormalizeSettingKey 保留空格和标点,因此设置键 Two Sided 和 TwoSided 是不同的键。NormalizeMappingKey 会移除它们,因此值 Two Sided Foliage、TwoSidedFoliage 和 two-sided_foliage 都解析为同一个着色模型。

文件分类谓词

DREAMSHADER_API bool IsDreamShaderMaterialFile(const FString& InPath);
DREAMSHADER_API bool IsDreamShaderHeaderFile(const FString& InPath);
DREAMSHADER_API bool IsDreamShaderFunctionFile(const FString& InPath);
DREAMSHADER_API bool IsDreamShaderLang2File(const FString& InPath);
DREAMSHADER_API bool IsDreamShaderInstanceFile(const FString& InPath);
DREAMSHADER_API bool IsDreamShaderPipelineFile(const FString& InPath);
DREAMSHADER_API bool IsDreamShaderSourceFile(const FString& InPath);
函数当扩展名为以下内容时返回 true
IsDreamShaderMaterialFile.dsm
IsDreamShaderHeaderFile.dsh
IsDreamShaderFunctionFile.dsf
IsDreamShaderLang2File.dss — 2.0 编译单元
IsDreamShaderInstanceFile.dsi — 2.0 材质实例
IsDreamShaderPipelineFile (自 2.1.0 起).dsp — Custom Pass 管线
IsDreamShaderSourceFile六者之一:.dsm、.dsh、.dsf、.dss、.dsi 或 .dsp (.dsp 自 2.1.0 起)

IsDreamShaderSourceFile 是所有扫描、监视或列出源文件的代码都会询问的谓词 —— 启动扫描、源目录监视器、compile -All、材质内容浏览器 —— 因此 .dsp 对它们而言都是源文件;只有编译器会区分各种类型。所有七个谓词都将 FPaths::GetExtension(InPath, /*bIncludeDot*/ true) 的结果与 ESearchCase::IgnoreCase 进行比较,因此扩展名匹配不区分大小写:Foo.DSM 会被归类为材质文件。仅检查扩展名 —— 文件无需存在,也从不读取其内容。纯函数;可在任意线程调用。

FDreamShaderModule

class DREAMSHADER_API FDreamShaderModule : public IModuleInterface
{
public:
    virtual void StartupModule() override;
    virtual void ShutdownModule() override;
};

通过 IMPLEMENT_MODULE(FDreamShaderModule, DreamShader) 注册。该类不暴露任何状态和访问器;如需获取,可使用 FModuleManager::LoadModuleChecked<FDreamShaderModule>(TEXT("DreamShader"))。

StartupModule()

在模块加载时于游戏线程运行,处于 PostConfigInit 阶段 —— 早于任何材质加载,以便缓存包含路径中命名了 /DreamShaderGenerated 的材质能找到已注册的映射。它按顺序执行五个副作用。

#操作备注
1将 GetSourceShaderDirectory() 创建为目录树默认为 <Project>/DShader
2将 GetPackageShaderDirectory() 创建为目录树默认为 <Project>/DShader/Packages
3将配置的生成着色器目录创建为目录树使用配置的路径,而非 GetGeneratedShaderDirectory()
4如果 /DreamShaderGenerated 在 AllShaderSourceDirectoryMappings() 中不存在,则将其映射到配置的生成着色器目录绝不重新指向已存在的映射
5如果通过 IPluginManager 找到 DreamShader 插件,且 /Plugin/DreamShader 不在映射中,则将其映射到 <PluginBaseDir>/Shaders绝不重新指向已存在的映射

插件的 Shaders/ 文件夹存放 DreamShaderBuiltins.ush,可通过 /Plugin/DreamShader/DreamShaderBuiltins.ush 访问(参见 DreamShaderBuiltins.ush),并且 (自 2.1.0 起) 存放 Pass/ 下的 Custom Pass 着色器(/Plugin/DreamShader/Pass/…)。

步骤 1–3 意味着无论用户是否创作了任何内容,源目录和包目录从首次编辑器启动起就已存在。

ShutdownModule()

为空。

[!NOTE] StartupModule 注册的两个虚拟着色器目录映射永远不会被注销。在卸载并重新加载该模块的编辑器会话中,第二次执行时步骤 4 和步骤 5 都是空操作,挂载点会一直指向它们最初被赋予的内容。

说明

  • 此头文件包含了 DreamShaderVersionCompat.h,因此任何包含 DreamShaderModule.h 的内容也会获得那十三个版本宏。参见 DreamShaderVersionCompat.h。
  • 所有自由函数都不记录日志、不抛出异常、不断言。失败以回退值表示 —— 默认路径、DreamShaderSymbol 或 false。
  • 此头文件不声明任何委托。编译完成的通知是 DreamShaderCompiler 模块中的 OnDreamShaderSourceGenerated —— 参见 编译器模块;编辑器外的工具则改为读取桥接模块的诊断文件 —— 参见 编辑器桥接。

示例

#include "DreamShaderModule.h"
#include "HAL/FileManager.h"

using namespace UE::DreamShader;

/** Enumerate every DreamShaderLang source under the configured source root. */
void CollectProjectSources(TArray<FString>& OutFiles)
{
    const FString Root = GetSourceShaderDirectory();

    TArray<FString> Found;
    IFileManager::Get().FindFilesRecursive(Found, *Root, TEXT("*.*"), /*Files*/ true, /*Dirs*/ false);

    for (const FString& File : Found)
    {
        if (IsDreamShaderSourceFile(File))
        {
            OutFiles.Add(NormalizeSourceFilePath(File));
        }
    }

    UE_LOG(LogDreamShader, Display, TEXT("Found %d DreamShader source(s) under %s"), OutFiles.Num(), *Root);
}

使用默认设置的典型输出:

LogDreamShader: Found 3 DreamShader source(s) under I:/Project/DShader

编译不写入辅助包含文件 (自 2.0.0 起):HLSL 函数成为自定义节点的代码,其调用的 HLSL 函数嵌入该代码中。/DreamShaderGenerated 映射仍在启动时注册,清理生成的着色器 仍会清空 GetGeneratedShaderDirectory() 中的 *.ush 文件。

另请参阅

  • C++ API — 模块、头文件与链接
  • DreamShaderTypes.h — NormalizeSettingKey 与已解析源数据模型
  • DreamShaderSettings.h — SourceDirectory、GeneratedShaderDirectory、NormalizeMappingKey
  • DreamShaderVersionCompat.h — 此头文件引入的宏
  • DreamShaderCompiler — 从 C++ 请求编译
  • DreamShaderLang — 仅限 Core 的前端。DreamShaderPreprocessor.h、 DreamShaderDefineTable.h 和 DreamShaderDiagnostic.h 在 2.0 中移入其中;include 的写法没有改变。只有 DreamShaderDefineResolution.h —— 读取 项目设置、引擎版本、插件描述符和命令行的那次层级合并 —— 留在了 此模块中
  • 项目设置 — 用户侧的两个目录设置
  • 生成的 HLSL — /DreamShaderGenerated 接收到的内容
  • 包 — DShader/Packages 目录
  • 源文件 — 三种扩展名可能包含的内容
  • DreamShaderBuiltins.ush — 挂载在 /Plugin/DreamShader 的头文件

本页目录