Use bytemuck instead of slice::from_raw_parts for transmutes (#7376)

This commit is contained in:
Connor Fitzgerald 2025-03-19 22:05:21 -04:00 committed by GitHub
parent c6286791fe
commit 4687973c9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 74 additions and 75 deletions

1
Cargo.lock generated
View File

@ -4884,6 +4884,7 @@ name = "wgpu-types"
version = "24.0.0"
dependencies = [
"bitflags 2.9.0",
"bytemuck",
"js-sys",
"log",
"serde",

View File

@ -91,7 +91,6 @@ bit-set = { version = "0.8", default-features = false }
bit-vec = { version = "0.8", default-features = false }
bitflags = "2.9"
bytemuck = { version = "1.22", features = [
"derive",
"extern_crate_alloc",
"min_const_generics",
] }

View File

@ -21,12 +21,12 @@ struct Globals {
}
#[repr(C, align(256))]
#[derive(Clone, Copy, Zeroable)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct Bunny {
position: [f32; 2],
velocity: [f32; 2],
color: u32,
_pad: u32,
_pad: [u32; (256 - 20) / 4],
}
impl Bunny {
@ -81,7 +81,7 @@ impl Example {
position: [0.0, 0.5 * (self.extent[1] as f32)],
velocity: [speed, 0.0],
color,
_pad: 0,
_pad: Zeroable::zeroed(),
});
}
}
@ -98,12 +98,7 @@ impl Example {
}
let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment;
queue.write_buffer(&self.local_buffer, 0, unsafe {
std::slice::from_raw_parts(
self.bunnies.as_ptr() as *const u8,
self.bunnies.len() * uniform_alignment as usize,
)
});
queue.write_buffer(&self.local_buffer, 0, bytemuck::cast_slice(&self.bunnies));
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
{

View File

@ -76,7 +76,14 @@ observe_locks = ["std", "dep:ron", "serde/serde_derive"]
serde = ["dep:serde", "wgpu-types/serde", "arrayvec/serde", "hashbrown/serde"]
## Enable API tracing.
trace = ["serde", "std", "dep:ron", "naga/serialize", "wgpu-types/trace"]
trace = [
"serde",
"std",
"dep:ron",
"naga/serialize",
"wgpu-types/trace",
"dep:bytemuck",
]
## Enable API replaying
replay = ["serde", "naga/deserialize"]

View File

@ -964,9 +964,7 @@ impl Global {
#[cfg(feature = "trace")]
if let Some(ref mut trace) = *device.trace.lock() {
let data = trace.make_binary("spv", unsafe {
core::slice::from_raw_parts(source.as_ptr().cast::<u8>(), source.len() * 4)
});
let data = trace.make_binary("spv", bytemuck::cast_slice(&source));
trace.add(trace::Action::CreateShaderModule {
id: fid.id(),
desc: desc.clone(),

View File

@ -88,6 +88,7 @@ vulkan = [
"dep:android_system_properties",
"dep:arrayvec",
"dep:ash",
"dep:bytemuck",
"dep:gpu-alloc",
"dep:gpu-descriptor",
"dep:hashbrown",
@ -127,6 +128,7 @@ dx12 = [
"naga/hlsl-out",
"dep:arrayvec",
"dep:bit-set",
"dep:bytemuck",
"dep:hashbrown",
"dep:libloading",
"dep:log",
@ -208,6 +210,7 @@ thiserror.workspace = true
# Target agnostic dependencies used only in backends.
arrayvec = { workspace = true, optional = true }
bytemuck = { workspace = true, optional = true, features = ["derive"] }
hashbrown = { workspace = true, optional = true }
log = { workspace = true, optional = true }
once_cell = { workspace = true, optional = true }
@ -216,7 +219,6 @@ profiling = { workspace = true, optional = true, default-features = false }
rustc-hash = { workspace = true, optional = true }
# Backend: GLES
bytemuck = { workspace = true, optional = true }
glow = { workspace = true, optional = true }
cfg-if = { workspace = true, optional = true }

View File

@ -1,6 +1,5 @@
use std::{
borrow::Cow,
slice,
string::{String, ToString as _},
};
@ -48,18 +47,12 @@ unsafe extern "system" fn output_debug_string_handler(
return Debug::EXCEPTION_CONTINUE_SEARCH;
}
let message = match record.ExceptionCode {
Foundation::DBG_PRINTEXCEPTION_C => String::from_utf8_lossy(unsafe {
slice::from_raw_parts(
record.ExceptionInformation[1] as *const u8,
record.ExceptionInformation[0],
)
}),
Foundation::DBG_PRINTEXCEPTION_WIDE_C => Cow::Owned(String::from_utf16_lossy(unsafe {
slice::from_raw_parts(
record.ExceptionInformation[1] as *const u16,
record.ExceptionInformation[0],
)
})),
Foundation::DBG_PRINTEXCEPTION_C => {
String::from_utf8_lossy(bytemuck::cast_slice(&record.ExceptionInformation))
}
Foundation::DBG_PRINTEXCEPTION_WIDE_C => Cow::Owned(String::from_utf16_lossy(
bytemuck::cast_slice(&record.ExceptionInformation),
)),
_ => return Debug::EXCEPTION_CONTINUE_SEARCH,
};

View File

@ -2,13 +2,14 @@ use std::{
borrow::Cow,
ffi, mem,
num::NonZeroU32,
ptr, slice,
ptr,
string::{String, ToString as _},
sync::Arc,
time::{Duration, Instant},
vec::Vec,
};
use bytemuck::TransparentWrapper;
use parking_lot::Mutex;
use windows::{
core::Interface as _,
@ -2362,13 +2363,9 @@ impl crate::Device for super::Device {
_bitfield2: 0,
AccelerationStructure: instance.blas_address,
};
let temp: *const _ = &temp;
unsafe {
slice::from_raw_parts(
temp.cast::<u8>(),
size_of::<Direct3D12::D3D12_RAYTRACING_INSTANCE_DESC>(),
)
.to_vec()
}
wgt::bytemuck_wrapper!(unsafe struct Desc(Direct3D12::D3D12_RAYTRACING_INSTANCE_DESC));
bytemuck::bytes_of(&Desc::wrap(temp)).to_vec()
}
}

View File

@ -1,5 +1,5 @@
use alloc::string::String;
use core::{mem, ops::Range, slice};
use core::{mem, ops::Range};
use arrayvec::ArrayVec;
@ -84,7 +84,7 @@ impl super::CommandBuffer {
}
fn add_push_constant_data(&mut self, data: &[u32]) -> Range<u32> {
let data_raw = unsafe { slice::from_raw_parts(data.as_ptr().cast(), size_of_val(data)) };
let data_raw = bytemuck::cast_slice(data);
let start = self.data_bytes.len();
assert!(start < u32::MAX as usize);
self.data_bytes.extend_from_slice(data_raw);

View File

@ -1,6 +1,6 @@
use alloc::sync::Arc;
use alloc::vec;
use core::{slice, sync::atomic::Ordering};
use core::sync::atomic::Ordering;
use arrayvec::ArrayVec;
use glow::HasContext;
@ -1039,12 +1039,7 @@ impl super::Queue {
};
temp_query_results.push(result);
}
let query_data = unsafe {
slice::from_raw_parts(
temp_query_results.as_ptr().cast::<u8>(),
temp_query_results.len() * size_of::<u64>(),
)
};
let query_data = bytemuck::cast_slice(&temp_query_results);
match dst.raw {
Some(buffer) => {
unsafe { gl.bind_buffer(dst_target, Some(buffer)) };

View File

@ -3,7 +3,7 @@ use super::conv;
use arrayvec::ArrayVec;
use ash::vk;
use std::{mem, ops::Range, slice};
use std::{mem, ops::Range};
const ALLOCATION_GRANULARITY: u32 = 16;
const DST_IMAGE_LAYOUT: vk::ImageLayout = vk::ImageLayout::TRANSFER_DST_OPTIMAL;
@ -900,7 +900,7 @@ impl crate::CommandEncoder for super::CommandEncoder {
layout.raw,
conv::map_shader_stage(stages),
offset_bytes,
slice::from_raw_parts(data.as_ptr().cast(), data.len() * 4),
bytemuck::cast_slice(data),
)
};
}

View File

@ -4,7 +4,7 @@ use std::{
ffi::{CStr, CString},
mem::{self, MaybeUninit},
num::NonZeroU32,
ptr, slice,
ptr,
sync::Arc,
vec::Vec,
};
@ -2867,10 +2867,7 @@ impl crate::Device for super::Device {
shader_binding_table_record_offset_and_flags: 0,
acceleration_structure_reference: instance.blas_address,
};
let temp: *const _ = &temp;
unsafe {
slice::from_raw_parts::<u8>(temp.cast::<u8>(), size_of::<RawTlasInstance>()).to_vec()
}
bytemuck::bytes_of(&temp).to_vec()
}
}

View File

@ -47,6 +47,7 @@ use std::{
use arrayvec::ArrayVec;
use ash::{ext, khr, vk};
use bytemuck::{Pod, Zeroable};
use hashbrown::HashSet;
use parking_lot::{Mutex, RwLock};
@ -1489,7 +1490,7 @@ fn get_lost_err() -> crate::DeviceError {
crate::DeviceError::Lost
}
#[derive(Clone)]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
struct RawTlasInstance {
transform: [f32; 12],

View File

@ -48,6 +48,7 @@ trace = ["std"]
[dependencies]
bitflags = { workspace = true, features = ["serde"] }
bytemuck = { workspace = true, features = ["derive"] }
log.workspace = true
thiserror = { workspace = true, optional = true }
serde = { workspace = true, default-features = false, features = [

View File

@ -0,0 +1,25 @@
/// Wrapper to unsafely define a wrapper type that can be used with `bytemuck`'s traits.
///
/// This is very useful as it allows us to use bytemuck on foreign types. Despite the
/// unsafe assertion, it means that bytemuck is handling all the actual casting,
/// so we can't screw up size or alignment handling.
///
/// Once wrapped you can use the [`bytemuck::TransparentWrapper`] methods and
/// all the free methods that come with [`bytemuck::Pod`] and [`bytemuck::Zeroable`].
///
/// # Safety
///
/// Once wrapped, the resulting type must follow all the invariants
/// of the [`bytemuck::Pod`] and [`bytemuck::Zeroable`] traits.
#[macro_export]
macro_rules! bytemuck_wrapper {
(unsafe struct $name:ident($inner:ty)) => {
#[derive(Copy, Clone)]
#[repr(transparent)]
struct $name($inner);
unsafe impl bytemuck::Zeroable for $name {}
unsafe impl bytemuck::Pod for $name {}
unsafe impl bytemuck::TransparentWrapper<$inner> for $name {}
};
}

View File

@ -23,6 +23,8 @@ use core::{
ops::Range,
};
use bytemuck::{Pod, Zeroable};
#[cfg(any(feature = "serde", test))]
use {
alloc::format,
@ -30,6 +32,7 @@ use {
};
pub mod assertions;
mod cast_utils;
mod counters;
mod env;
mod features;
@ -7214,7 +7217,7 @@ bitflags::bitflags! {
/// Argument buffer layout for `draw_indirect` commands.
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)]
pub struct DrawIndirectArgs {
/// The number of vertices to draw.
pub vertex_count: u32,
@ -7232,18 +7235,13 @@ impl DrawIndirectArgs {
/// Returns the bytes representation of the struct, ready to be written in a buffer.
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe {
core::mem::transmute(core::slice::from_raw_parts(
core::ptr::from_ref(self).cast::<u8>(),
size_of::<Self>(),
))
}
bytemuck::bytes_of(self)
}
}
/// Argument buffer layout for `draw_indexed_indirect` commands.
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)]
pub struct DrawIndexedIndirectArgs {
/// The number of indices to draw.
pub index_count: u32,
@ -7263,18 +7261,13 @@ impl DrawIndexedIndirectArgs {
/// Returns the bytes representation of the struct, ready to be written in a buffer.
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe {
core::mem::transmute(core::slice::from_raw_parts(
core::ptr::from_ref(self).cast::<u8>(),
size_of::<Self>(),
))
}
bytemuck::bytes_of(self)
}
}
/// Argument buffer layout for `dispatch_indirect` commands.
#[repr(C)]
#[derive(Copy, Clone, Debug, Default)]
#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)]
pub struct DispatchIndirectArgs {
/// The number of work groups in X dimension.
pub x: u32,
@ -7288,12 +7281,7 @@ impl DispatchIndirectArgs {
/// Returns the bytes representation of the struct, ready to be written into a buffer.
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe {
core::mem::transmute(core::slice::from_raw_parts(
core::ptr::from_ref(self).cast::<u8>(),
size_of::<Self>(),
))
}
bytemuck::bytes_of(self)
}
}