From b362ccf9c97c44bca9e61e8fa03a6190e1bd457f Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 1 May 2024 09:42:02 +0200 Subject: [PATCH 1/3] metal: Migrate to `objc2` architecture with `objc2-metal` bindings The current `objc` crate stack is completely unmaintained and has severely fallen out of date with no updates for over 4 years. The `metal-rs` crate, built on top of this architecture, is completely written by hand which is tedious to keep up-to-date, not to mention has inconsistencies in its implementation. All of this is superseded by the new `objc2` crate stack built by @Madsmtm. Beyond providing what seems like a better, safer abstraction over Objective-C, _all_ bindings are completely autogenerated meaning we'll no longer lag behind upstream bindings (requiring painstaking manual patching) or have inconsistencies in the implementations, as long as the generator is properly able to represent the bindings. --- .github/workflows/ci.yml | 9 --- Cargo.toml | 21 ++++- README.md | 6 +- examples/metal-buffer.rs | 34 ++++---- src/lib.rs | 17 ++-- src/metal/mod.rs | 163 +++++++++++++++++++++++---------------- src/vulkan/mod.rs | 2 - 7 files changed, 149 insertions(+), 103 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c02b508..16b12ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,15 +69,6 @@ jobs: uses: dtolnay/rust-toolchain@nightly - name: Generate lockfile with minimal dependency versions run: cargo +nightly generate-lockfile -Zminimal-versions - - name: Bump `libc 0.1` version to `0.2` via `malloc_buf 0.0.6` - if: ${{ runner.os == 'macOS' }} - run: | - # The 7-year-unmaintained malloc_buf (depended on via metal-rs->objc) - # only allows using libc 0.2 since the 0.0.6 release, which is necessary - # since the libc 0.1 range no longer compiles. Fortunately objc which - # is also unmaintained for 4 years depends on malloc_buf >=0.0,<0.1.0, - # allowing the 0.0.6 release to be used (but not the 1.0.0 release). - cargo update -p malloc_buf --precise 0.0.6 - name: Cargo clippy with minimal-versions run: cargo +stable clippy --workspace --features ${{ matrix.features }} --no-default-features -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index 76d526d..a096a15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,17 @@ egui = { version = ">=0.24, <=0.27", optional = true, default-features = false } egui_extras = { version = ">=0.24, <=0.27", optional = true, default-features = false } [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -metal = { version = "0.29.0", default-features = false, features = ["link", "dispatch"], optional = true } +objc2 = { version = "0.5.2", default-features = false, optional = true } +objc2-foundation = { version = "0.2", default-features = false, optional = true } +objc2-metal = { version = "0.2.1", default-features = false, features = [ + "MTLAccelerationStructure", + "MTLBuffer", + "MTLDevice", + "MTLHeap", + "MTLResource", + "MTLTexture", + "std", +], optional = true } [target.'cfg(windows)'.dependencies] # Only needed for public-winapi interop helpers @@ -65,6 +75,11 @@ features = [ "Win32_Graphics_Dxgi_Common", ] +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dev-dependencies] +objc2-metal = { version = "0.2.1", default-features = false, features = [ + "MTLPixelFormat", +] } + [[example]] name = "vulkan-buffer" required-features = ["vulkan", "ash/loaded"] @@ -85,8 +100,8 @@ required-features = ["metal"] visualizer = ["dep:egui", "dep:egui_extras"] vulkan = ["dep:ash"] d3d12 = ["dep:windows"] -metal = ["dep:metal"] +metal = ["dep:objc2", "dep:objc2-metal", "dep:objc2-foundation"] # Expose helper functionality for winapi types to interface with gpu-allocator, which is primarily windows-rs driven public-winapi = ["dep:winapi"] -default = ["d3d12", "vulkan"] +default = ["d3d12", "vulkan", "metal"] diff --git a/README.md b/README.md index 7349a25..7aa7dad 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ allocator.free(allocation).unwrap(); ```rust use gpu_allocator::metal::*; - +use objc2_metal as metal; let mut allocator = Allocator::new(&AllocatorCreateDesc { device: device.clone(), debug_settings: Default::default(), @@ -146,12 +146,12 @@ let mut allocator = Allocator::new(&AllocatorCreateDesc { ```rust use gpu_allocator::metal::*; use gpu_allocator::MemoryLocation; - +use objc2_metal as metal; let allocation_desc = AllocationCreateDesc::buffer( &device, "Example allocation", 512, // size in bytes - gpu_allocator::MemoryLocation::GpuOnly, + MemoryLocation::GpuOnly, ); let allocation = allocator.allocate(&allocation_desc).unwrap(); let resource = allocation.make_buffer().unwrap(); diff --git a/examples/metal-buffer.rs b/examples/metal-buffer.rs index 5674bef..0a88b51 100644 --- a/examples/metal-buffer.rs +++ b/examples/metal-buffer.rs @@ -1,12 +1,20 @@ -use std::sync::Arc; - use gpu_allocator::metal::{AllocationCreateDesc, Allocator, AllocatorCreateDesc}; use log::info; +use metal::MTLDevice as _; +use objc2::rc::Id; +use objc2_foundation::NSArray; +use objc2_metal as metal; fn main() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init(); - let device = Arc::new(metal::Device::system_default().unwrap()); + // Allow the innards of objc2-metal to link the static function below: + // https://docs.rs/objc2-metal/0.2.2/objc2_metal/index.html + #[link(name = "CoreGraphics", kind = "framework")] + extern "C" {} + + let device = + unsafe { Id::from_raw(metal::MTLCreateSystemDefaultDevice()) }.expect("No MTLDevice found"); // Setting up the allocator let mut allocator = Allocator::new(&AllocatorCreateDesc { @@ -60,11 +68,11 @@ fn main() { // Test allocating texture { - let texture_desc = metal::TextureDescriptor::new(); - texture_desc.set_pixel_format(metal::MTLPixelFormat::RGBA8Unorm); - texture_desc.set_width(64); - texture_desc.set_height(64); - texture_desc.set_storage_mode(metal::MTLStorageMode::Private); + let texture_desc = unsafe { metal::MTLTextureDescriptor::new() }; + texture_desc.setPixelFormat(metal::MTLPixelFormat::RGBA8Unorm); + unsafe { texture_desc.setWidth(64) }; + unsafe { texture_desc.setHeight(64) }; + texture_desc.setStorageMode(metal::MTLStorageMode::Private); let allocation_desc = AllocationCreateDesc::texture(&device, "Test allocation (Texture)", &texture_desc); let allocation = allocator.allocate(&allocation_desc).unwrap(); @@ -75,14 +83,14 @@ fn main() { // Test allocating acceleration structure { - let empty_array = metal::Array::from_slice(&[]); - let acc_desc = metal::PrimitiveAccelerationStructureDescriptor::descriptor(); - acc_desc.set_geometry_descriptors(empty_array); - let sizes = device.acceleration_structure_sizes_with_descriptor(&acc_desc); + let empty_array = NSArray::from_slice(&[]); + let acc_desc = metal::MTLPrimitiveAccelerationStructureDescriptor::descriptor(); + acc_desc.setGeometryDescriptors(Some(&empty_array)); + let sizes = device.accelerationStructureSizesWithDescriptor(&acc_desc); let allocation_desc = AllocationCreateDesc::acceleration_structure_with_size( &device, "Test allocation (Acceleration structure)", - sizes.acceleration_structure_size, + sizes.accelerationStructureSize as u64, gpu_allocator::MemoryLocation::GpuOnly, ); let allocation = allocator.allocate(&allocation_desc).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 7d2112d..1e5b2d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,10 +161,11 @@ //! ```no_run //! # #[cfg(feature = "metal")] //! # fn main() { -//! # use std::sync::Arc; //! use gpu_allocator::metal::*; -//! -//! # let device = Arc::new(metal::Device::system_default().unwrap()); +//! # use objc2::rc::Id; +//! use objc2_metal as metal; +//! # let device = unsafe { metal::MTLCreateSystemDefaultDevice() }; +//! # let device = unsafe { Id::from_raw(device) }.expect("No MTLDevice found"); //! let mut allocator = Allocator::new(&AllocatorCreateDesc { //! device: device.clone(), //! debug_settings: Default::default(), @@ -179,22 +180,23 @@ //! ```no_run //! # #[cfg(feature = "metal")] //! # fn main() { -//! # use std::sync::Arc; //! use gpu_allocator::metal::*; //! use gpu_allocator::MemoryLocation; -//! # let device = Arc::new(metal::Device::system_default().unwrap()); +//! # use objc2::rc::Id; +//! use objc2_metal as metal; +//! # let device = unsafe { metal::MTLCreateSystemDefaultDevice() }; +//! # let device = unsafe { Id::from_raw(device) }.expect("No MTLDevice found"); //! # let mut allocator = Allocator::new(&AllocatorCreateDesc { //! # device: device.clone(), //! # debug_settings: Default::default(), //! # allocation_sizes: Default::default(), //! # }) //! # .unwrap(); -//! //! let allocation_desc = AllocationCreateDesc::buffer( //! &device, //! "Example allocation", //! 512, // size in bytes -//! gpu_allocator::MemoryLocation::GpuOnly, +//! MemoryLocation::GpuOnly, //! ); //! let allocation = allocator.allocate(&allocation_desc).unwrap(); //! let resource = allocation.make_buffer().unwrap(); @@ -206,6 +208,7 @@ //! # #[cfg(not(feature = "metal"))] //! # fn main() {} //! ``` +#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)] mod result; pub use result::*; diff --git a/src/metal/mod.rs b/src/metal/mod.rs index b14096c..f1dab75 100644 --- a/src/metal/mod.rs +++ b/src/metal/mod.rs @@ -1,7 +1,10 @@ -#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)] use std::{backtrace::Backtrace, sync::Arc}; use log::debug; +use metal::{MTLDevice as _, MTLHeap as _, MTLResource as _}; +use objc2::{rc::Retained, runtime::ProtocolObject}; +use objc2_foundation::NSString; +use objc2_metal as metal; use crate::{ allocator::{self, AllocatorReport, MemoryBlockReport}, @@ -10,9 +13,9 @@ use crate::{ fn memory_location_to_metal(location: MemoryLocation) -> metal::MTLResourceOptions { match location { - MemoryLocation::GpuOnly => metal::MTLResourceOptions::StorageModePrivate, + MemoryLocation::GpuOnly => metal::MTLResourceOptions::MTLResourceStorageModePrivate, MemoryLocation::CpuToGpu | MemoryLocation::GpuToCpu | MemoryLocation::Unknown => { - metal::MTLResourceOptions::StorageModeShared + metal::MTLResourceOptions::MTLResourceStorageModeShared } } } @@ -24,44 +27,57 @@ pub struct Allocation { size: u64, memory_block_index: usize, memory_type_index: usize, - heap: Arc, + heap: Retained>, name: Option>, } impl Allocation { - pub fn heap(&self) -> &metal::Heap { - self.heap.as_ref() + pub fn heap(&self) -> &ProtocolObject { + &self.heap } - pub fn make_buffer(&self) -> Option { - let resource = - self.heap - .new_buffer_with_offset(self.size, self.heap.resource_options(), self.offset); + pub fn make_buffer(&self) -> Option>> { + let resource = unsafe { + self.heap.newBufferWithLength_options_offset( + self.size as usize, + self.heap.resourceOptions(), + self.offset as usize, + ) + }; if let Some(resource) = &resource { if let Some(name) = &self.name { - resource.set_label(name); + resource.setLabel(Some(&NSString::from_str(name))); } } resource } - pub fn make_texture(&self, desc: &metal::TextureDescriptor) -> Option { - let resource = self.heap.new_texture_with_offset(desc, self.offset); + pub fn make_texture( + &self, + desc: &metal::MTLTextureDescriptor, + ) -> Option>> { + let resource = unsafe { + self.heap + .newTextureWithDescriptor_offset(desc, self.offset as usize) + }; if let Some(resource) = &resource { if let Some(name) = &self.name { - resource.set_label(name); + resource.setLabel(Some(&NSString::from_str(name))); } } resource } - pub fn make_acceleration_structure(&self) -> Option { - let resource = self - .heap - .new_acceleration_structure_with_size_offset(self.size, self.offset); + pub fn make_acceleration_structure( + &self, + ) -> Option>> { + let resource = unsafe { + self.heap + .newAccelerationStructureWithSize_offset(self.size as usize, self.offset as usize) + }; if let Some(resource) = &resource { if let Some(name) = &self.name { - resource.set_label(name); + resource.setLabel(Some(&NSString::from_str(name))); } } resource @@ -84,54 +100,64 @@ pub struct AllocationCreateDesc<'a> { impl<'a> AllocationCreateDesc<'a> { pub fn buffer( - device: &metal::Device, + device: &ProtocolObject, name: &'a str, length: u64, location: MemoryLocation, ) -> Self { - let size_and_align = - device.heap_buffer_size_and_align(length, memory_location_to_metal(location)); + let size_and_align = device.heapBufferSizeAndAlignWithLength_options( + length as usize, + memory_location_to_metal(location), + ); Self { name, location, - size: size_and_align.size, - alignment: size_and_align.align, + size: size_and_align.size as u64, + alignment: size_and_align.align as u64, } } - pub fn texture(device: &metal::Device, name: &'a str, desc: &metal::TextureDescriptor) -> Self { - let size_and_align = device.heap_texture_size_and_align(desc); + pub fn texture( + device: &ProtocolObject, + name: &'a str, + desc: &metal::MTLTextureDescriptor, + ) -> Self { + let size_and_align = device.heapTextureSizeAndAlignWithDescriptor(desc); Self { name, - location: match desc.storage_mode() { + location: match desc.storageMode() { metal::MTLStorageMode::Shared | metal::MTLStorageMode::Managed | metal::MTLStorageMode::Memoryless => MemoryLocation::Unknown, metal::MTLStorageMode::Private => MemoryLocation::GpuOnly, + metal::MTLStorageMode(mode /* @ 4.. */) => todo!("Unknown storage mode {mode}"), }, - size: size_and_align.size, - alignment: size_and_align.align, + size: size_and_align.size as u64, + alignment: size_and_align.align as u64, } } pub fn acceleration_structure_with_size( - device: &metal::Device, + device: &ProtocolObject, name: &'a str, - size: u64, + size: u64, // TODO: usize location: MemoryLocation, ) -> Self { - let size_and_align = device.heap_acceleration_structure_size_and_align_with_size(size); + // TODO: See if we can mark this function as safe, after checking what happens if size is too large? + // What other preconditions need to be upheld? + let size_and_align = + unsafe { device.heapAccelerationStructureSizeAndAlignWithSize(size as usize) }; Self { name, location, - size: size_and_align.size, - alignment: size_and_align.align, + size: size_and_align.size as u64, + alignment: size_and_align.align as u64, } } } pub struct Allocator { - device: Arc, + device: Retained>, debug_settings: AllocatorDebugSettings, memory_types: Vec, allocation_sizes: AllocationSizes, @@ -139,7 +165,7 @@ pub struct Allocator { #[derive(Debug)] pub struct AllocatorCreateDesc { - pub device: Arc, + pub device: Retained>, pub debug_settings: AllocatorDebugSettings, pub allocation_sizes: AllocationSizes, } @@ -152,23 +178,28 @@ pub struct CommittedAllocationStatistics { #[derive(Debug)] struct MemoryBlock { - heap: Arc, + heap: Retained>, size: u64, sub_allocator: Box, } impl MemoryBlock { fn new( - device: &Arc, + device: &ProtocolObject, size: u64, - heap_descriptor: &metal::HeapDescriptor, + heap_descriptor: &metal::MTLHeapDescriptor, dedicated: bool, memory_location: MemoryLocation, ) -> Result { - heap_descriptor.set_size(size); + heap_descriptor.setSize(size as usize); - let heap = Arc::new(device.new_heap(heap_descriptor)); - heap.set_label(&format!("MemoryBlock {memory_location:?}")); + let heap = device + .newHeapWithDescriptor(heap_descriptor) + .ok_or_else(|| AllocationError::Internal("No MTLHeap was returned".to_string()))?; + + heap.setLabel(Some(&NSString::from_str(&format!( + "MemoryBlock {memory_location:?}" + )))); let sub_allocator: Box = if dedicated { Box::new(allocator::DedicatedBlockAllocator::new(size)) @@ -189,7 +220,7 @@ struct MemoryType { memory_blocks: Vec>, _committed_allocations: CommittedAllocationStatistics, memory_location: MemoryLocation, - heap_properties: metal::HeapDescriptor, + heap_properties: Retained, memory_type_index: usize, active_general_blocks: usize, } @@ -197,14 +228,14 @@ struct MemoryType { impl MemoryType { fn allocate( &mut self, - device: &Arc, + device: &ProtocolObject, desc: &AllocationCreateDesc<'_>, backtrace: Arc, allocation_sizes: &AllocationSizes, ) -> Result { let allocation_type = allocator::AllocationType::Linear; - let memblock_size = if self.heap_properties.storage_mode() == metal::MTLStorageMode::Private + let memblock_size = if self.heap_properties.storageMode() == metal::MTLStorageMode::Private { allocation_sizes.device_memblock_size } else { @@ -380,24 +411,24 @@ impl Allocator { pub fn new(desc: &AllocatorCreateDesc) -> Result { let heap_types = [ (MemoryLocation::GpuOnly, { - let heap_desc = metal::HeapDescriptor::new(); - heap_desc.set_cpu_cache_mode(metal::MTLCPUCacheMode::DefaultCache); - heap_desc.set_storage_mode(metal::MTLStorageMode::Private); - heap_desc.set_heap_type(metal::MTLHeapType::Placement); + let heap_desc = unsafe { metal::MTLHeapDescriptor::new() }; + heap_desc.setCpuCacheMode(metal::MTLCPUCacheMode::DefaultCache); + heap_desc.setStorageMode(metal::MTLStorageMode::Private); + heap_desc.setType(metal::MTLHeapType::Placement); heap_desc }), (MemoryLocation::CpuToGpu, { - let heap_desc = metal::HeapDescriptor::new(); - heap_desc.set_cpu_cache_mode(metal::MTLCPUCacheMode::WriteCombined); - heap_desc.set_storage_mode(metal::MTLStorageMode::Shared); - heap_desc.set_heap_type(metal::MTLHeapType::Placement); + let heap_desc = unsafe { metal::MTLHeapDescriptor::new() }; + heap_desc.setCpuCacheMode(metal::MTLCPUCacheMode::WriteCombined); + heap_desc.setStorageMode(metal::MTLStorageMode::Shared); + heap_desc.setType(metal::MTLHeapType::Placement); heap_desc }), (MemoryLocation::GpuToCpu, { - let heap_desc = metal::HeapDescriptor::new(); - heap_desc.set_cpu_cache_mode(metal::MTLCPUCacheMode::DefaultCache); - heap_desc.set_storage_mode(metal::MTLStorageMode::Shared); - heap_desc.set_heap_type(metal::MTLHeapType::Placement); + let heap_desc = unsafe { metal::MTLHeapDescriptor::new() }; + heap_desc.setCpuCacheMode(metal::MTLCPUCacheMode::DefaultCache); + heap_desc.setStorageMode(metal::MTLStorageMode::Shared); + heap_desc.setType(metal::MTLHeapType::Placement); heap_desc }), ]; @@ -482,15 +513,15 @@ impl Allocator { Ok(()) } - pub fn get_heaps(&self) -> Vec<&metal::HeapRef> { - // Get all memory blocks - let mut heaps: Vec<&metal::HeapRef> = Vec::new(); - for memory_type in &self.memory_types { - for block in memory_type.memory_blocks.iter().flatten() { - heaps.push(block.heap.as_ref()); - } - } - heaps + /// Returns heaps for all memory blocks + pub fn heaps(&self) -> impl Iterator> { + self.memory_types.iter().flat_map(|memory_type| { + memory_type + .memory_blocks + .iter() + .flatten() + .map(|block| block.heap.as_ref()) + }) } pub fn generate_report(&self) -> AllocatorReport { diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 560d7cb..bb189d4 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,5 +1,3 @@ -#![deny(clippy::unimplemented, clippy::unwrap_used, clippy::ok_expect)] - #[cfg(feature = "visualizer")] mod visualizer; use std::{backtrace::Backtrace, fmt, marker::PhantomData, sync::Arc}; From a0badca07d873c0fa8ea4ed90319c9f4152d40ce Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 21 May 2024 23:54:11 +0200 Subject: [PATCH 2/3] Use `target_vendor = "apple"` instead of many custom `target_os`es --- Cargo.toml | 4 ++-- src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a096a15..3b06dac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ ash = { version = "0.38", optional = true, default-features = false, features = egui = { version = ">=0.24, <=0.27", optional = true, default-features = false } egui_extras = { version = ">=0.24, <=0.27", optional = true, default-features = false } -[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +[target.'cfg(target_vendor = "apple")'.dependencies] objc2 = { version = "0.5.2", default-features = false, optional = true } objc2-foundation = { version = "0.2", default-features = false, optional = true } objc2-metal = { version = "0.2.1", default-features = false, features = [ @@ -75,7 +75,7 @@ features = [ "Win32_Graphics_Dxgi_Common", ] -[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dev-dependencies] +[target.'cfg(target_vendor = "apple")'.dev-dependencies] objc2-metal = { version = "0.2.1", default-features = false, features = [ "MTLPixelFormat", ] } diff --git a/src/lib.rs b/src/lib.rs index 1e5b2d9..878082c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -226,7 +226,7 @@ pub mod vulkan; #[cfg(all(windows, feature = "d3d12"))] pub mod d3d12; -#[cfg(all(any(target_os = "macos", target_os = "ios"), feature = "metal"))] +#[cfg(all(target_vendor = "apple", feature = "metal"))] pub mod metal; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] From be8b03e972792cdfd497d729de97beace9887912 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 3 Oct 2024 10:24:07 +0200 Subject: [PATCH 3/3] Work around unused_qualifications lint for Rust 1.80 prelude extension `size_of(_val)()` was added to the prelude in Rust 1.80, causing `unused_qualifications` warnings whenever we qualify a call to it with via `std::mem::size_of_val()`. The easiest workaround is to remove the prefix and explicitly import the function in scope. We annotate the import with a `TODO` to remove it once bumping our MSRV on or past 1.80. --- src/d3d12/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/d3d12/mod.rs b/src/d3d12/mod.rs index 6236fd9..d2baef0 100644 --- a/src/d3d12/mod.rs +++ b/src/d3d12/mod.rs @@ -1,4 +1,10 @@ -use std::{backtrace::Backtrace, fmt, sync::Arc}; +use std::{ + backtrace::Backtrace, + fmt, + // TODO: Remove when bumping MSRV to 1.80 + mem::size_of_val, + sync::Arc, +}; use log::{debug, warn, Level}; use windows::Win32::{ @@ -628,7 +634,7 @@ impl Allocator { device.CheckFeatureSupport( D3D12_FEATURE_D3D12_OPTIONS, <*mut D3D12_FEATURE_DATA_D3D12_OPTIONS>::cast(&mut options), - std::mem::size_of_val(&options) as u32, + size_of_val(&options) as u32, ) } .map_err(|e| {