Make PersistentGpuBufferable a safe trait (#12744)
# Objective Fixes #12727. All parts that `PersistentGpuBuffer` interact with should be 100% safe both on the CPU and the GPU: `Queue::write_buffer_with` zeroes out the slice being written to and when uploading to the GPU, and all slice writes are bounds checked on the CPU side. ## Solution Make `PersistentGpuBufferable` a safe trait. Enforce it's correct implementation via assertions. Re-enable `forbid(unsafe_code)` on `bevy_pbr`.
This commit is contained in:
parent
afff818e5c
commit
e62a01f403
@ -1,6 +1,7 @@
|
|||||||
// FIXME(3492): remove once docs are ready
|
// FIXME(3492): remove once docs are ready
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
#![doc(
|
#![doc(
|
||||||
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
||||||
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
#![allow(unsafe_code)]
|
|
||||||
|
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
render_resource::{
|
render_resource::{
|
||||||
BindingResource, Buffer, BufferAddress, BufferDescriptor, BufferUsages,
|
BindingResource, Buffer, BufferAddress, BufferDescriptor, BufferUsages,
|
||||||
CommandEncoderDescriptor,
|
CommandEncoderDescriptor, COPY_BUFFER_ALIGNMENT,
|
||||||
},
|
},
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
};
|
};
|
||||||
@ -41,6 +39,7 @@ impl<T: PersistentGpuBufferable> PersistentGpuBuffer<T> {
|
|||||||
/// Queue an item of type T to be added to the buffer, returning the byte range within the buffer that it will be located at.
|
/// Queue an item of type T to be added to the buffer, returning the byte range within the buffer that it will be located at.
|
||||||
pub fn queue_write(&mut self, data: T, metadata: T::Metadata) -> Range<BufferAddress> {
|
pub fn queue_write(&mut self, data: T, metadata: T::Metadata) -> Range<BufferAddress> {
|
||||||
let data_size = data.size_in_bytes() as u64;
|
let data_size = data.size_in_bytes() as u64;
|
||||||
|
debug_assert!(data_size % COPY_BUFFER_ALIGNMENT == 0);
|
||||||
if let Ok(buffer_slice) = self.allocation_planner.allocate_range(data_size) {
|
if let Ok(buffer_slice) = self.allocation_planner.allocate_range(data_size) {
|
||||||
self.write_queue
|
self.write_queue
|
||||||
.push((data, metadata, buffer_slice.clone()));
|
.push((data, metadata, buffer_slice.clone()));
|
||||||
@ -110,17 +109,18 @@ impl<T: PersistentGpuBufferable> PersistentGpuBuffer<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A trait representing data that can be written to a [`PersistentGpuBuffer`].
|
/// A trait representing data that can be written to a [`PersistentGpuBuffer`].
|
||||||
///
|
pub trait PersistentGpuBufferable {
|
||||||
/// # Safety
|
|
||||||
/// * All data must be a multiple of `wgpu::COPY_BUFFER_ALIGNMENT` bytes.
|
|
||||||
/// * The amount of bytes written to `buffer` in `write_bytes_le()` must match `size_in_bytes()`.
|
|
||||||
pub unsafe trait PersistentGpuBufferable {
|
|
||||||
/// Additional metadata associated with each item, made available during `write_bytes_le`.
|
/// Additional metadata associated with each item, made available during `write_bytes_le`.
|
||||||
type Metadata;
|
type Metadata;
|
||||||
|
|
||||||
/// The size in bytes of `self`.
|
/// The size in bytes of `self`. This will determine the size of the buffer passed into
|
||||||
|
/// `write_bytes_le`.
|
||||||
|
///
|
||||||
|
/// All data written must be in a multiple of `wgpu::COPY_BUFFER_ALIGNMENT` bytes. Failure to do so will
|
||||||
|
/// result in a panic when using [`PersistentGpuBuffer`].
|
||||||
fn size_in_bytes(&self) -> usize;
|
fn size_in_bytes(&self) -> usize;
|
||||||
|
|
||||||
/// Convert `self` + `metadata` into bytes (little-endian), and write to the provided buffer slice.
|
/// Convert `self` + `metadata` into bytes (little-endian), and write to the provided buffer slice.
|
||||||
|
/// Any bytes not written to in the slice will be zeroed out when uploaded to the GPU.
|
||||||
fn write_bytes_le(&self, metadata: Self::Metadata, buffer_slice: &mut [u8]);
|
fn write_bytes_le(&self, metadata: Self::Metadata, buffer_slice: &mut [u8]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
#![allow(unsafe_code)]
|
|
||||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
|
||||||
|
|
||||||
use super::{persistent_buffer::PersistentGpuBufferable, Meshlet, MeshletBoundingSphere};
|
use super::{persistent_buffer::PersistentGpuBufferable, Meshlet, MeshletBoundingSphere};
|
||||||
use std::{mem::size_of, sync::Arc};
|
use std::{mem::size_of, sync::Arc};
|
||||||
|
|
||||||
const MESHLET_VERTEX_SIZE_IN_BYTES: u32 = 48;
|
const MESHLET_VERTEX_SIZE_IN_BYTES: u32 = 48;
|
||||||
|
|
||||||
unsafe impl PersistentGpuBufferable for Arc<[u8]> {
|
impl PersistentGpuBufferable for Arc<[u8]> {
|
||||||
type Metadata = ();
|
type Metadata = ();
|
||||||
|
|
||||||
fn size_in_bytes(&self) -> usize {
|
fn size_in_bytes(&self) -> usize {
|
||||||
@ -18,7 +15,7 @@ unsafe impl PersistentGpuBufferable for Arc<[u8]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl PersistentGpuBufferable for Arc<[u32]> {
|
impl PersistentGpuBufferable for Arc<[u32]> {
|
||||||
type Metadata = u64;
|
type Metadata = u64;
|
||||||
|
|
||||||
fn size_in_bytes(&self) -> usize {
|
fn size_in_bytes(&self) -> usize {
|
||||||
@ -37,7 +34,7 @@ unsafe impl PersistentGpuBufferable for Arc<[u32]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl PersistentGpuBufferable for Arc<[Meshlet]> {
|
impl PersistentGpuBufferable for Arc<[Meshlet]> {
|
||||||
type Metadata = (u64, u64);
|
type Metadata = (u64, u64);
|
||||||
|
|
||||||
fn size_in_bytes(&self) -> usize {
|
fn size_in_bytes(&self) -> usize {
|
||||||
@ -65,7 +62,7 @@ unsafe impl PersistentGpuBufferable for Arc<[Meshlet]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl PersistentGpuBufferable for Arc<[MeshletBoundingSphere]> {
|
impl PersistentGpuBufferable for Arc<[MeshletBoundingSphere]> {
|
||||||
type Metadata = ();
|
type Metadata = ();
|
||||||
|
|
||||||
fn size_in_bytes(&self) -> usize {
|
fn size_in_bytes(&self) -> usize {
|
||||||
|
|||||||
@ -51,7 +51,7 @@ pub use wgpu::{
|
|||||||
TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
|
TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
|
||||||
TextureViewDescriptor, TextureViewDimension, VertexAttribute,
|
TextureViewDescriptor, TextureViewDimension, VertexAttribute,
|
||||||
VertexBufferLayout as RawVertexBufferLayout, VertexFormat, VertexState as RawVertexState,
|
VertexBufferLayout as RawVertexBufferLayout, VertexFormat, VertexState as RawVertexState,
|
||||||
VertexStepMode,
|
VertexStepMode, COPY_BUFFER_ALIGNMENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod encase {
|
pub mod encase {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user