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 中使用

MIT 许可证

2MB
42K SLoC

Documentation Crates.io

rusty-d3d12

本项目为 D3D12 API 提供了低级绑定。它使用 rust-bindgen 生成原始绑定(不同于 d3d12-rs crate),但旨在提供符合 Rust 习惯的 API(不同于 winapiwindows-rs crate 中的原始 D3D12 包装器)。

功能

  • ID3D12* 接口和 POD 结构提供包装器。后者标记为 #[repr),以便它们可以作为原生类型的直接替换,但提供类型安全的获取器和设置器。设置器有两种形式:with_*, mut self, ...) -> Selfset_*, &mut self, ...) -> &Self,分别用于构建新结构和修改现有结构。
  • 为 D3D12 枚举和位标志提供类型安全的包装器(有关详细信息,请参阅 enum_wrappers.rs
  • D3D12DXGI 前缀已从所有类型、函数和枚举变体中去除(例如,这个库公开了 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 的依赖)
  • 通过 CloneDrop 特性实现 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。

当前实现示例包括

本项目下一步计划是覆盖DXR API并提供相应的示例。

API稳定性

当前库正在积极开发中,因此小版本之间可能会发生破坏性更改(但在补丁版本之间不应发生)。发布版本1.0后,将应用标准的语义版本。

进行更改

如上所述,该库仍在开发中,因此欢迎所有贡献:)

如何添加缺少的结构体或枚举类型

如果相关类型已经存在于预生成的d3d12.rs中,您可以使用conversion_assist.py脚本为您生成大部分(或有时是全部)代码。

  • 来生成结构体包装器

    1. 运行python tools/conversion_assist.py struct
    2. 粘贴结构体的原始定义,不带属性和派生(即从pub struct开始)和impl块,例如:
    pub struct D3D12_ROOT_DESCRIPTOR1 {
        pub ShaderRegister: UINT,
        pub RegisterSpace: UINT,
        pub Flags: D3D12_ROOT_DESCRIPTOR_FLAGS,
    }
    
    1. Enter键。
    2. 脚本将为您提供样板包装器结构体定义,例如:
    /// 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) }
        }
    }
    
    1. 请注意,原始未类型化枚举D3D12_ROOT_DESCRIPTOR_FLAGS在获取器和设置器的签名中自动更改为对应的包装器RootDescriptorFlags:这是可能的,因为脚本解析了已知类型的enum_wrappers.rs并试图识别它们。
    2. 如果需要(即如果原始结构体包含原始指针),请添加带有生命周期指定符的PhantomData(请参阅src/struct_wrappers.rs中的示例)。
    3. 将最终类型定义添加到struct_wrappers.rs并提交一个PR:)
  • 来生成枚举包装器

    1. 运行python tools/conversion_assist.py enum
    2. 粘贴枚举变体和来自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;
    
    1. 如您所注意到的,rust-bindgen在每个变体中都重复枚举名称,因此脚本将询问您想要从变体中删除的部分;在这种情况下是D3D12_DESCRIPTOR_RANGE_TYPE_D3D12_DESCRIPTOR_RANGE_TYPE_
    2. 将自动生成的枚举定义粘贴到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