17 个版本 (4 个重大变更)
0.5.2 | 2022 年 3 月 16 日 |
---|---|
0.4.1 | 2022 年 1 月 6 日 |
0.3.1 | 2021 年 12 月 16 日 |
0.2.5 | 2021 年 11 月 3 日 |
0.1.1 | 2021 年 3 月 7 日 |
#264 in 图形 API
在 imgui-d3d12-renderer 中使用
2MB
42K SLoC
rusty-d3d12
本项目为 D3D12 API 提供了低级绑定。它使用 rust-bindgen
生成原始绑定(不同于 d3d12-rs
crate),但旨在提供符合 Rust 习惯的 API(不同于 winapi
或 windows-rs
crate 中的原始 D3D12 包装器)。
功能
- 为
ID3D12*
接口和 POD 结构提供包装器。后者标记为#[repr)
,以便它们可以作为原生类型的直接替换,但提供类型安全的获取器和设置器。设置器有两种形式:with_*, mut self, ...) -> Self
和set_*, &mut self, ...) -> &Self
,分别用于构建新结构和修改现有结构。 - 为 D3D12 枚举和位标志提供类型安全的包装器(有关详细信息,请参阅 enum_wrappers.rs)
D3D12
和DXGI
前缀已从所有类型、函数和枚举变体中去除(例如,这个库公开了CommandListType::Direct
而不是D3D12_COMMAND_LIST_TYPE_DIRECT
),因为使用它的人很可能已经知道它所包装的 API 的名称(毕竟它在 crate 名称中提到了),并且不需要不断提醒它 :) 此外,所有类型和函数名称都根据官方 Rust 代码风格进行了修改(例如,get_gpu_descriptor_handle_for_heap_start
而不是GetGPUDescriptorHandleForHeapStart
)。请注意,大多数但 不是 所有枚举变体名称都已转换,因此其中一些将在未来版本中更改- D3D12 Agility SDK 已集成到库中,并与其一起分发(有关导出所需符号的示例,请参阅
heterogeneous_multiadapter.rs
)。当前 SDK 版本是1.600.10
- PIX 标记(它们需要启用
pix
功能,该功能默认关闭,以免为不需要的人引入对WinPixEventRuntime.dll
的依赖) - 通过
Clone
和Drop
特性实现 COM 对象引用计数 - D3D12 调试回调支持(请注意,需要显式激活
debug_callback
功能,因为ID3D12InfoQueue1
接口仅支持 Windows 11) - 包装 API 调用的便利宏(例如
dx_call!
和dx_try!
) - 尚未涵盖的 API 可通过原始绑定访问,并且可以通过
conversion_assist.py
脚本以半自动方式包装新的 API - 大多数由
rusty-d3d12
提供的 API 都没有标记为unsafe
,因为它们污染客户端代码,并且显然会有很多坏处:显然,像这样的东西不应该作为示例或高质量 Rust 代码示例被处理,因为它们的目的是仅仅展示 API。
示例
- 创建调试控制器并启用验证
let debug_controller = Debug::new().expect("cannot create debug controller");
debug_controller.enable_debug_layer();
debug_controller.enable_gpu_based_validation();
debug_controller.enable_object_auto_name();
- 创建描述符堆
let rtv_heap = device
.create_descriptor_heap(
&DescriptorHeapDesc::default()
.with_heap_type(DescriptorHeapType::Rtv)
.with_num_descriptors(FRAMES_IN_FLIGHT),
)
.expect("Cannot create RTV heap");
rtv_heap
.set_name("RTV heap")
.expect("Cannot set RTV heap name");
- 检查跨适配器纹理是否受支持
let mut feature_data = FeatureDataOptions::default();
device
.check_feature_support(Feature::D3D12Options, &mut feature_data)
.expect("Cannot check feature support");
let cross_adapter_textures_supported = feature_data.cross_adapter_row_major_texture_supported();
- 创建网格着色器 PSO
let ms_bytecode = ShaderBytecode::new(&mesh_shader);
let ps_bytecode = ShaderBytecode::new(&pixel_shader);
let pso_subobjects_desc = MeshShaderPipelineStateDesc::default()
.with_root_signature(root_signature)
.with_ms_bytecode(&ms_bytecode)
.with_ps_bytecode(&ps_bytecode)
.with_rasterizer_state(
RasterizerDesc::default().with_depth_clip_enable(false),
)
.with_blend_state(BlendDesc::default())
.with_depth_stencil_state(
DepthStencilDesc::default().with_depth_enable(false),
)
.with_primitive_topology_type(PrimitiveTopologyType::Triangle)
.with_rtv_formats(&[Format::R8G8B8A8Unorm]);
let pso_desc = PipelineStateStreamDesc::default()
.with_pipeline_state_subobject_stream(
pso_subobjects_desc.as_byte_stream(),
);
let pso = device
.create_pipeline_state(&pso_desc)
.expect("Cannot create PSO");
在 examples 目录中可以找到多个可运行示例。请注意,它们的代码可能很脏,并且包含一些(非关键)错误,因此它们不应该被视为示例或高质量的 Rust 代码示例,因为它们的目的是仅仅展示 API。
当前实现示例包括
- hello_triangle
- hello_texture(基于 Microsoft 示例)
- dynamic_indexing_sm66(基于 Microsoft 示例,对使用 SM6.6 动态资源和 Agility SDK 导出进行了更改)
- hello_mesh_shaders (基于微软示例,略有改动)
- heterogeneous_multiadapter (紧密遵循微软示例,因此是目前推荐的学习示例,如果您想逐行比较与C++代码的话)
- interprocess_communication (演示了两个进程(生产者和消费者)通过共享堆的使用)
- n_body_gravity (基于微软示例,但使用不同的线程模型)
本项目下一步计划是覆盖DXR API并提供相应的示例。
API稳定性
当前库正在积极开发中,因此小版本之间可能会发生破坏性更改(但在补丁版本之间不应发生)。发布版本1.0
后,将应用标准的语义版本。
进行更改
如上所述,该库仍在开发中,因此欢迎所有贡献:)
如何添加缺少的结构体或枚举类型
如果相关类型已经存在于预生成的d3d12.rs
中,您可以使用conversion_assist.py脚本为您生成大部分(或有时是全部)代码。
-
来生成结构体包装器
- 运行
python tools/conversion_assist.py struct
- 粘贴结构体的原始定义,不带属性和派生(即从
pub struct
开始)和impl块,例如:
pub struct D3D12_ROOT_DESCRIPTOR1 { pub ShaderRegister: UINT, pub RegisterSpace: UINT, pub Flags: D3D12_ROOT_DESCRIPTOR_FLAGS, }
- 按
Enter
键。 - 脚本将为您提供样板包装器结构体定义,例如:
/// Wrapper around D3D12_ROOT_DESCRIPTOR1 structure #[derive(Default, Debug, Hash, PartialOrd, Ord, PartialEq, Eq, Clone)] #[repr(transparent)] pub struct RootDescriptor(pub(crate) D3D12_ROOT_DESCRIPTOR1); impl RootDescriptor { pub fn set_shader_register(&mut self, shader_register: u32) -> &mut Self { self.0.ShaderRegister = shader_register; self } pub fn with_shader_register(mut self, shader_register: u32) -> Self { self.set_shader_register(shader_register); self } pub fn shader_register(&self) -> u32 { self.0.ShaderRegister } pub fn set_register_space(&mut self, register_space: u32) -> &mut Self { self.0.RegisterSpace = register_space; self } pub fn with_register_space(mut self, register_space: u32) -> Self { self.set_register_space(register_space); self } pub fn register_space(&self) -> u32 { self.0.RegisterSpace } pub fn set_flags(&mut self, flags: RootDescriptorFlags) -> &mut Self { self.0.Flags = flags.bits(); self } pub fn with_flags(mut self, flags: RootDescriptorFlags) -> Self { self.set_flags(flags); self } pub fn flags(&self) -> RootDescriptorFlags { unsafe { RootDescriptorFlags::from_bits_unchecked(self.0.Flags) } } }
- 请注意,原始未类型化枚举
D3D12_ROOT_DESCRIPTOR_FLAGS
在获取器和设置器的签名中自动更改为对应的包装器RootDescriptorFlags
:这是可能的,因为脚本解析了已知类型的enum_wrappers.rs
并试图识别它们。 - 如果需要(即如果原始结构体包含原始指针),请添加带有生命周期指定符的
PhantomData
(请参阅src/struct_wrappers.rs中的示例)。 - 将最终类型定义添加到
struct_wrappers.rs
并提交一个PR:)
- 运行
-
来生成枚举包装器
- 运行
python tools/conversion_assist.py enum
- 粘贴枚举变体和来自
d3d12.rs
的类型别名
pub const D3D12_DESCRIPTOR_RANGE_TYPE_D3D12_DESCRIPTOR_RANGE_TYPE_SRV: D3D12_DESCRIPTOR_RANGE_TYPE = 0; pub const D3D12_DESCRIPTOR_RANGE_TYPE_D3D12_DESCRIPTOR_RANGE_TYPE_UAV: D3D12_DESCRIPTOR_RANGE_TYPE = 1; pub const D3D12_DESCRIPTOR_RANGE_TYPE_D3D12_DESCRIPTOR_RANGE_TYPE_CBV: D3D12_DESCRIPTOR_RANGE_TYPE = 2; pub const D3D12_DESCRIPTOR_RANGE_TYPE_D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: D3D12_DESCRIPTOR_RANGE_TYPE = 3; pub type D3D12_DESCRIPTOR_RANGE_TYPE = ::std::os::raw::c_int;
- 如您所注意到的,
rust-bindgen
在每个变体中都重复枚举名称,因此脚本将询问您想要从变体中删除的部分;在这种情况下是D3D12_DESCRIPTOR_RANGE_TYPE_D3D12_DESCRIPTOR_RANGE_TYPE_
- 将自动生成的枚举定义粘贴到src/enum_wrappers.rs。
- 运行
-
如果您的枚举变体不是互斥的(即可以按位或等),则遵循相同的程序,但使用
python tools/conversion_assist.py flags
:脚本将生成bitflags定义。
如何添加缺少的函数
遗憾的是,conversion_assist.py
还不支持生成函数定义,因此需要手动完成 :(
运行 rust-bindgen
如果所需的函数或类型尚未存在于交付的 d3d12.rs
中(即新的 Agility SDK 已发布但尚未集成到 rusty-d3d12
中),则需要在更新 Agility SDK 后在工作区上运行 rust-bindgen
。
当作为 Cargo 依赖项使用时,rusty-d3d12
默认不生成绑定(除了增加构建时间外,运行 rust-bindgen
需要 libclang.dll
,该文件在某些系统上可能不存在,并且由于其大小过大,无法通过 crates.io
进行 vendoring)。因此,作为先决条件,Cargo 应能够在 LIBCLANG_PATH
环境变量中设置的路径下找到此 DLL。
使用 rust-bindgen
生成新的原始绑定文件(d3d12_bindings.rs
,请参阅构建脚本以获取详细信息)后,应将其从 $OUT_DIR
复制到 src/raw_bindings 目录,并将其重命名为 d3d12.rs
。
依赖项
~1.1–4MB
~72K SLoC