From b6d1207a80e8fbe6fc1fb024103f6f12e7606c0c Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 29 Oct 2026 20:27:00 +0100 Subject: [PATCH] d3d12: Port from `windows` to `d3d12-agility` bindings Replace the `windows` crate (0.53..=0.62) with the self-contained `d3d12-agility` bindings (generated from the D3D12 Agility SDK 1.722-preview headers on top of `windows-core` 0.100), so that the D3D12 types in our public API are shared with the rest of the `d3d12-agility` ecosystem. This is an intentional breaking change: every D3D12/DXGI type in the `d3d12` module API now comes from `d3d12_agility` instead of `windows::Win32::Graphics::*`. - Enums and flags are plain integer aliases now, so `.0` accesses in the visualizer are gone. - `CheckFeatureSupport()` returns a raw `HRESULT` that we `.ok()`. - Castable formats are passed as explicit count + pointer pairs. - `E_OUTOFMEMORY` and `DXGI_ERROR_*` codes are not part of `d3d12-agility`, so they are defined locally. - The example no longer needs `windows` either. `d3d12-agility` requires Rust 1.95 and, being a git dependency with `edition = "2024"`, its manifest must be parsed by Cargo for every target and feature set. Bump the crate MSRV to 1.95 accordingly (also in CI and the README), which in turn enables `clippy::manual_is_multiple_of`. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 4 ++-- Cargo.toml | 24 +++++--------------- README.md | 22 +++++++++--------- README.tpl | 6 ++--- examples/d3d12-buffer-winrs.rs | 41 +++++++++++++--------------------- src/d3d12/mod.rs | 38 +++++++++++++++---------------- src/d3d12/visualizer.rs | 14 ++++++------ src/lib.rs | 21 +++++++++-------- 8 files changed, 71 insertions(+), 99 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ba27a75..c1082259 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,9 +15,9 @@ jobs: - os: macos-latest backend: vulkan,metal version: - - msrv: 1.71.0 + - msrv: 1.95.0 features: std - - msrv: 1.81.0 + - msrv: 1.95.0 features: hashbrown runs-on: ${{ matrix.target.os }} steps: diff --git a/Cargo.toml b/Cargo.toml index 8e6f5869..11080f11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/Traverse-Research/gpu-allocator" repository = "https://github.com/Traverse-Research/gpu-allocator" keywords = ["vulkan", "memory", "allocator"] documentation = "https://docs.rs/gpu-allocator/" -rust-version = "1.71" +rust-version = "1.95" include = [ "/README.md", @@ -49,28 +49,16 @@ objc2-metal = { version = "0.3.2", default-features = false, features = [ "std", ], optional = true } -[target.'cfg(windows)'.dependencies.windows] -version = ">=0.53, <=0.62" -features = [ - "Win32_Graphics_Direct3D12", - "Win32_Graphics_Dxgi_Common", -] -optional = true +[target.'cfg(windows)'.dependencies] +# Only needed for D3D12, and requires Rust 1.95. +d3d12-agility = { git = "https://github.com/Traverse-Research/d3d12-agility-rs", rev = "873651e", optional = true } +windows-core = { version = "0.100", default-features = false, optional = true } [dev-dependencies] # Enable the "loaded" feature to be able to access the Vulkan entrypoint. ash = { version = "0.38", default-features = false, features = ["debug", "loaded"] } env_logger = "0.11" -[target.'cfg(windows)'.dev-dependencies.windows] -# API-breaks since Windows 0.58 only affect our examples -version = ">=0.58, <=0.62" -features = [ - "Win32_Graphics_Direct3D", - "Win32_Graphics_Direct3D12", - "Win32_Graphics_Dxgi_Common", -] - [target.'cfg(target_vendor = "apple")'.dev-dependencies] objc2-metal = { version = "0.3.2", default-features = false, features = [ "MTLPixelFormat", @@ -92,7 +80,7 @@ required-features = ["metal"] std = ["presser/std"] visualizer = ["dep:egui", "dep:egui_extras"] vulkan = ["dep:ash"] -d3d12 = ["dep:windows"] +d3d12 = ["dep:d3d12-agility", "dep:windows-core"] metal = ["dep:objc2", "dep:objc2-metal", "dep:objc2-foundation"] # Enables the FreeListAllocator when `std` is not enabled by using the `hashbrown` crate hashbrown = ["dep:hashbrown"] diff --git a/README.md b/README.md index 0eaff06c..0d9f68ce 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE-MIT) [![LICENSE](https://img.shields.io/badge/license-apache-blue.svg?logo=apache)](LICENSE-APACHE) [![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](../main/CODE_OF_CONDUCT.md) -[![MSRV](https://img.shields.io/badge/rustc-1.71.0+-ab6000.svg)](https://blog.rust-lang.org/2023/07/13/Rust-1.71.0.html) +[![MSRV](https://img.shields.io/badge/rustc-1.95.0+-ab6000.svg)](https://blog.rust-lang.org/) [![Banner](banner.png)](https://traverseresearch.nl) @@ -84,20 +84,20 @@ use gpu_allocator::d3d12::*; use gpu_allocator::MemoryLocation; -let buffer_desc = Direct3D12::D3D12_RESOURCE_DESC { - Dimension: Direct3D12::D3D12_RESOURCE_DIMENSION_BUFFER, +let buffer_desc = d3d12_agility::D3D12_RESOURCE_DESC { + Dimension: d3d12_agility::D3D12_RESOURCE_DIMENSION_BUFFER, Alignment: 0, Width: 512, Height: 1, DepthOrArraySize: 1, MipLevels: 1, - Format: Dxgi::Common::DXGI_FORMAT_UNKNOWN, - SampleDesc: Dxgi::Common::DXGI_SAMPLE_DESC { + Format: d3d12_agility::DXGI_FORMAT_UNKNOWN, + SampleDesc: d3d12_agility::DXGI_SAMPLE_DESC { Count: 1, Quality: 0, }, - Layout: Direct3D12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - Flags: Direct3D12::D3D12_RESOURCE_FLAG_NONE, + Layout: d3d12_agility::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + Flags: d3d12_agility::D3D12_RESOURCE_FLAG_NONE, }; let allocation_desc = AllocationCreateDesc::from_d3d12_resource_desc( &allocator.device(), @@ -106,13 +106,13 @@ let allocation_desc = AllocationCreateDesc::from_d3d12_resource_desc( MemoryLocation::GpuOnly, ); let allocation = allocator.allocate(&allocation_desc).unwrap(); -let mut resource: Option = None; +let mut resource: Option = None; let hr = unsafe { device.CreatePlacedResource( allocation.heap(), allocation.offset(), &buffer_desc, - Direct3D12::D3D12_RESOURCE_STATE_COMMON, + d3d12_agility::D3D12_RESOURCE_STATE_COMMON, None, &mut resource, ) @@ -187,9 +187,7 @@ gpu-allocator = { version = "0.28.0", default-features = false } ## Minimum Supported Rust Version -The MSRV for this crate and the `vulkan`, `d3d12` and `metal` features is Rust **1.71**. - -The `no_std` support requires Rust **1.81** or higher because `no_std` support of dependency `thiserror` requires `core::error::Error` which is stabilized in **1.81**. +The MSRV for this crate and the `vulkan`, `d3d12` and `metal` features is Rust **1.95**, as required by the [`d3d12-agility`](https://github.com/Traverse-Research/d3d12-agility-rs) dependency of the `d3d12` feature. Because it is a git dependency, Cargo needs to parse its manifest (which uses `edition = "2024"`) even when the `d3d12` feature is disabled or when building for non-Windows targets. Any other features such as the `visualizer` (with all the `egui` dependencies) may have a higher requirement and are not tested in our CI. diff --git a/README.tpl b/README.tpl index 76d9c0f4..6b855f02 100644 --- a/README.tpl +++ b/README.tpl @@ -6,7 +6,7 @@ [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE-MIT) [![LICENSE](https://img.shields.io/badge/license-apache-blue.svg?logo=apache)](LICENSE-APACHE) [![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](../main/CODE_OF_CONDUCT.md) -[![MSRV](https://img.shields.io/badge/rustc-1.71.0+-ab6000.svg)](https://blog.rust-lang.org/2023/07/13/Rust-1.71.0.html) +[![MSRV](https://img.shields.io/badge/rustc-1.95.0+-ab6000.svg)](https://blog.rust-lang.org/) [![Banner](banner.png)](https://traverseresearch.nl) @@ -44,9 +44,7 @@ gpu-allocator = { version = "0.28.0", default-features = false } ## Minimum Supported Rust Version -The MSRV for this crate and the `vulkan`, `d3d12` and `metal` features is Rust **1.71**. - -The `no_std` support requires Rust **1.81** or higher because `no_std` support of dependency `thiserror` requires `core::error::Error` which is stabilized in **1.81**. +The MSRV for this crate and the `vulkan`, `d3d12` and `metal` features is Rust **1.95**, as required by the [`d3d12-agility`](https://github.com/Traverse-Research/d3d12-agility-rs) dependency of the `d3d12` feature. Because it is a git dependency, Cargo needs to parse its manifest (which uses `edition = "2024"`) even when the `d3d12` feature is disabled or when building for non-Windows targets. Any other features such as the `visualizer` (with all the `egui` dependencies) may have a higher requirement and are not tested in our CI. diff --git a/examples/d3d12-buffer-winrs.rs b/examples/d3d12-buffer-winrs.rs index b5f5354c..761aa67f 100644 --- a/examples/d3d12-buffer-winrs.rs +++ b/examples/d3d12-buffer-winrs.rs @@ -1,4 +1,11 @@ -//! Example showcasing [`gpu-allocator`] with types and functions from the [`windows`] crate. +//! Example showcasing [`gpu-allocator`] with types and functions from the [`d3d12-agility`] crate. +use d3d12_agility::{ + CreateDXGIFactory2, D3D12CreateDevice, ID3D12Device, ID3D12Resource, IDXGIAdapter4, + IDXGIFactory6, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, D3D12_RESOURCE_DESC, + D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COMMON, + D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_12_0, DXGI_ADAPTER_FLAG3_SOFTWARE, DXGI_FORMAT_UNKNOWN, DXGI_SAMPLE_DESC, +}; use gpu_allocator::{ d3d12::{ AllocationCreateDesc, Allocator, AllocatorCreateDesc, ID3D12DeviceVersion, ResourceCategory, @@ -6,26 +13,10 @@ use gpu_allocator::{ MemoryLocation, }; use log::*; -use windows::{ - core::{Interface, Result}, - Win32::{ - Foundation::E_NOINTERFACE, - Graphics::{ - Direct3D::{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_12_0}, - Direct3D12::{ - D3D12CreateDevice, ID3D12Device, ID3D12Resource, - D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, D3D12_RESOURCE_DESC, - D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_FLAG_NONE, - D3D12_RESOURCE_STATE_COMMON, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - }, - Dxgi::{ - Common::{DXGI_FORMAT_UNKNOWN, DXGI_SAMPLE_DESC}, - CreateDXGIFactory2, IDXGIAdapter4, IDXGIFactory6, DXGI_ADAPTER_FLAG3_SOFTWARE, - DXGI_ERROR_NOT_FOUND, - }, - }, - }, -}; +use windows_core::{Interface, Result, HRESULT}; + +const E_NOINTERFACE: HRESULT = HRESULT(0x8000_4002_u32 as i32); +const DXGI_ERROR_NOT_FOUND: HRESULT = HRESULT(0x887A_0002_u32 as i32); fn create_d3d12_device(dxgi_factory: &IDXGIFactory6) -> Option { for idx in 0.. { @@ -37,9 +28,9 @@ fn create_d3d12_device(dxgi_factory: &IDXGIFactory6) -> Option { }; let adapter4: IDXGIAdapter4 = adapter1.cast().unwrap(); - let desc = unsafe { adapter4.GetDesc3() }.unwrap(); + let mut desc = Default::default(); + unsafe { adapter4.GetDesc3(&mut desc) }.unwrap(); // Skip software adapters - // Vote for https://github.com/microsoft/windows-rs/issues/793! if (desc.Flags & DXGI_ADAPTER_FLAG3_SOFTWARE) == DXGI_ADAPTER_FLAG3_SOFTWARE { continue; } @@ -82,9 +73,7 @@ fn create_d3d12_device(dxgi_factory: &IDXGIFactory6) -> Option { fn main() -> Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init(); - let dxgi_factory = unsafe { - CreateDXGIFactory2(windows::Win32::Graphics::Dxgi::DXGI_CREATE_FACTORY_FLAGS::default()) - }?; + let dxgi_factory = unsafe { CreateDXGIFactory2(0) }?; let device = create_d3d12_device(&dxgi_factory).expect("Failed to create D3D12 device."); diff --git a/src/d3d12/mod.rs b/src/d3d12/mod.rs index 6ead72c5..94f35efa 100644 --- a/src/d3d12/mod.rs +++ b/src/d3d12/mod.rs @@ -1,22 +1,17 @@ #[cfg(feature = "std")] use alloc::sync::Arc; use alloc::{boxed::Box, string::String, vec::Vec}; -use core::{ - fmt, - // TODO: Remove when bumping MSRV to 1.80 - mem::size_of_val, -}; +use core::fmt; #[cfg(feature = "std")] use std::backtrace::Backtrace; +use d3d12_agility::*; use log::{debug, warn, Level}; -use windows::Win32::{ - Foundation::E_OUTOFMEMORY, - Graphics::{ - Direct3D12::*, - Dxgi::{Common::DXGI_FORMAT, DXGI_ERROR_DEVICE_REMOVED}, - }, -}; +use windows_core::HRESULT; + +// `d3d12-agility` does not carry the generic Win32/DXGI error codes. +const E_OUTOFMEMORY: HRESULT = HRESULT(0x8007_000E_u32 as i32); +const DXGI_ERROR_DEVICE_REMOVED: HRESULT = HRESULT(0x887A_0005_u32 as i32); #[cfg(feature = "visualizer")] mod visualizer; @@ -527,6 +522,7 @@ impl Allocator { size_of_val(&options) as u32, ) } + .ok() .map_err(|e| { AllocationError::Internal(format!("ID3D12Device::CheckFeatureSupport failed: {e}")) })?; @@ -760,11 +756,11 @@ impl Allocator { let (num_castable_formats_opt, castable_formats_opt) = if num_castable_formats > 0 { ( - Some(num_castable_formats_array.as_ptr()), - Some(castable_formats_array.as_ptr()), + num_castable_formats_array.as_ptr(), + castable_formats_array.as_ptr(), ) } else { - (None, None) + (core::ptr::null(), core::ptr::null()) }; device.GetResourceAllocationInfo3( @@ -811,7 +807,8 @@ impl Allocator { initial_layout, clear_value, None, // TODO - Some(desc.castable_formats), + desc.castable_formats.len() as u32, + desc.castable_formats.as_ptr(), &mut result, ) } @@ -833,7 +830,8 @@ impl Allocator { initial_layout, clear_value, None, // TODO - None, + 0, + core::ptr::null(), &mut result, ) } @@ -928,7 +926,8 @@ impl Allocator { &resource_desc1, initial_layout, None, - Some(desc.castable_formats), + desc.castable_formats.len() as u32, + desc.castable_formats.as_ptr(), &mut result, ) } @@ -948,7 +947,8 @@ impl Allocator { &resource_desc1, initial_layout, None, - None, + 0, + core::ptr::null(), &mut result, ) } diff --git a/src/d3d12/visualizer.rs b/src/d3d12/visualizer.rs index c1cd37a2..4fc71517 100644 --- a/src/d3d12/visualizer.rs +++ b/src/d3d12/visualizer.rs @@ -1,6 +1,6 @@ #![allow(clippy::new_without_default)] -use windows::Win32::Graphics::Direct3D12::*; +use d3d12_agility::*; use super::Allocator; use crate::visualizer::{ @@ -39,7 +39,7 @@ fn format_heap_type(heap_type: D3D12_HEAP_TYPE) -> &'static str { "D3D12_HEAP_TYPE_CUSTOM", ]; - names[heap_type.0 as usize] + names[heap_type as usize] } fn format_cpu_page_property(prop: D3D12_CPU_PAGE_PROPERTY) -> &'static str { @@ -50,7 +50,7 @@ fn format_cpu_page_property(prop: D3D12_CPU_PAGE_PROPERTY) -> &'static str { "D3D12_CPU_PAGE_PROPERTY_WRITE_BACK", ]; - names[prop.0 as usize] + names[prop as usize] } fn format_memory_pool(pool: D3D12_MEMORY_POOL) -> &'static str { @@ -60,7 +60,7 @@ fn format_memory_pool(pool: D3D12_MEMORY_POOL) -> &'static str { "D3D12_MEMORY_POOL_L1", ]; - names[pool.0 as usize] + names[pool as usize] } impl AllocatorVisualizer { @@ -106,17 +106,17 @@ impl AllocatorVisualizer { ui.label(format!( "Heap Type: {} ({})", format_heap_type(mem_type.heap_properties.Type), - mem_type.heap_properties.Type.0 + mem_type.heap_properties.Type )); ui.label(format!( "CpuPageProperty: {} ({})", format_cpu_page_property(mem_type.heap_properties.CPUPageProperty), - mem_type.heap_properties.CPUPageProperty.0 + mem_type.heap_properties.CPUPageProperty )); ui.label(format!( "MemoryPoolPreference: {} ({})", format_memory_pool(mem_type.heap_properties.MemoryPoolPreference), - mem_type.heap_properties.MemoryPoolPreference.0 + mem_type.heap_properties.MemoryPoolPreference )); ui.label(format!("total block size: {} KiB", total_block_size / 1024)); ui.label(format!("total allocated: {} KiB", total_allocated / 1024)); diff --git a/src/lib.rs b/src/lib.rs index 2eef751b..eac30687 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,10 +94,9 @@ //! //! ```no_run //! # #[cfg(feature = "d3d12")] -//! # fn main() -> windows::core::Result<()> { +//! # fn main() -> windows_core::Result<()> { //! use gpu_allocator::d3d12::*; //! use gpu_allocator::MemoryLocation; -//! # use windows::Win32::Graphics::{Dxgi, Direct3D12}; //! # let device = todo!(); //! //! # let mut allocator = Allocator::new(&AllocatorCreateDesc { @@ -106,20 +105,20 @@ //! # allocation_sizes: Default::default(), //! # }).unwrap(); //! -//! let buffer_desc = Direct3D12::D3D12_RESOURCE_DESC { -//! Dimension: Direct3D12::D3D12_RESOURCE_DIMENSION_BUFFER, +//! let buffer_desc = d3d12_agility::D3D12_RESOURCE_DESC { +//! Dimension: d3d12_agility::D3D12_RESOURCE_DIMENSION_BUFFER, //! Alignment: 0, //! Width: 512, //! Height: 1, //! DepthOrArraySize: 1, //! MipLevels: 1, -//! Format: Dxgi::Common::DXGI_FORMAT_UNKNOWN, -//! SampleDesc: Dxgi::Common::DXGI_SAMPLE_DESC { +//! Format: d3d12_agility::DXGI_FORMAT_UNKNOWN, +//! SampleDesc: d3d12_agility::DXGI_SAMPLE_DESC { //! Count: 1, //! Quality: 0, //! }, -//! Layout: Direct3D12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, -//! Flags: Direct3D12::D3D12_RESOURCE_FLAG_NONE, +//! Layout: d3d12_agility::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, +//! Flags: d3d12_agility::D3D12_RESOURCE_FLAG_NONE, //! }; //! let allocation_desc = AllocationCreateDesc::from_d3d12_resource_desc( //! &allocator.device(), @@ -128,13 +127,13 @@ //! MemoryLocation::GpuOnly, //! ); //! let allocation = allocator.allocate(&allocation_desc).unwrap(); -//! let mut resource: Option = None; +//! let mut resource: Option = None; //! let hr = unsafe { //! device.CreatePlacedResource( //! allocation.heap(), //! allocation.offset(), //! &buffer_desc, -//! Direct3D12::D3D12_RESOURCE_STATE_COMMON, +//! d3d12_agility::D3D12_RESOURCE_STATE_COMMON, //! None, //! &mut resource, //! ) @@ -404,7 +403,7 @@ impl AllocationSizes { let size = size.clamp(4 * MB, 256 * MB); - if size % (4 * MB) == 0 { + if size.is_multiple_of(4 * MB) { return size; }