TiXL 导出独立可执行文件(Player)完整指南:从编辑器到一键分发
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
TiXL(T3,tooll3 的开源继任者)允许你把任意一个带Texture2D输出的运算符一键打包成可独立运行的Player.exe,连同其依赖的运算符代码、贴图资源、音轨和预编译着色器一起复制到一个分发文件夹中,交付给没有安装 TiXL 的机器直接运行。本文将基于 ExportExecutables.md 的官方说明,结合仓库源码(PlayerExporter.cs、PlayerStartupOptions.cs、Program.cs)逐层拆解导出前置条件、导出配置、Player 启动流程、命令行参数与常见坑,读完即可完成一次"导出 → 分发 → 远程运行"的完整实战。
一、导出机制概述:它到底做了什么
导出是一个高度自动化的过程。官方文档(.help/docs/using/ExportExecutables.md)对它的定义是:
当你导出一个运算符时,TiXL 会收集所有依赖的运算符类型以及链接的文件资源(贴图、音轨等),复制到一个名为
Export的文件夹中,然后放入一个会查找ProjectSettings.json中列出的主运算符的Player.exe。
从源码看,这一步的核心实现在 PlayerExporter.TryExportInstance,它完成的工作比文档描述更多:
- 保存当前工程(
T3Ui.Save(false)),保证导出的是最新内容; - 校验被导出运算符的第一个输出必须是
Texture2D类型,否则直接报错Can only export ops with 'Texture2D' output(PlayerExporter.cs); - 从输出端出发递归遍历整张依赖图,收集所有可达的运算符实例、它们引用的资源地址和所需符号包;
- 额外收集"自动播放类"运算符(见下文第四节);
- 定位主音轨(soundtrack),找不到则弹窗让你选择继续或取消;
- 按需裁剪:剔除未使用运算符、排除未声明的可选依赖、只保留 win-x64 运行时;
- 复制运算符包、资源、共享资产、编辑器资源与 Player 运行时,写出
exportSettings.json,按工程标题重命名 exe,并把编辑器已编译过的着色器字节码预置进导出的ShaderCache/; - 成功后在日志中报告复制/跳过的文件数,并自动打开导出目录。
导出的目标目录由 GetExportDirectory 计算为:
<工程包目录>/<Export 子文件夹>/<被导出运算符的可读名称>其中"Export 子文件夹"常量定义在 FileLocations.cs(ExportSubFolder = "Export")。需要特别留意文档中的版本说明:TiXL v4.0.6(2025-09-15)起,文档记载可执行文件会输出到一个名为T3Export\的文件夹,且该位置将来还会变化。当前仓库源码中的常量仍为"Export",实际目录名请以你所使用的 TiXL 版本为准——这正体现了文档"此位置未来会改变"的提醒。
二、导出前置条件与操作步骤
2.1 前置条件
官方文档(Tooll v3.9 部分的"How to export")明确了两条硬性要求:
- 以 Release 模式运行 TiXL;
- 完整重新编译整个解决方案(包括 Player 工程),确保
Player.exe与编辑器版本一致。
另外,被导出的运算符必须满足:
- 具有Texture2D 类型的输出(
Outputs.FirstOrDefault()的类型必须是Texture2D,见 PlayerExporter.cs); - 在工程中作为Symbol 子实例存在(右键导出针对的是图上的某个子实例,而不是裸运算符定义)。
2.2 操作步骤
- 在编辑器中打开工程,选中要导出的运算符子实例(官方文档以
Demo_There为例); - 确认它的输出是
Texture2D; - 右键 → Export as Executable;
- TiXL 会先删除已存在的同名导出目录(TryRemoveExistingExportDir,若目录被占用会提示你关闭文件与资源管理器窗口),然后创建新目录并复制所有必需资源、音轨、库和
Player.exe; - 成功后编辑器会弹出成功对话框并自动用系统默认应用打开导出目录(ExportAndReport)。
2.3 TiXL v4 对音轨的特殊要求
v4 起,导出带音轨的工程时,音轨文件必须位于工程自己的Resources/文件夹内,例如:
c:\Users\<你的用户名>\Documents\TiXL\<你的工程>\Resources\<mysoundtrack.mp3>(具体路径取决于 Windows 版本与系统语言,中文系统通常为文档目录。)
源码层面的音轨查找逻辑见 TryFindSoundtrack:优先在被导出运算符内部查找主音轨(把某个 [AudioClip] 的 Display 参数设为Background image即标记为主音轨);找不到时回退到父级工程中已设置的主音轨,同时输出警告You should define soundtracks within the exported operators;两者都找不到则弹窗询问是否无音轨继续导出。音轨不仅用于播放,还决定 Player 的运行时长(何时结束或循环)以及音频分析,因此官方强烈建议在导出运算符内部显式定义它。
三、Project Settings → Executable:导出配置全参数
v4 中,导出行为的默认值来自Project Settings的Executable面板。配置模型定义在 CompositionSettings.ExportConfig,各字段及其默认值如下:
| 配置项 | 默认值 | 说明 |
|---|---|---|
Title | 空字符串(回退为运算符名) | 窗口标题,同时作为 exe 文件名(见下文);导出时若为空则用运算符名称,见 PlayerExporter.cs |
Author | 空(回退为程序集名) | 启动对话框标题栏显示的作者信息 |
DefaultWindowMode | Fullscreen | 默认窗口模式(全屏 / 窗口化) |
PreferredWidth | 1920 | 启动对话框与无对话框启动时的默认渲染宽度 |
PreferredHeight | 1080 | 默认渲染高度 |
ShowLogs | false | 默认是否弹出控制台日志窗口 |
SkipStartupDialog | false | 为true时跳过启动对话框,直接以工程默认值启动——官方文档称这在"安装部署"(installation)场景下非常有用 |
StripUnusedOperators | true | 是否剔除从导出输出不可达的运算符;导出缺内容时先关闭此项重试 |
上述配置会序列化进导出目录的exportSettings.json(文件名常量见 CoreSettings.cs),Player 启动时从该文件读取。写出的配置还额外包含OperatorId、ApplicationTitle、Author、BuildId(每次导出新生成的 GUID)、EditorVersion以及仅含DefaultOscPort、TimeClipSuspending的纯净ConfigData——源码注释明确说明这样做的原因:编辑器本机的输入设备名、MIDI 捕获限制、调试日志开关等机器相关配置绝不能泄漏到给别人运行的导出包中(PlayerExporter.cs)。
"Strip Unused Operators" 勾选框的 UI 入口在 ProjectSettingsWindow.cs。开启时,导出器会为每个被使用的符号重写其.t3符号文件,从符号 JSON 中剔除不可达的子运算符(见 TryWriteStrippedSymbolFiles,日志会报告每个包剔除了多少个未用子运算符)。这也是"导出内容变少"的常见根源:如果导出后运行缺内容,第一步就是按文档建议回到Project Settings → Executable关闭Strip Unused Operators重新导出。
四、导出内容收集的源码级细节
4.1 图遍历与资源扫描
RecursivelyCollectExportData 从被导出运算符的输出端沿连接递归回溯,逐实例加入收集集合,并在每个输入槽位上调用 CheckInputForResourcePath:
- 对
Usage == FilePath的字符串输入:解析地址并登记为共享资源(贴图、音轨等); - 对
Usage == DirectoryPath的字符串输入:递归枚举整个目录,把目录内所有文件都作为导出资源加入——也就是说,指向资源文件夹的目录型输入会把整棵子目录树打包进去。
4.2 自动播放的音频运算符
Strip Unused Operators只保留"输出可达"的运算符,但 Player 的渲染循环还会直接求值被导出运算符的某些无输出连接的子实例:自动播放的音频剪辑(AudioClipCollector)和游离音频源(AudioGraphCollector)。为此 CollectAutoCollectedOps 会把实现IAudioClipProvider/IAudioSource的子实例及其上游一并收集,避免把"听得到的"内容误删。
4.3 隐式共享资产与可选依赖裁剪
无论图里是否显式引用,导出始终附带三份 PBR/渲染管线必需资产(PlayerExporter.cs):
Lib:shaders/dx11/resolve-multisampled-depth-buffer-cs.hlsl Lib:pbr/studio_small_08-prefiltered.dds Lib:pbr/BRDF-LookUp.dds同时,Player 运行时的可选依赖(各 DLL)会按导出运算符实际声明的依赖进行过滤(DependencyFileFilter);runtimes/目录下非win-x64/win的原生库会被判定为"异机运行时"直接跳过(IsForeignRuntimeFile),因为 Player 目前仅面向 win-x64 运行。每次导出后日志会打印复制/跳过统计(Export copied N files (X MB), skipped M files (Y MB))以及被跳过的可选依赖模式列表。
4.4 exe 命名
导出后Player.exe会被重命名为工程Title对应的合法文件名(如标题为MyShow则生成MyShow.exe),见 RenamePlayerExecutable。源码注释解释了为何安全:apphost 中内嵌的Player.dll路径是构建时写死的,只有 exe 外壳改名、DLL 名不变,因此改名不影响启动;若标题非法或恰好叫 "Player" 则保持原名,手动重命名 exe 同样可行。
五、运行导出的可执行文件
5.1 启动对话框
Player.exe是独立应用,负责运算符加载、预初始化与音频播放。首次启动会弹出一个小对话框,询问:
- 使用哪块显示器(按对话框列表中的序号选择);
- 分辨率(该显示器的原生模式,或自定义尺寸);
- 是否全屏;
- 是否在控制台窗口显示日志。
对话框的默认值来自exportSettings.json中 Executable 配置的PreferredWidth/PreferredHeight/DefaultWindowMode/ShowLogs,而最后一次的选择会被记住(写到可执行文件旁的.temp/目录;该目录只读时回退到用户 AppData 目录)。若在工程设置中勾选Skip Startup Dialog,则直接以工程默认值启动——适合安装部署场景;Title和Author则分别设置窗口标题与对话框头部。
5.2 启动选项的解析优先级
从 PlayerStartupOptions.Resolve 可以看出,最终生效的配置按四层优先级从低到高叠加:
- 工程默认值(
exportSettings.json中的 Export 配置); - 上次使用的值(
.temp/中的记忆文件,--reset可清除); - 命令行开关(显式给出的参数覆盖上面两层);
- 启动对话框(除非被跳过)。
任何一层都只覆盖其明确给出的项,未给出的字段保持低层取值。显示器的解析还考虑了按名称匹配:即使显示器顺序变化,只要名称一致仍能命中;找不到已保存的显示器则回退到主显示器(ResolveDisplay)。
5.3 加载界面与加载报告
对话框之后,Player 显示深色加载画面:进度条 + 最新一条日志(LastLogLineWriter会截断多行消息只取首行,见 PlayerLoadReport.cs)。加载阶段依次为:加载运算符包 → 创建实例 → 准备音频 → 预热着色器(见 Program.cs),按Esc可取消。
加载完成后,日志与.temp/loadReport.json中会写入一份加载报告(PlayerLoadReport.LogAndSave),包含:
- 加载总耗时;
- 包 / 符号 / 实例数量;
- 编译着色器数与缓存命中着色器数;
- 资源文件数与总字节数(MB);
- 每个加载阶段各自的耗时(秒)。
官方文档指出,这份报告在"导出启动很慢"时是定位瓶颈的利器——无需附加调试器,读 JSON 即可。
六、命令行参数完整参考
官方文档给出并可由 CommandLineArgs 逐项印证的开关如下:
--display N 使用第 N 块显示器(从 1 开始,序号与启动对话框中列出的一致) --width N 渲染宽度(像素) --height N 渲染高度(像素) --windowed 以窗口模式运行 --fullscreen 在所选显示器上以无边框全屏运行 --show-logs 打开带日志的控制台窗口 --loop 时间线播完后自动重新开始 --novsync 关闭垂直同步 --no-dialog 跳过启动对话框,直接使用已解析的设置 --dialog 即使工程禁用了对话框也强制显示 --reset 忘记之前记住的启动设置 --help 显示帮助屏幕源码细节:--display在内部是 1-based(DisplayOneBased - 1转为 0-based 索引,-1 代表主显示器,见 PlayerStartupOptions.cs)。--reset会删除.temp/中记忆的启动设置文件(TryDeleteLastUsed)。
开关会覆盖"记忆设置"与"工程设置",因此可以用批处理脚本为每台演出机强制一套确定配置。官方文档给出的示例player-windowed.bat:
Player.exe --no-dialog --windowed --width 1280 --height 720 --display 2即:跳过对话框、窗口化、固定 1280×720、输出到第 2 块显示器。配合--loop与--novsync,可以组合出"通电自启、循环播放、无边框全屏"的展陈机方案;--reset则适合在更新安装包后清掉旧机器的记忆设置。
七、常见坑与排查指南
7.1 资源缺失:哪些情况 TiXL 扫不到
TiXL 只扫描运算符的字符串参数且带有 FilePath 属性的输入来收集资源。官方文档明确指出以下情况不会被自动收集:
- 动态拼接路径:在代码/图中用字符串拼接出路径再连到文件路径参数上;
- 使用 [FilesInDirectories] 运算符;
- 自定义字体;
- 资源不在工程的
./Resources/文件夹内,或使用了绝对路径(如c:/myfile.mp3)。
处理方式:手动把文件放进导出目录的Resources/文件夹;同时 TiXL 在导出时会就这些问题给出警告。源码印证:资源扫描只发生在StringInputUi.Usage为FilePath/DirectoryPath的输入上(PlayerExporter.cs),动态拼接产生的值在导出时刻并不存在于参数值里,自然无法登记。
7.2 Player 启动异常:看日志
若Player.exe启动结果不符合预期,官方建议:查看可执行文件旁的.temp/Log/目录,扫描其中的日志文件找问题。加载报告loadReport.json与日志同目录,结合第六节的字段逐阶段核对耗时,可快速定位"卡在哪一步"。
7.3 着色器首次启动慢
导出包会自带编辑器为导出图编译过的所有着色器字节码(存放在导出的ShaderCache/),因此 Player 首次启动不需要重新编译;Player 自己也会在.temp/ShaderCache/维护一份缓存。前提是:导出前先在编辑器中查看一次该运算符,让相关着色器在编辑器里完成编译,导出时 ShaderCompiler.ExportCacheEntries 才有可导出的缓存条目(日志会打印Exported N precompiled shaders.)。
7.4 导出内容不完整
优先按官方建议处理:在Project Settings → Executable中关闭Strip Unused Operators再导出一次。该开关默认开启(true),会把图遍历不到的运算符从符号文件中剔除;关闭后则完整保留符号定义,代价是包体更大。
八、高级自定义:让导出程序读取你自己的配置
对于"同一套节目在不同场地显示不同文字/参数"这类需求,官方文档给出的思路是:使用lib.io.file的 [ReadFile] 运算符,在启动时读取你自己的设置文件,并在导出的运算符内自行解析。例如把场地名、标语、IP 地址等放在一个纯文本或 JSON 文件里,随包分发,Player 启动后由 ReadFile 读入并驱动 UI。结合 7.1 的注意事项,这类自定义文件请放在工程的Resources/内并显式引用,确保能被导出收集。
九、结语
从"右键导出"到"展陈机全屏循环播放",TiXL 的可执行文件导出是一条高度自动化的流水线:图遍历收集内容、符号裁剪控制体积、预编译着色器保证冷启动速度、四层优先级解析启动配置,再加上一份结构化的加载报告用于诊断。理解 PlayerExporter.cs 与 PlayerStartupOptions.cs 的实现细节,能让你在遇到"缺内容、启动慢、配置不对"三类经典问题时,第一时间从源码和.temp/日志中找到答案。
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考