diff --git a/fyrox-graphics/src/framebuffer.rs b/fyrox-graphics/src/framebuffer.rs index ee8dc3a93..f9fafd3cc 100644 --- a/fyrox-graphics/src/framebuffer.rs +++ b/fyrox-graphics/src/framebuffer.rs @@ -322,22 +322,63 @@ pub trait GpuFrameBufferTrait: GpuFrameBufferAsAny { ) -> Result; } +/// Owned, typed view of pixels read back from a [`GpuFrameBufferTrait`]. Preserves the +/// original byte allocation (and therefore its memory layout) so it is dropped with the +/// same `Layout` it was allocated with, while exposing a `&[T]` view of the contents. +/// +/// This avoids the UB of reinterpreting a `Vec` allocation directly as `Vec` +/// (which would deallocate with `Layout::array::(cap)` instead of the original +/// `Layout::array::(cap)`) without copying the data. +pub struct TypedPixels { + bytes: Vec, + _marker: std::marker::PhantomData, +} + +impl TypedPixels { + /// Returns the pixels as a typed slice. + #[inline] + pub fn as_slice(&self) -> &[T] { + bytemuck::cast_slice(&self.bytes) + } + + /// Number of `T` elements in the readback. + #[inline] + pub fn len(&self) -> usize { + self.bytes.len() / std::mem::size_of::() + } + + /// Whether the readback contains zero elements. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl std::ops::Deref for TypedPixels { + type Target = [T]; + + #[inline] + fn deref(&self) -> &[T] { + self.as_slice() + } +} + impl dyn GpuFrameBufferTrait { /// Reads the pixels and reinterprets them using the given type. - pub fn read_pixels_of_type(&self, read_target: ReadTarget) -> Option> + /// + /// Returns an owning [`TypedPixels`] that derefs to `&[T]`. The underlying byte + /// buffer is preserved as-is, so it is freed with the same memory layout it was + /// allocated with (avoiding the UB of dropping a `Vec` allocation as a `Vec`), + /// and no extra copy is made when constructing the typed view. + pub fn read_pixels_of_type(&self, read_target: ReadTarget) -> Option> where T: Pod, { - let mut bytes = self.read_pixels(read_target)?; - let typed = unsafe { - Vec::::from_raw_parts( - bytes.as_mut_ptr() as *mut T, - bytes.len() / size_of::(), - bytes.capacity() / size_of::(), - ) - }; - std::mem::forget(bytes); - Some(typed) + let bytes = self.read_pixels(read_target)?; + Some(TypedPixels { + bytes, + _marker: std::marker::PhantomData, + }) } }