20 个版本 (破坏性)
0.15.0 | 2024 年 8 月 5 日 |
---|---|
0.14.0 | 2024 年 5 月 28 日 |
0.13.0 | 2024 年 2 月 2 日 |
0.11.0 | 2023 年 11 月 17 日 |
0.3.0 | 2022 年 11 月 23 日 |
#8 在 图形 API 中
每月 76,023 次下载
在 555 个 crate 中使用 (5 个直接使用)
355KB
9K SLoC
Naga 组织集成库 (naga-oil
) 是一个用于组合和操作着色器的 crate。
compose
提供了一个模块化着色器组合框架prune
将着色器精简到所需的部分
以及可能对外部不太有用的部分
derive
允许将多个着色器中的项目导入到单个着色器中redirect
通过替换函数调用和修改绑定来修改着色器
组合
组合模块允许从模块(它们自身也是着色器)构建着色器。
它通过将着色器视为模块,并
- 独立地将每个模块构建为 naga IR
- 为每种支持的语言创建 "头文件",这些文件用于构建依赖的模块/着色器
- 通过将着色器 IR 与导入模块的 IR 相结合来制作最终着色器
对于多个具有大型公共导入的小型着色器,这可能比解析每个着色器的完整源代码更快,并且允许以更干净、更模块化的方式构建着色器,同时更好地控制作用域。
导入
着色器可以作为模块添加到组合器中。这使得它们的类型、常量、变量和函数可用于导入它们的模块/着色器。请注意,如果模块定义具有绑定的全局变量,则导入模块将影响最终着色器的全局状态。
模块可以包含一个 #define_import_path
指令,用于命名模块
#define_import_path my_module
fn my_func() -> f32 {
return 1.0;
}
模块名称还可以作为参数指定给 Composer::add_composable_module
。
着色器可以使用 #import
指令(可选带有 as
名称)导入模块
#import my_module;
#import my_other_module as mod2;
fn main() -> f32 {
let x = my_module::my_func();
let y = mod2::my_other_func();
return x*y;
}
或导入逗号分隔的各个项目列表
#import my_module::{my_func, my_const}
fn main() -> f32 {
return my_func(my_const);
}
支持一些 Rust 风格的导入语法,可以直接使用完全限定的项目名称导入
#import my_package::{
first_module::{item_one as item, item_two},
second_module::submodule,
}
fn main() -> f32 {
return item + item_two + submodule::subitem + my_package::third_module::item;
}
module::self
和 module::*
目前不支持。
导入可以嵌套 - 模块可以导入其他模块,但不能递归地导入。当添加新模块时,所有其 #import
必须已添加。同一模块可以被导入树中的不同模块多次导入。命名空间没有重叠,因此可以在不同的模块中使用相同的函数名(或类型、常量或变量名)。
注意:最终的着色器将包括使用到的任何导入项目的所需依赖项(绑定、全局变量、常量、其他函数),但不会包括导入模块的其余部分。
覆盖函数
可以使用 virtual
关键字声明虚函数
virtual fn point_light(world_position: vec3<f32>) -> vec3<f32> { ... }
导入模块中定义的虚函数可以使用 override
关键字覆盖
#import bevy_pbr::lighting as Lighting
override fn Lighting::point_light (world_position: vec3<f32>) -> vec3<f32> {
let original = Lighting::point_light(world_position);
let quantized = vec3<u32>(original * 3.0);
return vec3<f32>(quantized) / 3.0;
}
覆盖必须在顶层着色器中声明,或者包含覆盖的模块必须作为 additional_import
导入到 Composer::add_composable_module
或 Composer::make_naga_module
调用中。由于树摇,使用 #import
导入带有覆盖的模块将不会工作。
覆盖函数定义会导致整个着色器作用域中原始函数的所有调用都被替换为对新函数的调用,但覆盖函数内部的调用除外。
覆盖函数的签名必须与基函数匹配。
覆盖可以在最终着色器导入树的任何位置指定。
可以对同一函数应用多个覆盖。例如,给定
- 包含函数
f
的模块a
, - 导入
a
并包含覆盖函数override a::f
的模块b
, - 导入
a
和b
并包含覆盖函数override a::f
的模块c
,
那么 b
和 c
都指定了对 a::f
的覆盖。
模块 b
中声明的 override fn a::f
可能在其体内部调用 a::f
。
模块 c
中声明的 override fn a::f
可能在其体内部调用 a::f
,但调用将被重定向到 b::f
。
对 a::f
(在模块 a
或 b
内,或任何其他地方)的任何其他调用都将重定向到 c::f
。
通过这种方式,可以应用一系列或堆栈形式的覆盖。
同一函数的不同覆盖可以在不同的导入分支中指定。最终堆栈将根据导入树中覆盖首次出现的位置进行排序(使用深度优先搜索)。
请注意,模块/着色器的导入按顺序处理,但它们在当前着色器/模块的主体之前进行处理,无论它们在该模块中的位置如何,因此无法导入包含覆盖的模块并将调用注入到导入覆盖之前。相反,您可以创建两个每个都包含覆盖的模块,并将它们导入到父模块/着色器中,以按所需的方式排序。
覆盖函数目前只能定义在 wgsl 中。
如果启用了 override_any
铁砧功能,则不需要对被覆盖的函数使用 virtual
关键字。
语言
模块可以用 GLSL 或 WGSL 编写。具有入口点的着色器可以作为模块导入(前提是它们具有 #define_import_path
指令)。入口点可以通过其名称(对于 WGSL)或通过 module::main
(对于 GLSL)从导入的模块中调用。
最终着色器也可以用 GLSL 或 WGSL 编写。对于 GLSL 用户,必须通过 ShaderType 参数指定着色器是顶点着色器还是片段着色器(GLSL 计算着色器不受支持)。
预处理
生成最终着色器或添加可组合的模块时,必须提供一组 shader_def
字符串/值对。值可以是 bool(ShaderDefValue::Bool
)、i32(ShaderDefValue::Int
)或 u32(ShaderDefValue::UInt
)。
这些允许对模块和最终着色器的一部分进行条件编译。条件编译使用 #if
/ #ifdef
/ #ifndef
、#else
和 #endif
预处理器指令执行。
fn get_number() -> f32 {
#ifdef BIG_NUMBER
return 999.0;
#else
return 0.999;
#endif
}
#ifdef
指令在定义名称存在于输入绑定集时匹配(无论值如何)。#ifndef
指令正好相反。
#if
指令需要一个定义名称、一个运算符和一个比较值。
- 定义名称必须是提供的
shader_def
名称。 - 运算符必须是以下之一:
==
、!=
、>=
、>
、<
、<=
。 - 当与
ShaderDefValue::Int
或ShaderDefValue::Uint
进行比较时,值必须是一个整数字面量;当与ShaderDef::Bool
进行比较时,值为true
或false
。
着色器定义还可以在着色器源中使用#SHADER_DEF
或#{SHADER_DEF}
,并将用它们的值进行替换。
预处理器的分支指令(ifdef
、ifndef
和if
)可以与#else
一起使用,以创建更复杂的控制流。
fn get_number() -> f32 {
#ifdef BIG_NUMBER
return 999.0;
#else if USER_NUMBER > 1
return f32(#USER_NUMBER)
#else
return 0.999;
#endif
}
着色器定义可以通过使用#define
指令在顶级着色器开始处创建或覆盖。
#define USER_NUMBER 42
如果未指定,创建的值默认为true
。
错误报告
使用错误emit_to_string
方法可以提供错误报告的codespan。这需要启用验证,默认情况下是启用的。Composer::non_validating()
产生一个非验证的作曲家,不能提供准确的错误报告。
修剪
- 根据指定的所需输出从着色器中删除死代码和绑定。旨在用于从任意顶点/片段着色器构建减少深度和/或法线着色器。
待定文档
重定向
- 重定向函数调用
- 待定:重新绑定全局绑定
- 将来某天:在均匀、纹理和缓冲区访问之间进行转换,以便可以在间接传递中使用为直接传递编写的着色器
待定文档
推导
- 从一个或多个现有模块的部分构建一个单一的自我包含的naga模块
待定文档
依赖项
~8–17MB
~212K SLoC