Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions benches/benches/bevy_render/compute_normals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use criterion::{criterion_group, Criterion};
use rand::random;
use std::time::{Duration, Instant};

use bevy_asset::RenderAssetUsages;
use bevy_mesh::{Indices, Mesh, PrimitiveTopology};
use bevy_asset::{ExtractableAsset, RenderAssetUsages};
use bevy_mesh::{Indices, Mesh, MeshExtractableData, PrimitiveTopology};

const GRID_SIZE: usize = 256;

Expand All @@ -28,19 +28,20 @@ fn compute_normals(c: &mut Criterion) {
.flat_map(|i| std::iter::repeat(i).zip(0..GRID_SIZE))
.map(|(i, j)| [i as f32, j as f32, random::<f32>()])
.collect::<Vec<_>>();
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::MAIN_WORLD,
Mesh::from(
MeshExtractableData::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_indices(indices.clone()),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_indices(indices.clone())
.with_asset_usage(RenderAssetUsages::MAIN_WORLD)
};

c.bench_function("smooth_normals", |b| {
b.iter_custom(|iters| {
let mut total = Duration::default();
for _ in 0..iters {
let mut mesh = new_mesh();
let mesh = mesh.extractable_data_mut().unwrap();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
let start = Instant::now();
mesh.compute_smooth_normals();
Expand All @@ -57,6 +58,7 @@ fn compute_normals(c: &mut Criterion) {
let mut total = Duration::default();
for _ in 0..iters {
let mut mesh = new_mesh();
let mesh = mesh.extractable_data_mut().unwrap();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
let start = Instant::now();
mesh.compute_smooth_normals();
Expand All @@ -73,6 +75,7 @@ fn compute_normals(c: &mut Criterion) {
let mut total = Duration::default();
for _ in 0..iters {
let mut mesh = new_mesh();
let mesh = mesh.extractable_data_mut().unwrap();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
let start = Instant::now();
mesh.compute_area_weighted_normals();
Expand All @@ -84,13 +87,14 @@ fn compute_normals(c: &mut Criterion) {
});
});

let new_mesh = || new_mesh().with_duplicated_vertices();
let new_mesh = || new_mesh().with_extractable_data(|d| d.unwrap().with_duplicated_vertices());

c.bench_function("flat_normals", |b| {
b.iter_custom(|iters| {
let mut total = Duration::default();
for _ in 0..iters {
let mut mesh = new_mesh();
let mesh = mesh.extractable_data_mut().unwrap();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
let start = Instant::now();
mesh.compute_flat_normals();
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ pub mod prelude {
#[doc(hidden)]
pub use crate::{
Asset, AssetApp, AssetEvent, AssetId, AssetMode, AssetPlugin, AssetServer, Assets,
DirectAssetAccessExt, Handle, UntypedHandle,
DirectAssetAccessExt, ExtractableAsset, Handle, UntypedHandle,
};
}

Expand Down
58 changes: 58 additions & 0 deletions crates/bevy_asset/src/render_asset.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::Asset;

bitflags::bitflags! {
/// Defines where the asset will be used.
Expand Down Expand Up @@ -50,3 +53,58 @@ impl Default for RenderAssetUsages {
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD
}
}

/// Error returned when an asset due for extraction has already been extracted
#[derive(Debug, Error, Clone, Copy)]
pub enum AssetExtractionError {
#[error("The asset has already been extracted")]
AlreadyExtracted,
#[error("The asset type does not support extraction. To clone the asset to the renderworld, use `RenderAssetUsages::default()`")]
NoExtractionImplementation,
}

/// Error returned when an asset due for extraction has already been extracted
#[derive(Debug, Error, Clone, Copy)]
pub enum ExtractableAssetAccessError {
#[error("The data has been extracted to the RenderWorld")]
ExtractedToRenderWorld,
}

pub trait ExtractableAsset: Asset + Sized + Clone {
type Data;

/// Take `self` and call `f` with previous gpu data, or error if it has been extracted, replace the data in place and returns the asset which can be extracted to the `RenderWorld`.
fn with_extractable_data(
mut self,
f: impl FnOnce(Result<Self::Data, AssetExtractionError>) -> Self::Data,
) -> Self {
let prev_data = self.extract();
let new_data = f(prev_data);
self.extractable_data_replace(new_data);
self
}

/// Replace the data with a new value and return the old value, or `None` if it has been extracted. Then this asset can be re-extracted to the `RenderWorld`.
fn extractable_data_replace(&mut self, data: Self::Data) -> Option<Self::Data>;

/// Access the extractable data. Returns error if the data has been extracted.
fn extractable_data_ref(&self) -> Result<&Self::Data, ExtractableAssetAccessError>;

/// Mutably access the extractable data. Returns error if the data has been extracted.
fn extractable_data_mut(&mut self) -> Result<&mut Self::Data, ExtractableAssetAccessError>;

/// Extract the data and return it, or error if the data has been extracted.
fn extract(&mut self) -> Result<Self::Data, AssetExtractionError>;

/// Make a copy of the asset to be moved to the `RenderWorld` / gpu. Heavy internal data (pixels, vertex attributes)
/// should be moved into the copy, leaving this asset with only metadata.
/// An error may be returned to indicate that the asset has already been extracted.
///
/// This can be called in `RenderAsset::take_gpu_data`.
fn take_gpu_data(&mut self) -> Result<Self, AssetExtractionError> {
let data = self.extract()?;
let mut new_asset = self.clone();
new_asset.extractable_data_replace(data);
Ok(new_asset)
}
}
10 changes: 6 additions & 4 deletions crates/bevy_camera/src/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use core::borrow::Borrow;

use bevy_asset::ExtractableAsset;
use bevy_ecs::{component::Component, entity::EntityHashMap, reflect::ReflectComponent};
use bevy_math::{
bounding::{Aabb3d, BoundingVolume},
Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles,
};
use bevy_mesh::{Mesh, VertexAttributeValues};
use bevy_reflect::prelude::*;
use core::borrow::Borrow;

pub trait MeshAabb {
/// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space
Expand All @@ -23,8 +23,10 @@ impl MeshAabb for Mesh {
return Some(aabb.into());
}

let Ok(VertexAttributeValues::Float32x3(values)) =
self.try_attribute(Mesh::ATTRIBUTE_POSITION)
let Some(VertexAttributeValues::Float32x3(values)) = self
.extractable_data_ref()
.ok()
.and_then(|d| d.attribute(Mesh::ATTRIBUTE_POSITION))
else {
return None;
};
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/src/tonemapping/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,5 +452,6 @@ pub fn lut_placeholder() -> Image {
texture_view_descriptor: None,
asset_usage: RenderAssetUsages::RENDER_WORLD,
copy_on_resize: false,
is_extracted_to_render_world: false,
}
}
11 changes: 6 additions & 5 deletions crates/bevy_gltf/src/loader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use async_lock::RwLock;
#[cfg(feature = "bevy_animation")]
use bevy_animation::{prelude::*, AnimatedBy, AnimationTargetId};
use bevy_asset::{
io::Reader, AssetLoadError, AssetLoader, AssetPath, Handle, LoadContext, ParseAssetPathError,
ReadAssetBytesError, RenderAssetUsages,
io::Reader, AssetLoadError, AssetLoader, AssetPath, ExtractableAsset, Handle, LoadContext,
ParseAssetPathError, ReadAssetBytesError, RenderAssetUsages,
};
use bevy_camera::{
primitives::Aabb, visibility::Visibility, Camera, Camera3d, OrthographicProjection,
Expand Down Expand Up @@ -691,8 +691,8 @@ impl GltfLoader {
};
let primitive_topology = primitive_topology(primitive.mode())?;

let mut mesh = Mesh::new(primitive_topology, settings.load_meshes);

let mut mesh_asset = Mesh::new(primitive_topology, settings.load_meshes);
let mesh = mesh_asset.extractable_data_mut().unwrap();
// Read vertex attributes
for (semantic, accessor) in primitive.attributes() {
if [Semantic::Joints(0), Semantic::Weights(0)].contains(&semantic) {
Expand Down Expand Up @@ -801,7 +801,8 @@ impl GltfLoader {
});
}

let mesh_handle = load_context.add_labeled_asset(primitive_label.to_string(), mesh);
let mesh_handle =
load_context.add_labeled_asset(primitive_label.to_string(), mesh_asset);
primitives.push(super::GltfPrimitive::new(
&gltf_mesh,
&primitive,
Expand Down
50 changes: 49 additions & 1 deletion crates/bevy_image/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use bevy_reflect::TypePath;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{std_traits::ReflectDefault, Reflect};

use bevy_asset::{uuid_handle, Asset, AssetApp, Assets, Handle, RenderAssetUsages};
use bevy_asset::{
uuid_handle, Asset, AssetApp, Assets, ExtractableAsset, Handle, RenderAssetUsages,
};
use bevy_color::{Color, ColorToComponents, Gray, LinearRgba, Srgba, Xyza};
use bevy_ecs::resource::Resource;
use bevy_math::{AspectRatio, UVec2, UVec3, Vec2};
Expand Down Expand Up @@ -609,6 +611,50 @@ pub struct Image {
pub asset_usage: RenderAssetUsages,
/// Whether this image should be copied on the GPU when resized.
pub copy_on_resize: bool,
/// Whether this image has been extracted to the render world.
pub is_extracted_to_render_world: bool,
}

impl ExtractableAsset for Image {
type Data = Option<Vec<u8>>;

fn extractable_data_replace(&mut self, data: Self::Data) -> Option<Self::Data> {
let old_data = core::mem::replace(&mut self.data, data);
let old_data = if self.is_extracted_to_render_world {
None
} else {
Some(old_data)
};
self.is_extracted_to_render_world = false;
old_data
}

fn extractable_data_ref(&self) -> Result<&Self::Data, bevy_asset::ExtractableAssetAccessError> {
if self.is_extracted_to_render_world {
Err(bevy_asset::ExtractableAssetAccessError::ExtractedToRenderWorld)
} else {
Ok(&self.data)
}
}

fn extractable_data_mut(
&mut self,
) -> Result<&mut Self::Data, bevy_asset::ExtractableAssetAccessError> {
if self.is_extracted_to_render_world {
Err(bevy_asset::ExtractableAssetAccessError::ExtractedToRenderWorld)
} else {
Ok(&mut self.data)
}
}

fn extract(&mut self) -> Result<Self::Data, bevy_asset::AssetExtractionError> {
if self.is_extracted_to_render_world {
Err(bevy_asset::AssetExtractionError::AlreadyExtracted)
} else {
self.is_extracted_to_render_world = true;
Ok(self.data.take())
}
}
}

/// Used in [`Image`], this determines what image sampler to use when rendering. The default setting,
Expand Down Expand Up @@ -1049,6 +1095,7 @@ impl Image {
texture_view_descriptor: None,
asset_usage,
copy_on_resize: false,
is_extracted_to_render_world: false,
}
}

Expand Down Expand Up @@ -1180,6 +1227,7 @@ impl Image {
}),
asset_usage: RenderAssetUsages::default(),
copy_on_resize: true,
is_extracted_to_render_world: false,
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_image/src/serialized_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl SerializedImage {
.map(SerializedTextureViewDescriptor::into_texture_view_descriptor),
asset_usage: RenderAssetUsages::RENDER_WORLD,
copy_on_resize: false,
is_extracted_to_render_world: false,
}
}
}
Expand Down
11 changes: 4 additions & 7 deletions crates/bevy_mesh/src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ use serde::{Deserialize, Serialize};
use thiserror::Error;
use wgpu_types::IndexFormat;

use crate::MeshAccessError;

/// A disjunction of four iterators. This is necessary to have a well-formed type for the output
/// of [`Mesh::triangles`](super::Mesh::triangles), which produces iterators of four different types depending on the
/// of [`MeshExtractableData::triangles`](crate::MeshExtractableData::triangles), which produces iterators of four different types depending on the
/// branch taken.
pub(crate) enum FourIterators<A, B, C, D> {
First(A),
Expand Down Expand Up @@ -58,8 +56,6 @@ pub enum MeshWindingInvertError {
/// * [`PrimitiveTopology::LineList`](super::PrimitiveTopology::LineList), but the indices are not in chunks of 2.
#[error("Indices weren't in chunks according to topology")]
AbruptIndicesEnd,
#[error("Mesh access error: {0}")]
MeshAccessError(#[from] MeshAccessError),
}

/// An error that occurred while trying to extract a collection of triangles from a [`Mesh`](super::Mesh).
Expand All @@ -68,13 +64,14 @@ pub enum MeshTrianglesError {
#[error("Source mesh does not have primitive topology TriangleList or TriangleStrip")]
WrongTopology,

#[error("Source mesh position data does not exist")]
BadPositions,

#[error("Source mesh position data is not Float32x3")]
PositionsFormat,

#[error("Face index data references vertices that do not exist")]
BadIndices,
#[error("mesh access error: {0}")]
MeshAccessError(#[from] MeshAccessError),
}

/// An array of indices into the [`VertexAttributeValues`](super::VertexAttributeValues) for a mesh.
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_mesh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod components;
mod conversions;
mod index;
mod mesh;
mod mesh_extractable_data;
#[cfg(feature = "bevy_mikktspace")]
mod mikktspace;
#[cfg(feature = "morph")]
Expand All @@ -21,6 +22,7 @@ use bitflags::bitflags;
pub use components::*;
pub use index::*;
pub use mesh::*;
pub use mesh_extractable_data::*;
#[cfg(feature = "bevy_mikktspace")]
pub use mikktspace::*;
pub use primitives::*;
Expand Down
Loading