跳转至

自定义渲染管线原理

接口、Shader DSL 和接入示例见 API reference。

描述与执行分离

管线用不可变描述保存绘制状态、执行阶段与排序信息;注册表负责生效、替换和释放。 描绘同一内容的配置可以复用,持有 GPU 资源和宿主状态的执行对象则属于某一次注册。 一条注册被替换后,旧句柄仍代表旧对象,避免卸载旧模块时误删新对象。

世界绘制按宿主阶段组织。同一阶段先按优先级,再按资源标识排序。注册表缓存每阶段的执行快照, 变化时失效,绘制遍历不直接依赖可变注册列表。单条回调失败不会阻止同阶段的其他管线。 姿态栈按回调隔离;缓冲批次必须在该次绘制内完成。

所有权与分代资源

静态注册属于客户端生命周期,动态内容属于可关闭的作用域。父作用域拥有子作用域,资源按注册的 逆序释放。绘制状态和纹理变体缓存属于注册对象本身,同名替换得到独立缓存。

shader 句柄跨资源重载保持稳定,内部编译实例按代替换。只有成功安装新实例才推进 generation。 可编辑参数独立于编译实例保存,所以资源重载不丢失用户参数。不同注册之间不能靠相同 ID 共享生命周期。

Shader 准备与渲染线程

生成 IR 到 GLSL/资源描述的转换是 CPU 工作,由有界后台执行器处理;OpenGL 编译与链接必须回到 渲染线程。相同程序的并发请求共享一次转换,转换结果进入有界缓存。准备队列满时明确失败, 不会退回调用线程做同步转换。

每帧编译队列限制启动数量与启动前预算,并对不同 owner 做调度。一次驱动编译已经开始后不能被 中途切断,因此预算不能视为严格的帧耗时上限。关闭和替换会取消尚未开始的准备任务。 失败策略决定禁用当前管线或中止正在进行的重载;晚到的预加载没有可中止的重载上下文。

Java DSL 的执行模型

Java builder 立即构建类型化 shader IR,不解析 Java AST,也不反编译 lambda。Java 条件和循环只 控制 IR 的构造,运行时 GPU 控制流必须显式表示在 IR 中。复用一个表达式对象不等于创建 GLSL 局部变量。 图形程序在构建时检查顶点输出和片元输入的名称、类型,以及跨阶段 uniform/sampler 声明的一致性。 当前后端将 IR 转换为 GLSL 150 和 Minecraft core-shader 资源。

效果实例与后处理图

同类效果共享渲染器和管线状态,每实例保存位置、生命周期和插值数据。客户端 tick 推进效果, 绘制阶段使用插值包围体剔除,必要时按距相机远近排序。世界切换会清理实例和后处理目标池。

后处理以声明式读写关系和显式依赖构建有向无环图。每个中间目标有明确的生产者,构建时拒绝循环、 多写者和未声明目标;单 pass 不读写同一个目标,避免 framebuffer feedback。目标物理标识包含图 ID, 避免多个图的同名逻辑目标冲突。目标按描述和实际尺寸复用,最后一条图注册释放其管理的目标。

执行前准备可以取消本帧,不分配无用目标。每帧的准备与 pass 共享临时数据容器,目标访问受声明约束。 后处理结束或异常后恢复宿主主目标。直接操作底层 GPU 对象会绕过这些约束,状态恢复仍由调用方承担。