From 3400fb4e6169a22f9528582faf9a786cded93d73 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 1 Jun 2021 19:59:17 -0700 Subject: [PATCH] SubGraphs, Views, Shadows, and more --- Cargo.toml | 6 + crates/bevy_ecs/src/archetype.rs | 5 + crates/bevy_ecs/src/entity/mod.rs | 94 ++- crates/bevy_ecs/src/lib.rs | 127 ++++ crates/bevy_ecs/src/query/state.rs | 60 +- crates/bevy_ecs/src/storage/sparse_set.rs | 2 +- crates/bevy_ecs/src/storage/table.rs | 2 +- crates/bevy_ecs/src/system/commands/mod.rs | 21 + crates/bevy_ecs/src/world/mod.rs | 59 +- crates/bevy_internal/Cargo.toml | 1 + crates/bevy_internal/src/default_plugins.rs | 8 +- crates/bevy_internal/src/lib.rs | 6 + crates/bevy_log/src/lib.rs | 4 +- crates/bevy_render/src/lib.rs | 2 +- crates/bevy_window/Cargo.toml | 1 + crates/bevy_window/src/lib.rs | 4 +- crates/bevy_window/src/raw_window_handle.rs | 37 ++ crates/bevy_window/src/window.rs | 10 + crates/bevy_winit/Cargo.toml | 1 + crates/bevy_winit/src/winit_windows.rs | 3 + crates/crevice/Cargo.toml | 10 +- crates/crevice/crevice-derive/Cargo.toml | 25 + crates/crevice/crevice-derive/src/lib.rs | 301 ++++++++++ crates/crevice/src/glam.rs | 24 + crates/crevice/src/lib.rs | 4 +- crates/crevice/src/mint.rs | 52 ++ crates/crevice/src/std140.rs | 2 + crates/crevice/src/std140/dynamic_uniform.rs | 14 +- crates/crevice/src/std140/primitives.rs | 13 +- crates/crevice/src/std140/sizer.rs | 2 +- crates/crevice/src/std140/traits.rs | 119 +++- crates/crevice/src/std430.rs | 2 + crates/crevice/src/std430/primitives.rs | 13 +- crates/crevice/src/std430/sizer.rs | 2 +- crates/crevice/src/std430/traits.rs | 117 +++- .../std140__more_than_16_alignment.snap | 16 - .../tests/snapshots/std140__point_light.snap | 40 -- .../snapshots/std140__primitive_f32.snap | 24 - .../snapshots/std140__using_vec3_padding.snap | 24 - .../crevice/tests/snapshots/std140__vec3.snap | 24 - crates/crevice/tests/std140.rs | 121 ---- examples/3d/3d_scene_pipelined.rs | 95 +++ examples/3d/render_to_texture.rs | 2 +- examples/async_tasks/async_compute.rs | 2 +- examples/ecs/iter_combinations.rs | 2 +- examples/ecs/query_bundle.rs | 2 +- examples/ecs/system_sets.rs | 2 +- pipelined/bevy_pbr2/Cargo.toml | 30 + pipelined/bevy_pbr2/src/bundle.rs | 32 + pipelined/bevy_pbr2/src/lib.rs | 88 +++ pipelined/bevy_pbr2/src/light.rs | 24 + pipelined/bevy_pbr2/src/material.rs | 17 + pipelined/bevy_pbr2/src/render/light.rs | 424 ++++++++++++++ pipelined/bevy_pbr2/src/render/mod.rs | 367 ++++++++++++ pipelined/bevy_pbr2/src/render/pbr.frag | 298 ++++++++++ pipelined/bevy_pbr2/src/render/pbr.vert | 25 + pipelined/bevy_render2/Cargo.toml | 1 - pipelined/bevy_render2/changes.md | 6 + pipelined/bevy_render2/src/camera/bundle.rs | 10 +- pipelined/bevy_render2/src/camera/camera.rs | 3 +- pipelined/bevy_render2/src/camera/mod.rs | 114 ++-- .../src/core_pipeline/main_pass_2d.rs | 89 +++ .../src/core_pipeline/main_pass_3d.rs | 99 ++++ .../src/core_pipeline/main_pass_driver.rs | 52 ++ .../bevy_render2/src/core_pipeline/mod.rs | 214 +++++++ pipelined/bevy_render2/src/lib.rs | 57 +- pipelined/bevy_render2/src/main_pass/draw.rs | 26 - pipelined/bevy_render2/src/main_pass/mod.rs | 136 ----- pipelined/bevy_render2/src/mesh/mesh.rs | 32 +- .../src/mesh/mesh/mesh_resource_provider.rs | 83 +++ pipelined/bevy_render2/src/mesh/mod.rs | 15 + pipelined/bevy_render2/src/pass/pass.rs | 6 +- .../bevy_render2/src/pipeline/bind_group.rs | 6 +- pipelined/bevy_render2/src/pipeline/mod.rs | 4 +- .../src/pipeline/pipeline_layout.rs | 23 + .../bevy_render2/src/render_command/mod.rs | 20 +- .../bevy_render2/src/render_graph/context.rs | 234 ++++++++ .../bevy_render2/src/render_graph/graph.rs | 151 +++-- .../bevy_render2/src/render_graph/mod.rs | 12 +- .../bevy_render2/src/render_graph/node.rs | 65 ++- .../src/render_graph/node_slot.rs | 180 +++--- .../src/render_graph/nodes/mod.rs | 3 - .../nodes/window_swap_chain_node.rs | 99 ---- .../bevy_render2/src/render_graph/schedule.rs | 552 ------------------ .../bevy_render2/src/render_phase/draw.rs | 59 ++ .../{main_pass => render_phase}/draw_state.rs | 25 + .../bevy_render2/src/render_phase/mod.rs | 47 ++ .../src/render_resource/bind_group.rs | 48 +- .../src/render_resource/buffer_vec.rs | 2 +- .../render_resource_bindings.rs | 25 +- .../src/render_resource/render_resource_id.rs | 15 +- .../src/render_resource/texture.rs | 10 + .../src/render_resource/uniform_vec.rs | 28 +- .../headless_render_resource_context.rs | 40 +- .../src/renderer/render_resource_context.rs | 20 +- .../bevy_render2/src/shader/shader_reflect.rs | 17 +- pipelined/bevy_render2/src/texture/mod.rs | 26 +- pipelined/bevy_render2/src/texture/texture.rs | 11 +- .../bevy_render2/src/texture/texture_cache.rs | 101 ++++ .../src/texture/texture_descriptor.rs | 45 +- .../src/texture/texture_dimension.rs | 21 +- pipelined/bevy_render2/src/view/mod.rs | 95 +++ pipelined/bevy_render2/src/view/window.rs | 87 +++ pipelined/bevy_sprite2/Cargo.toml | 4 - pipelined/bevy_sprite2/src/lib.rs | 11 +- pipelined/bevy_sprite2/src/render/mod.rs | 180 +++--- pipelined/bevy_sprite2/src/render/sprite.vert | 3 +- pipelined/bevy_wgpu2/Cargo.toml | 2 + pipelined/bevy_wgpu2/src/lib.rs | 54 +- pipelined/bevy_wgpu2/src/render_context.rs | 2 +- .../bevy_wgpu2/src/render_graph_executor.rs | 102 ---- .../bevy_wgpu2/src/render_graph_runner.rs | 192 ++++++ pipelined/bevy_wgpu2/src/render_pass.rs | 2 +- .../bevy_wgpu2/src/render_resource_context.rs | 61 +- pipelined/bevy_wgpu2/src/renderer.rs | 43 +- pipelined/bevy_wgpu2/src/resources.rs | 18 +- pipelined/bevy_wgpu2/src/type_converter.rs | 33 +- 117 files changed, 4715 insertions(+), 1818 deletions(-) create mode 100644 crates/bevy_window/src/raw_window_handle.rs create mode 100644 crates/crevice/crevice-derive/Cargo.toml create mode 100644 crates/crevice/crevice-derive/src/lib.rs delete mode 100644 crates/crevice/tests/snapshots/std140__more_than_16_alignment.snap delete mode 100644 crates/crevice/tests/snapshots/std140__point_light.snap delete mode 100644 crates/crevice/tests/snapshots/std140__primitive_f32.snap delete mode 100644 crates/crevice/tests/snapshots/std140__using_vec3_padding.snap delete mode 100644 crates/crevice/tests/snapshots/std140__vec3.snap delete mode 100644 crates/crevice/tests/std140.rs create mode 100644 examples/3d/3d_scene_pipelined.rs create mode 100644 pipelined/bevy_pbr2/Cargo.toml create mode 100644 pipelined/bevy_pbr2/src/bundle.rs create mode 100644 pipelined/bevy_pbr2/src/lib.rs create mode 100644 pipelined/bevy_pbr2/src/light.rs create mode 100644 pipelined/bevy_pbr2/src/material.rs create mode 100644 pipelined/bevy_pbr2/src/render/light.rs create mode 100644 pipelined/bevy_pbr2/src/render/mod.rs create mode 100644 pipelined/bevy_pbr2/src/render/pbr.frag create mode 100644 pipelined/bevy_pbr2/src/render/pbr.vert create mode 100644 pipelined/bevy_render2/changes.md create mode 100644 pipelined/bevy_render2/src/core_pipeline/main_pass_2d.rs create mode 100644 pipelined/bevy_render2/src/core_pipeline/main_pass_3d.rs create mode 100644 pipelined/bevy_render2/src/core_pipeline/main_pass_driver.rs create mode 100644 pipelined/bevy_render2/src/core_pipeline/mod.rs delete mode 100644 pipelined/bevy_render2/src/main_pass/draw.rs delete mode 100644 pipelined/bevy_render2/src/main_pass/mod.rs create mode 100644 pipelined/bevy_render2/src/mesh/mesh/mesh_resource_provider.rs create mode 100644 pipelined/bevy_render2/src/render_graph/context.rs delete mode 100644 pipelined/bevy_render2/src/render_graph/nodes/mod.rs delete mode 100644 pipelined/bevy_render2/src/render_graph/nodes/window_swap_chain_node.rs delete mode 100644 pipelined/bevy_render2/src/render_graph/schedule.rs create mode 100644 pipelined/bevy_render2/src/render_phase/draw.rs rename pipelined/bevy_render2/src/{main_pass => render_phase}/draw_state.rs (84%) create mode 100644 pipelined/bevy_render2/src/render_phase/mod.rs create mode 100644 pipelined/bevy_render2/src/texture/texture_cache.rs create mode 100644 pipelined/bevy_render2/src/view/mod.rs create mode 100644 pipelined/bevy_render2/src/view/window.rs delete mode 100644 pipelined/bevy_wgpu2/src/render_graph_executor.rs create mode 100644 pipelined/bevy_wgpu2/src/render_graph_runner.rs diff --git a/Cargo.toml b/Cargo.toml index fc872217a3..3a01b63e7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ default = [ "bevy_wgpu2", "bevy_sprite2", "bevy_render2", + "bevy_pbr2", "bevy_winit", "render", "png", @@ -54,6 +55,7 @@ bevy_winit = ["bevy_internal/bevy_winit"] bevy_wgpu2 = ["bevy_internal/bevy_wgpu2"] bevy_render2 = ["bevy_internal/bevy_render2"] bevy_sprite2 = ["bevy_internal/bevy_sprite2"] +bevy_pbr2 = ["bevy_internal/bevy_pbr2"] trace_chrome = ["bevy_internal/trace_chrome"] trace = ["bevy_internal/trace"] @@ -142,6 +144,10 @@ path = "examples/2d/texture_atlas.rs" name = "3d_scene" path = "examples/3d/3d_scene.rs" +[[example]] +name = "3d_scene_pipelined" +path = "examples/3d/3d_scene_pipelined.rs" + [[example]] name = "load_gltf" path = "examples/3d/load_gltf.rs" diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 28d3939460..b213660cd7 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -25,6 +25,11 @@ impl ArchetypeId { ArchetypeId(0) } + #[inline] + pub const fn invalid() -> ArchetypeId { + ArchetypeId(usize::MAX) + } + #[inline] pub const fn resource() -> ArchetypeId { ArchetypeId(1) diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 0e866e188e..7160ed6acc 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -26,6 +26,12 @@ pub struct Entity { pub(crate) id: u32, } +pub enum AllocAtWithoutReplacement { + Exists(EntityLocation), + DidNotExist, + ExistsWithWrongGeneration, +} + impl Entity { /// Creates a new entity reference with a generation of 0. pub fn new(id: u32) -> Entity { @@ -242,11 +248,8 @@ impl Entities { } /// Allocate an entity ID directly. - /// - /// Location should be written immediately. pub fn alloc(&mut self) -> Entity { self.verify_flushed(); - self.len += 1; if let Some(id) = self.pending.pop() { let new_free_cursor = self.pending.len() as i64; @@ -294,6 +297,40 @@ impl Entities { loc } + /// Allocate a specific entity ID, overwriting its generation. + /// + /// Returns the location of the entity currently using the given ID, if any. + pub fn alloc_at_without_replacement(&mut self, entity: Entity) -> AllocAtWithoutReplacement { + self.verify_flushed(); + + let result = if entity.id as usize >= self.meta.len() { + self.pending.extend((self.meta.len() as u32)..entity.id); + let new_free_cursor = self.pending.len() as i64; + *self.free_cursor.get_mut() = new_free_cursor; + self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY); + self.len += 1; + AllocAtWithoutReplacement::DidNotExist + } else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) { + self.pending.swap_remove(index); + let new_free_cursor = self.pending.len() as i64; + *self.free_cursor.get_mut() = new_free_cursor; + self.len += 1; + AllocAtWithoutReplacement::DidNotExist + } else { + let current_meta = &mut self.meta[entity.id as usize]; + if current_meta.location.archetype_id == ArchetypeId::invalid() { + AllocAtWithoutReplacement::DidNotExist + } else if current_meta.generation == entity.generation { + AllocAtWithoutReplacement::Exists(current_meta.location) + } else { + return AllocAtWithoutReplacement::ExistsWithWrongGeneration; + } + }; + + self.meta[entity.id as usize].generation = entity.generation; + result + } + /// Destroy an entity, allowing it to be reused. /// /// Must not be called while reserved entities are awaiting `flush()`. @@ -342,23 +379,13 @@ impl Entities { self.len = 0; } - /// Access the location storage of an entity. - /// - /// Must not be called on pending entities. - pub fn get_mut(&mut self, entity: Entity) -> Option<&mut EntityLocation> { - let meta = &mut self.meta[entity.id as usize]; - if meta.generation == entity.generation { - Some(&mut meta.location) - } else { - None - } - } - - /// Returns `Ok(Location { archetype: 0, index: undefined })` for pending entities. + /// Returns `Ok(Location { archetype: Archetype::invalid(), index: undefined })` for pending entities. pub fn get(&self, entity: Entity) -> Option { if (entity.id as usize) < self.meta.len() { let meta = &self.meta[entity.id as usize]; - if meta.generation != entity.generation { + if meta.generation != entity.generation + || meta.location.archetype_id == ArchetypeId::invalid() + { return None; } Some(meta.location) @@ -401,7 +428,7 @@ impl Entities { /// Allocates space for entities previously reserved with `reserve_entity` or /// `reserve_entities`, then initializes each one using the supplied function. - pub fn flush(&mut self, mut init: impl FnMut(Entity, &mut EntityLocation)) { + pub unsafe fn flush(&mut self, mut init: impl FnMut(Entity, &mut EntityLocation)) { let free_cursor = self.free_cursor.get_mut(); let current_free_cursor = *free_cursor; @@ -439,6 +466,16 @@ impl Entities { } } + // Flushes all reserved entities to an "invalid" state. Attempting to retrieve them will return None + // unless they are later populated with a valid archetype. + pub fn flush_as_invalid(&mut self) { + unsafe { + self.flush(|_entity, location| { + location.archetype_id = ArchetypeId::invalid(); + }) + } + } + #[inline] pub fn len(&self) -> u32 { self.len @@ -460,7 +497,7 @@ impl EntityMeta { const EMPTY: EntityMeta = EntityMeta { generation: 0, location: EntityLocation { - archetype_id: ArchetypeId::empty(), + archetype_id: ArchetypeId::invalid(), index: usize::max_value(), // dummy value, to be filled in }, }; @@ -493,7 +530,24 @@ mod tests { fn reserve_entity_len() { let mut e = Entities::default(); e.reserve_entity(); - e.flush(|_, _| {}); + unsafe { e.flush(|_, _| {}) }; assert_eq!(e.len(), 1); } + + #[test] + fn get_reserved_and_invalid() { + let mut entities = Entities::default(); + let e = entities.reserve_entity(); + assert!(entities.contains(e)); + assert!(entities.get(e).is_none()); + + unsafe { + entities.flush(|_entity, _location| { + // do nothing ... leaving entity location invalid + }) + }; + + assert!(entities.contains(e)); + assert!(entities.get(e).is_none()); + } } diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 69ea8d33a5..dd2d3e23c0 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -1227,6 +1227,7 @@ mod tests { assert_eq!(dropped2.load(Ordering::Relaxed), 1); } + #[test] fn clear_entities() { let mut world = World::default(); world @@ -1266,4 +1267,130 @@ mod tests { "world should still contain resources" ); } + + #[test] + fn reserve_entities_across_worlds() { + let mut world_a = World::default(); + let mut world_b = World::default(); + + let e1 = world_a.spawn().insert(1u32).id(); + let e2 = world_a.spawn().insert(2u32).id(); + let e3 = world_a.entities().reserve_entity(); + world_a.flush(); + + let world_a_max_entities = world_a.entities().meta.len(); + world_b + .entities + .reserve_entities(world_a_max_entities as u32); + world_b.entities.flush_as_invalid(); + + let e4 = world_b.spawn().insert(4u32).id(); + assert_eq!( + e4, + Entity { + generation: 0, + id: 3, + }, + "new entity is created immediately after world_a's max entity" + ); + assert!(world_b.get::(e1).is_none()); + assert!(world_b.get_entity(e1).is_none()); + + assert!(world_b.get::(e2).is_none()); + assert!(world_b.get_entity(e2).is_none()); + + assert!(world_b.get::(e3).is_none()); + assert!(world_b.get_entity(e3).is_none()); + + world_b.get_or_spawn(e1).unwrap().insert(1.0f32); + assert_eq!( + world_b.get::(e1), + Some(&1.0f32), + "spawning into 'world_a' entities works" + ); + + world_b.get_or_spawn(e4).unwrap().insert(4.0f32); + assert_eq!( + world_b.get::(e4), + Some(&4.0f32), + "spawning into existing `world_b` entities works" + ); + assert_eq!( + world_b.get::(e4), + Some(&4u32), + "spawning into existing `world_b` entities works" + ); + + let e4_mismatched_generation = Entity { + generation: 1, + id: 3, + }; + assert!( + world_b.get_or_spawn(e4_mismatched_generation).is_none(), + "attempting to spawn on top of an entity with a mismatched entity generation fails" + ); + assert_eq!( + world_b.get::(e4), + Some(&4.0f32), + "failed mismatched spawn doesn't change existing entity" + ); + assert_eq!( + world_b.get::(e4), + Some(&4u32), + "failed mismatched spawn doesn't change existing entity" + ); + + let high_non_existent_entity = Entity { + generation: 0, + id: 6, + }; + world_b + .get_or_spawn(high_non_existent_entity) + .unwrap() + .insert(10.0f32); + assert_eq!( + world_b.get::(high_non_existent_entity), + Some(&10.0f32), + "inserting into newly allocated high / non-continous entity id works" + ); + + let high_non_existent_but_reserved_entity = Entity { + generation: 0, + id: 5, + }; + assert!( + world_b.get_entity(high_non_existent_but_reserved_entity).is_none(), + "entities between high-newly allocated entity and continuous block of existing entities don't exist" + ); + + let reserved_entities = vec![ + world_b.entities().reserve_entity(), + world_b.entities().reserve_entity(), + world_b.entities().reserve_entity(), + world_b.entities().reserve_entity(), + ]; + + assert_eq!( + reserved_entities, + vec![ + Entity { + generation: 0, + id: 5 + }, + Entity { + generation: 0, + id: 4 + }, + Entity { + generation: 0, + id: 7, + }, + Entity { + generation: 0, + id: 8, + }, + ], + "space between original entities and high entities is used for new entity ids" + ); + } } diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 05c009c6e8..17b76f12df 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -64,7 +64,7 @@ where matched_archetypes: Default::default(), archetype_component_access: Default::default(), }; - state.validate_world_and_update_archetypes(world); + state.update_archetypes(world); state } @@ -78,11 +78,8 @@ where } } - pub fn validate_world_and_update_archetypes(&mut self, world: &World) { - if world.id() != self.world_id { - panic!("Attempted to use {} with a mismatched World. QueryStates can only be used with the World they were created from.", - std::any::type_name::()); - } + pub fn update_archetypes(&mut self, world: &World) { + self.validate_world(world); let archetypes = world.archetypes(); let new_generation = archetypes.generation(); let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); @@ -93,6 +90,14 @@ where } } + #[inline] + pub fn validate_world(&self, world: &World) { + if world.id() != self.world_id { + panic!("Attempted to use {} with a mismatched World. QueryStates can only be used with the World they were created from.", + std::any::type_name::()); + } + } + pub fn new_archetype(&mut self, archetype: &Archetype) { if self.fetch_state.matches_archetype(archetype) && self.filter_state.matches_archetype(archetype) @@ -129,6 +134,27 @@ where unsafe { self.get_unchecked(world, entity) } } + #[inline] + pub fn get_manual<'w>( + &self, + world: &'w World, + entity: Entity, + ) -> Result<>::Item, QueryEntityError> + where + Q::Fetch: ReadOnlyFetch, + { + self.validate_world(world); + // SAFETY: query is read only and world is validated + unsafe { + self.get_unchecked_manual( + world, + entity, + world.last_change_tick(), + world.read_change_tick(), + ) + } + } + #[inline] pub fn get_mut<'w>( &mut self, @@ -149,7 +175,7 @@ where world: &'w World, entity: Entity, ) -> Result<>::Item, QueryEntityError> { - self.validate_world_and_update_archetypes(world); + self.update_archetypes(world); self.get_unchecked_manual( world, entity, @@ -202,6 +228,18 @@ where unsafe { self.iter_unchecked(world) } } + #[inline] + pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, Q, F> + where + Q::Fetch: ReadOnlyFetch, + { + self.validate_world(world); + // SAFETY: query is read only and world is validated + unsafe { + self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) + } + } + #[inline] pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, Q, F> { // SAFETY: query has unique world access @@ -238,7 +276,7 @@ where &'s mut self, world: &'w World, ) -> QueryIter<'w, 's, Q, F> { - self.validate_world_and_update_archetypes(world); + self.update_archetypes(world); self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) } @@ -251,7 +289,7 @@ where &'s mut self, world: &'w World, ) -> QueryCombinationIter<'w, 's, Q, F, K> { - self.validate_world_and_update_archetypes(world); + self.update_archetypes(world); self.iter_combinations_unchecked_manual( world, world.last_change_tick(), @@ -325,7 +363,7 @@ where world: &'w World, func: impl FnMut(>::Item), ) { - self.validate_world_and_update_archetypes(world); + self.update_archetypes(world); self.for_each_unchecked_manual( world, func, @@ -376,7 +414,7 @@ where batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, ) { - self.validate_world_and_update_archetypes(world); + self.update_archetypes(world); self.par_for_each_unchecked_manual( world, task_pool, diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index d98b647a0e..42357ec87c 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -108,7 +108,7 @@ impl ComponentSparseSet { pub fn clear(&mut self) { self.dense.clear(); - self.ticks.get_mut().clear(); + self.ticks.clear(); self.entities.clear(); self.sparse.clear(); } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 867656172c..9c041f8174 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -181,7 +181,7 @@ impl Column { pub fn clear(&mut self) { self.data.clear(); - self.ticks.get_mut().clear(); + self.ticks.clear(); } #[inline] diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 2edc4c7a46..27db5a49cf 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -58,6 +58,16 @@ impl<'a> Commands<'a> { } } + pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'a, '_> { + self.add(GetOrSpawn { + entity, + }); + EntityCommands { + entity, + commands: self, + } + } + // TODO: this is a hack to work around the "multiple worlds" limitations: // Right now Commands must allocate entities from their "scheduled" world, but Commands might be applied to other worlds, // such as the "render world" @@ -278,6 +288,17 @@ where } } +pub struct GetOrSpawn { + entity: Entity, +} + +impl Command for GetOrSpawn +{ + fn write(self: Box, world: &mut World) { + world.get_or_spawn(self.entity); + } +} + pub struct SpawnBatch where I: IntoIterator, diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index ac3e2c906f..843beb9708 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -16,7 +16,7 @@ use crate::{ Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError, StorageType, }, - entity::{Entities, Entity}, + entity::{AllocAtWithoutReplacement, Entities, Entity}, query::{FilterFetch, QueryState, WorldQuery}, storage::{Column, SparseSet, Storages}, }; @@ -94,6 +94,13 @@ impl World { &self.entities } + /// Retrieves this world's [Entities] collection mutably + #[inline] + pub fn entities_mut(&mut self) -> &mut Entities { + &mut self.entities + } + + /// Retrieves this world's [Archetypes] collection #[inline] pub fn archetypes(&self) -> &Archetypes { @@ -215,6 +222,24 @@ impl World { self.get_entity_mut(entity).expect("Entity does not exist") } + /// Returns an EntityMut for an existing entity or creates one if it doesn't exist. + /// This will return `None` if the entity exists with a different generation. + #[inline] + pub fn get_or_spawn(&mut self, entity: Entity) -> Option { + self.flush(); + match self.entities.alloc_at_without_replacement(entity) { + AllocAtWithoutReplacement::Exists(location) => { + // SAFE: `entity` exists and `location` is that entity's location + Some(unsafe { EntityMut::new(self, entity, location) }) + } + AllocAtWithoutReplacement::DidNotExist => { + // SAFE: entity was just allocated + Some(unsafe { self.spawn_at_internal(entity) }) + } + AllocAtWithoutReplacement::ExistsWithWrongGeneration => None, + } + } + /// Retrieves an [EntityRef] that exposes read-only operations for the given `entity`. /// Returns [None] if the `entity` does not exist. Use [World::entity] if you don't want /// to unwrap the [EntityRef] yourself. @@ -293,20 +318,25 @@ impl World { pub fn spawn(&mut self) -> EntityMut { self.flush(); let entity = self.entities.alloc(); + // SAFE: entity was just allocated + unsafe { self.spawn_at_internal(entity) } + } + + /// # Safety + /// must be called on an entity that was just allocated + unsafe fn spawn_at_internal(&mut self, entity: Entity) -> EntityMut { let archetype = self.archetypes.empty_mut(); - unsafe { - // PERF: consider avoiding allocating entities in the empty archetype unless needed - let table_row = self.storages.tables[archetype.table_id()].allocate(entity); - // SAFE: no components are allocated by archetype.allocate() because the archetype is - // empty - let location = archetype.allocate(entity, table_row); - // SAFE: entity index was just allocated - self.entities - .meta - .get_unchecked_mut(entity.id() as usize) - .location = location; - EntityMut::new(self, entity, location) - } + // PERF: consider avoiding allocating entities in the empty archetype unless needed + let table_row = self.storages.tables[archetype.table_id()].allocate(entity); + // SAFE: no components are allocated by archetype.allocate() because the archetype is + // empty + let location = archetype.allocate(entity, table_row); + // SAFE: entity index was just allocated + self.entities + .meta + .get_unchecked_mut(entity.id() as usize) + .location = location; + EntityMut::new(self, entity, location) } /// Spawns a batch of entities with the same component [Bundle] type. Takes a given [Bundle] @@ -878,6 +908,7 @@ impl World { unsafe { let table = &mut self.storages.tables[empty_archetype.table_id()]; // PERF: consider pre-allocating space for flushed entities + // SAFE: entity is set to a valid location self.entities.flush(|entity, location| { // SAFE: no components are allocated by archetype.allocate() because the archetype // is empty diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index cfdf643639..0515cdd792 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -68,6 +68,7 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.5.0" } bevy_audio = { path = "../bevy_audio", optional = true, version = "0.5.0" } bevy_gltf = { path = "../bevy_gltf", optional = true, version = "0.5.0" } bevy_pbr = { path = "../bevy_pbr", optional = true, version = "0.5.0" } +bevy_pbr2 = { path = "../../pipelined/bevy_pbr2", optional = true, version = "0.5.0" } bevy_render = { path = "../bevy_render", optional = true, version = "0.5.0" } bevy_render2 = { path = "../../pipelined/bevy_render2", optional = true, version = "0.5.0" } bevy_dynamic_plugin = { path = "../bevy_dynamic_plugin", optional = true, version = "0.5.0" } diff --git a/crates/bevy_internal/src/default_plugins.rs b/crates/bevy_internal/src/default_plugins.rs index 9d5d4886a0..b9a0d508d1 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -119,7 +119,10 @@ impl PluginGroup for PipelinedDefaultPlugins { group.add(bevy_asset::AssetPlugin::default()); #[cfg(feature = "bevy_render2")] - group.add(bevy_render2::RenderPlugin::default()); + { + group.add(bevy_render2::RenderPlugin::default()); + group.add(bevy_render2::core_pipeline::CorePipelinePlugin::default()); + } #[cfg(feature = "bevy_winit")] group.add(bevy_winit::WinitPlugin::default()); @@ -129,5 +132,8 @@ impl PluginGroup for PipelinedDefaultPlugins { #[cfg(feature = "bevy_sprite2")] group.add(bevy_sprite2::SpritePlugin::default()); + + #[cfg(feature = "bevy_pbr2")] + group.add(bevy_pbr2::PbrPlugin::default()); } } diff --git a/crates/bevy_internal/src/lib.rs b/crates/bevy_internal/src/lib.rs index 085ee803ad..f8648b0c58 100644 --- a/crates/bevy_internal/src/lib.rs +++ b/crates/bevy_internal/src/lib.rs @@ -99,6 +99,12 @@ pub mod pbr { pub use bevy_pbr::*; } +#[cfg(feature = "bevy_pbr2")] +pub mod pbr2 { + //! Physically based rendering. + pub use bevy_pbr2::*; +} + #[cfg(feature = "bevy_render")] pub mod render { //! Cameras, meshes, textures, shaders, and pipelines. diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index 8336cff5f0..6e672943fb 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -33,7 +33,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; /// # use bevy_log::LogSettings; /// # use bevy_utils::tracing::Level; /// fn main() { -/// App::build() +/// App::new() /// .insert_resource(LogSettings { /// level: Level::DEBUG, /// filter: "wgpu=error,bevy_render=info".to_string(), @@ -53,7 +53,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; /// # use bevy_app::App; /// # use bevy_log::LogPlugin; /// fn main() { -/// App::build() +/// App::new() /// .add_plugins_with(DefaultPlugins, |group| group.disable::()) /// .run(); /// } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 8eb28cfb02..2ff192eeb6 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -102,7 +102,7 @@ impl Default for RenderPlugin { } impl Plugin for RenderPlugin { - fn build(&self, app: &mut AppBuilder) { + fn build(&self, app: &mut App) { #[cfg(any( feature = "png", feature = "dds", diff --git a/crates/bevy_window/Cargo.toml b/crates/bevy_window/Cargo.toml index fc16c99b93..ac2617feb1 100644 --- a/crates/bevy_window/Cargo.toml +++ b/crates/bevy_window/Cargo.toml @@ -18,6 +18,7 @@ bevy_app = { path = "../bevy_app", version = "0.5.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.5.0" } bevy_math = { path = "../bevy_math", version = "0.5.0" } bevy_utils = { path = "../bevy_utils", version = "0.5.0" } +raw-window-handle = "0.3.0" # other diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 02aaa3bfa1..00826777cc 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -1,9 +1,10 @@ mod event; +mod raw_window_handle; mod system; mod window; mod windows; -use bevy_ecs::system::IntoSystem; +pub use crate::raw_window_handle::*; pub use event::*; pub use system::*; pub use window::*; @@ -18,6 +19,7 @@ pub mod prelude { } use bevy_app::{prelude::*, Events}; +use bevy_ecs::system::IntoSystem; pub struct WindowPlugin { pub add_primary_window: bool, diff --git a/crates/bevy_window/src/raw_window_handle.rs b/crates/bevy_window/src/raw_window_handle.rs new file mode 100644 index 0000000000..2393975490 --- /dev/null +++ b/crates/bevy_window/src/raw_window_handle.rs @@ -0,0 +1,37 @@ +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; + +/// This wrapper exist to enable safely passing a [`RawWindowHandle`] across threads. Extracting the handle +/// is still an unsafe operation, so the caller must still validate that using the raw handle is safe for a given context. +#[derive(Debug, Clone)] +pub struct RawWindowHandleWrapper(RawWindowHandle); + +impl RawWindowHandleWrapper { + pub(crate) fn new(handle: RawWindowHandle) -> Self { + Self(handle) + } + + /// # Safety + /// This returns a [`HasRawWindowHandle`] impl, which exposes [`RawWindowHandle`]. Some platforms + /// have constraints on where/how this handle can be used. For example, some platforms don't support doing window + /// operations off of the main thread. The caller must ensure the [`RawWindowHandle`] is only used in valid contexts. + pub unsafe fn get_handle(&self) -> HasRawWindowHandleWrapper { + HasRawWindowHandleWrapper(self.0.clone()) + } +} + +// SAFE: RawWindowHandle is just a normal "raw pointer", which doesn't impl Send/Sync. However the pointer is only +// exposed via an unsafe method that forces the user to make a call for a given platform. (ex: some platforms don't +// support doing window operations off of the main thread). +// A recommendation for this pattern (and more context) is available here: +// https://github.com/rust-windowing/raw-window-handle/issues/59 +unsafe impl Send for RawWindowHandleWrapper {} +unsafe impl Sync for RawWindowHandleWrapper {} + +pub struct HasRawWindowHandleWrapper(RawWindowHandle); + +// SAFE: the caller has validated that this is a valid context to get RawWindowHandle +unsafe impl HasRawWindowHandle for HasRawWindowHandleWrapper { + fn raw_window_handle(&self) -> RawWindowHandle { + self.0.clone() + } +} diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index be3a6c0f99..38de0e3fce 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -1,5 +1,6 @@ use bevy_math::{IVec2, Vec2}; use bevy_utils::{tracing::warn, Uuid}; +use raw_window_handle::RawWindowHandle; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct WindowId(Uuid); @@ -20,6 +21,8 @@ impl WindowId { use std::fmt; +use crate::raw_window_handle::RawWindowHandleWrapper; + impl fmt::Display for WindowId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.to_simple().fmt(f) @@ -123,6 +126,7 @@ pub struct Window { cursor_visible: bool, cursor_locked: bool, cursor_position: Option, + raw_window_handle: RawWindowHandleWrapper, focused: bool, mode: WindowMode, #[cfg(target_arch = "wasm32")] @@ -198,6 +202,7 @@ impl Window { physical_height: u32, scale_factor: f64, position: Option, + raw_window_handle: RawWindowHandle, ) -> Self { Window { id, @@ -216,6 +221,7 @@ impl Window { cursor_visible: window_descriptor.cursor_visible, cursor_locked: window_descriptor.cursor_locked, cursor_position: None, + raw_window_handle: RawWindowHandleWrapper::new(raw_window_handle), focused: true, mode: window_descriptor.mode, #[cfg(target_arch = "wasm32")] @@ -511,6 +517,10 @@ impl Window { pub fn is_focused(&self) -> bool { self.focused } + + pub fn raw_window_handle(&self) -> RawWindowHandleWrapper { + self.raw_window_handle.clone() + } } #[derive(Debug, Clone)] diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index 677c20ea57..53e6400871 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -27,6 +27,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.5.0" } # other winit = { version = "0.25.0", default-features = false } +raw-window-handle = "0.3.0" [target.'cfg(target_arch = "wasm32")'.dependencies] winit = { version = "0.25.0", features = ["web-sys"], default-features = false } diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index e3a3c518c0..1874a9a02d 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,6 +1,7 @@ use bevy_math::IVec2; use bevy_utils::HashMap; use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode}; +use raw_window_handle::HasRawWindowHandle; use winit::dpi::LogicalSize; #[derive(Debug, Default)] @@ -137,6 +138,7 @@ impl WinitWindows { .map(|position| IVec2::new(position.x, position.y)); let inner_size = winit_window.inner_size(); let scale_factor = winit_window.scale_factor(); + let raw_window_handle = winit_window.raw_window_handle(); self.windows.insert(winit_window.id(), winit_window); Window::new( window_id, @@ -145,6 +147,7 @@ impl WinitWindows { inner_size.height, scale_factor, position, + raw_window_handle, ) } diff --git a/crates/crevice/Cargo.toml b/crates/crevice/Cargo.toml index b7e3eb05f9..93a77d7da8 100644 --- a/crates/crevice/Cargo.toml +++ b/crates/crevice/Cargo.toml @@ -13,15 +13,19 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["std"] +std = [] + [dependencies] -crevice-derive = "0.6.0" +crevice-derive = { version = "0.6.0", path = "crevice-derive" } bytemuck = "1.4.1" -mint = { version = "0.5.5", optional = true } +mint = "0.5.5" glam = "0.15.1" [dev-dependencies] cgmath = { version = "0.17.0", features = ["mint"] } insta = "0.16.1" type-layout = { version = "0.2.0", features = ["serde1"] } -crevice-derive = { version = "0.6.0", features = ["test_type_layout"] } +crevice-derive = { version = "0.6.0", path = "crevice-derive" } diff --git a/crates/crevice/crevice-derive/Cargo.toml b/crates/crevice/crevice-derive/Cargo.toml new file mode 100644 index 0000000000..a84a1d4625 --- /dev/null +++ b/crates/crevice/crevice-derive/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "crevice-derive" +description = "Derive crate for the 'crevice' crate" +version = "0.6.0" +edition = "2018" +authors = ["Lucien Greathouse "] +documentation = "https://docs.rs/crevice-derive" +homepage = "https://github.com/LPGhatguy/crevice" +repository = "https://github.com/LPGhatguy/crevice" +license = "MIT OR Apache-2.0" + +[features] +# Feature used for testing; enables type_layout derive on types. +# Requires crate using derive to depend on type_layout as well. +test_type_layout = [] + +[lib] +proc-macro = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = "1.0.40" +quote = "1.0.7" +proc-macro2 = "1.0.21" diff --git a/crates/crevice/crevice-derive/src/lib.rs b/crates/crevice/crevice-derive/src/lib.rs new file mode 100644 index 0000000000..ee7bfe1d34 --- /dev/null +++ b/crates/crevice/crevice-derive/src/lib.rs @@ -0,0 +1,301 @@ +use proc_macro::TokenStream as CompilerTokenStream; + +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, Ident, Path}; + +#[proc_macro_derive(AsStd140)] +pub fn derive_as_std140(input: CompilerTokenStream) -> CompilerTokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = EmitOptions::new("Std140", "std140", 16).emit(input); + + CompilerTokenStream::from(expanded) +} + +#[proc_macro_derive(AsStd430)] +pub fn derive_as_std430(input: CompilerTokenStream) -> CompilerTokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = EmitOptions::new("Std430", "std430", 0).emit(input); + + CompilerTokenStream::from(expanded) +} + +struct EmitOptions { + /// The Rust-friendly name of the layout, like Std140. + layout_name: Ident, + + /// The minimum alignment for a struct in this layout. + min_struct_alignment: usize, + + /// The fully-qualified path to the Crevice module containing everything for + /// this layout. + mod_path: Path, + + /// The fully-qualified path to the trait defining a type in this layout. + trait_path: Path, + + /// The fully-qualified path to the trait implemented for types that can be + /// converted into this layout, like AsStd140. + as_trait_path: Path, + + /// The name of the associated type contained in AsTrait. + as_trait_assoc: Ident, + + /// The name of the method used to convert from AsTrait to Trait. + as_trait_method: Ident, + + // The name of the method used to convert from Trait to AsTrait. + from_trait_method: Ident, + + /// The name of the struct used for Padded type. + padded_name: Ident, +} + +impl EmitOptions { + fn new(layout_name: &'static str, mod_name: &'static str, min_struct_alignment: usize) -> Self { + let mod_name = Ident::new(mod_name, Span::call_site()); + let layout_name = Ident::new(layout_name, Span::call_site()); + + let mod_path = parse_quote!(::crevice::#mod_name); + let trait_path = parse_quote!(#mod_path::#layout_name); + + let as_trait_name = format_ident!("As{}", layout_name); + let as_trait_path = parse_quote!(#mod_path::#as_trait_name); + let as_trait_assoc = format_ident!("{}Type", layout_name); + let as_trait_method = format_ident!("as_{}", mod_name); + let from_trait_method = format_ident!("from_{}", mod_name); + + let padded_name = format_ident!("{}Padded", layout_name); + + Self { + layout_name, + min_struct_alignment, + + mod_path, + trait_path, + as_trait_path, + as_trait_assoc, + as_trait_method, + from_trait_method, + + padded_name, + } + } + + fn emit(&self, input: DeriveInput) -> TokenStream { + let min_struct_alignment = self.min_struct_alignment; + let layout_name = &self.layout_name; + let mod_path = &self.mod_path; + let trait_path = &self.trait_path; + let as_trait_path = &self.as_trait_path; + let as_trait_assoc = &self.as_trait_assoc; + let as_trait_method = &self.as_trait_method; + let from_trait_method = &self.from_trait_method; + let padded_name = &self.padded_name; + + let visibility = input.vis; + + let name = input.ident; + let generated_name = format_ident!("{}{}", layout_name, name); + let alignment_mod_name = format_ident!("{}{}Alignment", layout_name, name); + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let fields = match &input.data { + Data::Struct(data) => match &data.fields { + Fields::Named(fields) => fields, + Fields::Unnamed(_) => panic!("Tuple structs are not supported"), + Fields::Unit => panic!("Unit structs are not supported"), + }, + Data::Enum(_) | Data::Union(_) => panic!("Only structs are supported"), + }; + + // Generate the names we'll use for calculating alignment of each field. + // Each name will turn into a const fn that's invoked to compute the + // size of a padding array before each field. + let align_names: Vec<_> = fields + .named + .iter() + .map(|field| format_ident!("_{}_align", field.ident.as_ref().unwrap())) + .collect(); + + // Generate one function per field that is used to apply alignment + // padding. Each function invokes all previous functions to calculate + // the total offset into the struct for the current field, then aligns + // up to the nearest multiple of alignment. + let alignment_calculators: Vec<_> = fields + .named + .iter() + .enumerate() + .map(|(index, field)| { + let align_name = &align_names[index]; + + let offset_accumulation = + fields + .named + .iter() + .zip(&align_names) + .take(index) + .map(|(field, align_name)| { + let field_ty = &field.ty; + quote! { + offset += #align_name(); + offset += ::core::mem::size_of::<<#field_ty as #as_trait_path>::#as_trait_assoc>(); + } + }); + + let pad_at_end = index + .checked_sub(1) + .map_or(quote!{0usize}, |prev_index|{ + let field = &fields.named[prev_index]; + let field_ty = &field.ty; + quote! { + if <<#field_ty as #as_trait_path>::#as_trait_assoc as #mod_path::#layout_name>::PAD_AT_END { + <<#field_ty as #as_trait_path>::#as_trait_assoc as #mod_path::#layout_name>::ALIGNMENT + } + else { + 0usize + } + } + }); + + let field_ty = &field.ty; + + quote! { + pub const fn #align_name() -> usize { + let mut offset = 0; + #( #offset_accumulation )* + + ::crevice::internal::align_offset( + offset, + ::crevice::internal::max( + <<#field_ty as #as_trait_path>::#as_trait_assoc as #mod_path::#layout_name>::ALIGNMENT, + #pad_at_end + ) + ) + } + } + }) + .collect(); + + // Generate the struct fields that will be present in the generated + // struct. Each field in the original struct turns into two fields in + // the generated struct: + // + // * Alignment, a byte array whose size is computed from #align_name(). + // * Data, the layout-specific version of the original field. + let generated_fields: Vec<_> = fields + .named + .iter() + .zip(&align_names) + .map(|(field, align_name)| { + let field_ty = &field.ty; + let field_name = field.ident.as_ref().unwrap(); + + quote! { + #align_name: [u8; #alignment_mod_name::#align_name()], + #field_name: <#field_ty as #as_trait_path>::#as_trait_assoc, + } + }) + .collect(); + + // Generate an initializer for each field in the original struct. + // Alignment fields are filled in with zeroes using struct update + // syntax. + let field_initializers: Vec<_> = fields + .named + .iter() + .map(|field| { + let field_name = field.ident.as_ref().unwrap(); + + quote!(#field_name: self.#field_name.#as_trait_method()) + }) + .collect(); + + let field_unwrappers: Vec<_> = fields + .named + .iter() + .map(|field|{ + let field_name = field.ident.as_ref().unwrap(); + let field_ty = &field.ty; + quote!(#field_name: <#field_ty as #as_trait_path>::#from_trait_method(value.#field_name)) + }) + .collect(); + + // This fold builds up an expression that finds the maximum alignment out of + // all of the fields in the struct. For this struct: + // + // struct Foo { a: ty1, b: ty2 } + // + // ...we should generate an expression like this: + // + // max(ty2_align, max(ty1_align, min_align)) + let struct_alignment = fields.named.iter().fold( + quote!(#min_struct_alignment), + |last, field| { + let field_ty = &field.ty; + + quote! { + ::crevice::internal::max( + <<#field_ty as #as_trait_path>::#as_trait_assoc as #trait_path>::ALIGNMENT, + #last, + ) + } + }, + ); + + // For testing purposes, we can optionally generate type layout + // information using the type-layout crate. + let type_layout_derive = if cfg!(feature = "test_type_layout") { + quote!(#[derive(::type_layout::TypeLayout)]) + } else { + quote!() + }; + + quote! { + #[allow(non_snake_case)] + mod #alignment_mod_name { + use super::*; + + #( #alignment_calculators )* + } + + #[derive(Debug, Clone, Copy)] + #type_layout_derive + #[repr(C)] + #visibility struct #generated_name #ty_generics #where_clause { + #( #generated_fields )* + } + + unsafe impl #impl_generics ::crevice::internal::bytemuck::Zeroable for #generated_name #ty_generics #where_clause {} + unsafe impl #impl_generics ::crevice::internal::bytemuck::Pod for #generated_name #ty_generics #where_clause {} + + unsafe impl #impl_generics #mod_path::#layout_name for #generated_name #ty_generics #where_clause { + const ALIGNMENT: usize = #struct_alignment; + const PAD_AT_END: bool = true; + type Padded = #mod_path::#padded_name(), + #struct_alignment + )}>; + } + + impl #impl_generics #as_trait_path for #name #ty_generics #where_clause { + type #as_trait_assoc = #generated_name; + + fn #as_trait_method(&self) -> Self::#as_trait_assoc { + Self::#as_trait_assoc { + #( #field_initializers, )* + + ..::crevice::internal::bytemuck::Zeroable::zeroed() + } + } + + fn #from_trait_method(value: Self::#as_trait_assoc) -> Self { + Self { + #( #field_unwrappers, )* + } + } + } + } + } +} diff --git a/crates/crevice/src/glam.rs b/crates/crevice/src/glam.rs index 1c5b604aa2..18fe4cbf67 100644 --- a/crates/crevice/src/glam.rs +++ b/crates/crevice/src/glam.rs @@ -16,6 +16,10 @@ macro_rules! glam_vectors { )* } } + + fn from_std140(value: Self::Std140Type) -> Self { + Self::new($(value.$field,)*) + } } impl AsStd430 for $glam_ty { @@ -28,6 +32,10 @@ macro_rules! glam_vectors { )* } } + + fn from_std430(value: Self::Std430Type) -> Self { + Self::new($(value.$field,)*) + } } )* }; @@ -53,6 +61,14 @@ macro_rules! glam_matrices { ..Zeroable::zeroed() } } + + fn from_std140(value: Self::Std140Type) -> Self { + Self::from_cols( + $( + <_ as AsStd140>::from_std140(value.$field), + )* + ) + } } impl AsStd430 for $glam_ty { @@ -66,6 +82,14 @@ macro_rules! glam_matrices { ..Zeroable::zeroed() } } + + fn from_std430(value: Self::Std430Type) -> Self { + Self::from_cols( + $( + <_ as AsStd430>::from_std430(value.$field), + )* + ) + } } )* }; diff --git a/crates/crevice/src/lib.rs b/crates/crevice/src/lib.rs index 610d9ceb5f..642c85c664 100644 --- a/crates/crevice/src/lib.rs +++ b/crates/crevice/src/lib.rs @@ -129,6 +129,7 @@ Crevice supports Rust 1.46.0 and newer due to use of new `const fn` features. */ #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] pub mod std140; pub mod std430; @@ -136,7 +137,6 @@ pub mod std430; #[doc(hidden)] pub mod internal; -#[cfg(feature = "mint")] mod mint; -mod glam; +mod glam; \ No newline at end of file diff --git a/crates/crevice/src/mint.rs b/crates/crevice/src/mint.rs index f175d349b6..dbaf628e7c 100644 --- a/crates/crevice/src/mint.rs +++ b/crates/crevice/src/mint.rs @@ -16,6 +16,14 @@ macro_rules! mint_vectors { )* } } + + fn from_std140(value: Self::Std140Type) -> Self { + Self { + $( + $field: value.$field, + )* + } + } } impl AsStd430 for $mint_ty { @@ -28,6 +36,14 @@ macro_rules! mint_vectors { )* } } + + fn from_std430(value: Self::Std430Type) -> Self { + Self { + $( + $field: value.$field, + )* + } + } } )* }; @@ -37,6 +53,22 @@ mint_vectors! { mint::Vector2, Vec2, (x, y), mint::Vector3, Vec3, (x, y, z), mint::Vector4, Vec4, (x, y, z, w), + + mint::Vector2, IVec2, (x, y), + mint::Vector3, IVec3, (x, y, z), + mint::Vector4, IVec4, (x, y, z, w), + + mint::Vector2, UVec2, (x, y), + mint::Vector3, UVec3, (x, y, z), + mint::Vector4, UVec4, (x, y, z, w), + + mint::Vector2, BVec2, (x, y), + mint::Vector3, BVec3, (x, y, z), + mint::Vector4, BVec4, (x, y, z, w), + + mint::Vector2, DVec2, (x, y), + mint::Vector3, DVec3, (x, y, z), + mint::Vector4, DVec4, (x, y, z, w), } macro_rules! mint_matrices { @@ -53,6 +85,14 @@ macro_rules! mint_matrices { ..Zeroable::zeroed() } } + + fn from_std140(value: Self::Std140Type) -> Self { + Self { + $( + $field: <_ as AsStd140>::from_std140(value.$field), + )* + } + } } impl AsStd430 for $mint_ty { @@ -66,6 +106,14 @@ macro_rules! mint_matrices { ..Zeroable::zeroed() } } + + fn from_std430(value: Self::Std430Type) -> Self { + Self { + $( + $field: <_ as AsStd430>::from_std430(value.$field), + )* + } + } } )* }; @@ -75,4 +123,8 @@ mint_matrices! { mint::ColumnMatrix2, Mat2, (x, y), mint::ColumnMatrix3, Mat3, (x, y, z), mint::ColumnMatrix4, Mat4, (x, y, z, w), + + mint::ColumnMatrix2, DMat2, (x, y), + mint::ColumnMatrix3, DMat3, (x, y, z), + mint::ColumnMatrix4, DMat4, (x, y, z, w), } diff --git a/crates/crevice/src/std140.rs b/crates/crevice/src/std140.rs index 82902f9533..184b99a92d 100644 --- a/crates/crevice/src/std140.rs +++ b/crates/crevice/src/std140.rs @@ -5,12 +5,14 @@ mod dynamic_uniform; mod primitives; mod sizer; mod traits; +#[cfg(feature = "std")] mod writer; pub use self::dynamic_uniform::*; pub use self::primitives::*; pub use self::sizer::*; pub use self::traits::*; +#[cfg(feature = "std")] pub use self::writer::*; pub use crevice_derive::AsStd140; diff --git a/crates/crevice/src/std140/dynamic_uniform.rs b/crates/crevice/src/std140/dynamic_uniform.rs index b64443370e..9731ac1650 100644 --- a/crates/crevice/src/std140/dynamic_uniform.rs +++ b/crates/crevice/src/std140/dynamic_uniform.rs @@ -1,6 +1,7 @@ use bytemuck::{Pod, Zeroable}; -use crate::internal::max; +#[allow(unused_imports)] +use crate::internal::{align_offset, max}; use crate::std140::{AsStd140, Std140}; /// Wrapper type that aligns the inner type to at least 256 bytes. @@ -15,6 +16,10 @@ impl AsStd140 for DynamicUniform { fn as_std140(&self) -> Self::Std140Type { DynamicUniformStd140(self.0.as_std140()) } + + fn from_std140(value: Self::Std140Type) -> Self { + DynamicUniform(::from_std140(value.0)) + } } /// std140 variant of [`DynamicUniform`]. @@ -24,6 +29,13 @@ pub struct DynamicUniformStd140(T); unsafe impl Std140 for DynamicUniformStd140 { const ALIGNMENT: usize = max(256, T::ALIGNMENT); + #[cfg(const_evaluatable_checked)] + type Padded = crate::std140::Std140Padded< + Self, + { align_offset(core::mem::size_of::(), max(256, T::ALIGNMENT)) }, + >; + #[cfg(not(const_evaluatable_checked))] + type Padded = crate::std140::InvalidPadded; } unsafe impl Zeroable for DynamicUniformStd140 {} diff --git a/crates/crevice/src/std140/primitives.rs b/crates/crevice/src/std140/primitives.rs index 18f7a6581c..ec142b6b42 100644 --- a/crates/crevice/src/std140/primitives.rs +++ b/crates/crevice/src/std140/primitives.rs @@ -1,21 +1,28 @@ use bytemuck::{Pod, Zeroable}; -use crate::std140::Std140; +use crate::std140::{Std140, Std140Padded}; + +use crate::internal::{align_offset, max}; +use core::mem::size_of; unsafe impl Std140 for f32 { const ALIGNMENT: usize = 4; + type Padded = Std140Padded; } unsafe impl Std140 for f64 { const ALIGNMENT: usize = 8; + type Padded = Std140Padded; } unsafe impl Std140 for i32 { const ALIGNMENT: usize = 4; + type Padded = Std140Padded; } unsafe impl Std140 for u32 { const ALIGNMENT: usize = 4; + type Padded = Std140Padded; } macro_rules! vectors { @@ -37,6 +44,7 @@ macro_rules! vectors { unsafe impl Std140 for $name { const ALIGNMENT: usize = $align; + type Padded = Std140Padded(), max(16, $align))}>; } )+ }; @@ -87,6 +95,9 @@ macro_rules! matrices { unsafe impl Std140 for $name { const ALIGNMENT: usize = $align; + /// Matrices are technically arrays of primitives, and as such require pad at end. + const PAD_AT_END: bool = true; + type Padded = Std140Padded(), max(16, $align))}>; } )+ }; diff --git a/crates/crevice/src/std140/sizer.rs b/crates/crevice/src/std140/sizer.rs index 452e66922e..4d76ad6278 100644 --- a/crates/crevice/src/std140/sizer.rs +++ b/crates/crevice/src/std140/sizer.rs @@ -1,4 +1,4 @@ -use std::mem::size_of; +use core::mem::size_of; use crate::internal::align_offset; use crate::std140::{AsStd140, Std140}; diff --git a/crates/crevice/src/std140/traits.rs b/crates/crevice/src/std140/traits.rs index 1352264963..55eec47463 100644 --- a/crates/crevice/src/std140/traits.rs +++ b/crates/crevice/src/std140/traits.rs @@ -1,8 +1,10 @@ +use core::mem::{size_of, MaybeUninit}; +#[cfg(feature = "std")] use std::io::{self, Write}; -use std::mem::size_of; use bytemuck::{bytes_of, Pod, Zeroable}; +#[cfg(feature = "std")] use crate::std140::Writer; /// Trait implemented for all `std140` primitives. Generally should not be @@ -15,6 +17,15 @@ pub unsafe trait Std140: Copy + Zeroable + Pod { /// control and zero their padding bytes, making converting them to and from /// slices safe. const ALIGNMENT: usize; + /// Whether this type requires a padding at the end (ie, is a struct or an array + /// of primitives). + /// See https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159 + /// (rule 4 and 9) + const PAD_AT_END: bool = false; + /// Padded type (Std140Padded specialization) + /// The usual implementation is + /// type Padded = Std140Padded(), max(16, ALIGNMENT))}>; + type Padded: Std140Convertible; /// Casts the type to a byte array. Implementors should not override this /// method. @@ -27,9 +38,38 @@ pub unsafe trait Std140: Copy + Zeroable + Pod { } } +/// Trait specifically for Std140::Padded, implements conversions between padded type and base type. +pub trait Std140Convertible: Copy { + /// Convert from self to Std140 + fn into_std140(self) -> T; + /// Convert from Std140 to self + fn from_std140(_: T) -> Self; +} + +impl Std140Convertible for T { + fn into_std140(self) -> T { + self + } + fn from_std140(also_self: T) -> Self { + also_self + } +} + +/// Unfortunately, we cannot easily derive padded representation for generic Std140 types. +/// For now, we'll just use this empty enum with no values. +#[derive(Copy, Clone)] +pub enum InvalidPadded {} +impl Std140Convertible for InvalidPadded { + fn into_std140(self) -> T { + unimplemented!() + } + fn from_std140(_: T) -> Self { + unimplemented!() + } +} /** Trait implemented for all types that can be turned into `std140` values. - +* This trait can often be `#[derive]`'d instead of manually implementing it. Any struct which contains only fields that also implement `AsStd140` can derive `AsStd140`. @@ -80,6 +120,9 @@ pub trait AsStd140 { fn std140_size_static() -> usize { size_of::() } + + /// Converts from `std140` version of self to self. + fn from_std140(val: Self::Std140Type) -> Self; } impl AsStd140 for T @@ -91,6 +134,75 @@ where fn as_std140(&self) -> Self { *self } + + fn from_std140(x: Self) -> Self { + x + } +} + +#[doc(hidden)] +#[derive(Copy, Clone, Debug)] +pub struct Std140Padded { + inner: T, + _padding: [u8; PAD], +} + +unsafe impl Zeroable for Std140Padded {} +unsafe impl Pod for Std140Padded {} + +impl Std140Convertible for Std140Padded { + fn into_std140(self) -> T { + self.inner + } + + fn from_std140(inner: T) -> Self { + Self { + inner, + _padding: [0u8; PAD], + } + } +} + +#[doc(hidden)] +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] +pub struct Std140Array([T::Padded; N]); + +unsafe impl Zeroable for Std140Array where T::Padded: Zeroable {} +unsafe impl Pod for Std140Array where T::Padded: Pod {} +unsafe impl Std140 for Std140Array +where + T::Padded: Pod, +{ + const ALIGNMENT: usize = crate::internal::max(T::ALIGNMENT, 16); + type Padded = Self; +} + +impl AsStd140 for [T; N] +where + ::Padded: Pod, +{ + type Std140Type = Std140Array; + fn as_std140(&self) -> Self::Std140Type { + let mut res: [MaybeUninit<::Padded>; N] = + unsafe { MaybeUninit::uninit().assume_init() }; + + for i in 0..N { + res[i] = MaybeUninit::new(Std140Convertible::from_std140(self[i].as_std140())); + } + + unsafe { core::mem::transmute_copy(&res) } + } + + fn from_std140(val: Self::Std140Type) -> Self { + let mut res: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + + for i in 0..N { + res[i] = MaybeUninit::new(AsStd140::from_std140(val.0[i].into_std140())); + } + + unsafe { core::mem::transmute_copy(&res) } + } } /// Trait implemented for all types that can be written into a buffer as @@ -101,6 +213,7 @@ where /// `Std140` trait, `WriteStd140` directly writes bytes using a [`Writer`]. This /// makes `WriteStd140` usable for writing slices or other DSTs that could not /// implement `AsStd140` without allocating new memory on the heap. +#[cfg(feature = "std")] pub trait WriteStd140 { /// Writes this value into the given [`Writer`] using `std140` layout rules. /// @@ -118,6 +231,7 @@ pub trait WriteStd140 { } } +#[cfg(feature = "std")] impl WriteStd140 for T where T: AsStd140, @@ -131,6 +245,7 @@ where } } +#[cfg(feature = "std")] impl WriteStd140 for [T] where T: WriteStd140, diff --git a/crates/crevice/src/std430.rs b/crates/crevice/src/std430.rs index 977a75c505..7e1af050ce 100644 --- a/crates/crevice/src/std430.rs +++ b/crates/crevice/src/std430.rs @@ -4,11 +4,13 @@ mod primitives; mod sizer; mod traits; +#[cfg(feature = "std")] mod writer; pub use self::primitives::*; pub use self::sizer::*; pub use self::traits::*; +#[cfg(feature = "std")] pub use self::writer::*; pub use crevice_derive::AsStd430; diff --git a/crates/crevice/src/std430/primitives.rs b/crates/crevice/src/std430/primitives.rs index 35ae651b7e..beb8b257c5 100644 --- a/crates/crevice/src/std430/primitives.rs +++ b/crates/crevice/src/std430/primitives.rs @@ -1,21 +1,28 @@ use bytemuck::{Pod, Zeroable}; -use crate::std430::Std430; +use crate::std430::{Std430, Std430Padded}; + +use crate::internal::align_offset; +use core::mem::size_of; unsafe impl Std430 for f32 { const ALIGNMENT: usize = 4; + type Padded = Self; } unsafe impl Std430 for f64 { const ALIGNMENT: usize = 8; + type Padded = Self; } unsafe impl Std430 for i32 { const ALIGNMENT: usize = 4; + type Padded = Self; } unsafe impl Std430 for u32 { const ALIGNMENT: usize = 4; + type Padded = Self; } macro_rules! vectors { @@ -37,6 +44,7 @@ macro_rules! vectors { unsafe impl Std430 for $name { const ALIGNMENT: usize = $align; + type Padded = Std430Padded(), $align)}>; } )+ }; @@ -87,6 +95,9 @@ macro_rules! matrices { unsafe impl Std430 for $name { const ALIGNMENT: usize = $align; + /// Matrices are technically arrays of primitives, and as such require pad at end. + const PAD_AT_END: bool = true; + type Padded = Std430Padded(), $align)}>; } )+ }; diff --git a/crates/crevice/src/std430/sizer.rs b/crates/crevice/src/std430/sizer.rs index c418ed93fd..139c69cb78 100644 --- a/crates/crevice/src/std430/sizer.rs +++ b/crates/crevice/src/std430/sizer.rs @@ -1,4 +1,4 @@ -use std::mem::size_of; +use core::mem::size_of; use crate::internal::align_offset; use crate::std430::{AsStd430, Std430}; diff --git a/crates/crevice/src/std430/traits.rs b/crates/crevice/src/std430/traits.rs index a7785bab01..e53c2003f0 100644 --- a/crates/crevice/src/std430/traits.rs +++ b/crates/crevice/src/std430/traits.rs @@ -1,8 +1,10 @@ +use core::mem::{size_of, MaybeUninit}; +#[cfg(feature = "std")] use std::io::{self, Write}; -use std::mem::size_of; use bytemuck::{bytes_of, Pod, Zeroable}; +#[cfg(feature = "std")] use crate::std430::Writer; /// Trait implemented for all `std430` primitives. Generally should not be @@ -15,6 +17,15 @@ pub unsafe trait Std430: Copy + Zeroable + Pod { /// control and zero their padding bytes, making converting them to and from /// slices safe. const ALIGNMENT: usize; + /// Whether this type requires a padding at the end (ie, is a struct or an array + /// of primitives). + /// See https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159 + /// (rule 4 and 9) + const PAD_AT_END: bool = false; + /// Padded type (Std430Padded specialization) + /// The usual implementation is + /// type Padded = Std430Padded(), ALIGNMENT)}>; + type Padded: Std430Convertible; /// Casts the type to a byte array. Implementors should not override this /// method. @@ -27,6 +38,35 @@ pub unsafe trait Std430: Copy + Zeroable + Pod { } } +/// Trait specifically for Std430::Padded, implements conversions between padded type and base type. +pub trait Std430Convertible: Copy { + /// Convert from self to Std430 + fn into_std430(self) -> T; + /// Convert from Std430 to self + fn from_std430(_: T) -> Self; +} + +impl Std430Convertible for T { + fn into_std430(self) -> T { + self + } + fn from_std430(also_self: T) -> Self { + also_self + } +} + +/// Unfortunately, we cannot easily derive padded representation for generic Std140 types. +/// For now, we'll just use this empty enum with no values. +#[derive(Copy, Clone)] +pub enum InvalidPadded {} +impl Std430Convertible for InvalidPadded { + fn into_std430(self) -> T { + unimplemented!() + } + fn from_std430(_: T) -> Self { + unimplemented!() + } +} /** Trait implemented for all types that can be turned into `std430` values. @@ -80,6 +120,9 @@ pub trait AsStd430 { fn std430_size_static() -> usize { size_of::() } + + /// Converts from `std430` version of self to self. + fn from_std430(value: Self::Std430Type) -> Self; } impl AsStd430 for T @@ -91,6 +134,75 @@ where fn as_std430(&self) -> Self { *self } + + fn from_std430(value: Self) -> Self { + value + } +} + +#[doc(hidden)] +#[derive(Copy, Clone, Debug)] +pub struct Std430Padded { + inner: T, + _padding: [u8; PAD], +} + +unsafe impl Zeroable for Std430Padded {} +unsafe impl Pod for Std430Padded {} + +impl Std430Convertible for Std430Padded { + fn into_std430(self) -> T { + self.inner + } + + fn from_std430(inner: T) -> Self { + Self { + inner, + _padding: [0u8; PAD], + } + } +} + +#[doc(hidden)] +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] +pub struct Std430Array([T::Padded; N]); + +unsafe impl Zeroable for Std430Array where T::Padded: Zeroable {} +unsafe impl Pod for Std430Array where T::Padded: Pod {} +unsafe impl Std430 for Std430Array +where + T::Padded: Pod, +{ + const ALIGNMENT: usize = T::ALIGNMENT; + type Padded = Self; +} + +impl AsStd430 for [T; N] +where + ::Padded: Pod, +{ + type Std430Type = Std430Array; + fn as_std430(&self) -> Self::Std430Type { + let mut res: [MaybeUninit<::Padded>; N] = + unsafe { MaybeUninit::uninit().assume_init() }; + + for i in 0..N { + res[i] = MaybeUninit::new(Std430Convertible::from_std430(self[i].as_std430())); + } + + unsafe { core::mem::transmute_copy(&res) } + } + + fn from_std430(val: Self::Std430Type) -> Self { + let mut res: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + + for i in 0..N { + res[i] = MaybeUninit::new(AsStd430::from_std430(val.0[i].into_std430())); + } + + unsafe { core::mem::transmute_copy(&res) } + } } /// Trait implemented for all types that can be written into a buffer as @@ -101,6 +213,7 @@ where /// `Std430` trait, `WriteStd430` directly writes bytes using a [`Writer`]. This /// makes `WriteStd430` usable for writing slices or other DSTs that could not /// implement `AsStd430` without allocating new memory on the heap. +#[cfg(feature = "std")] pub trait WriteStd430 { /// Writes this value into the given [`Writer`] using `std430` layout rules. /// @@ -118,6 +231,7 @@ pub trait WriteStd430 { } } +#[cfg(feature = "std")] impl WriteStd430 for T where T: AsStd430, @@ -131,6 +245,7 @@ where } } +#[cfg(feature = "std")] impl WriteStd430 for [T] where T: WriteStd430, diff --git a/crates/crevice/tests/snapshots/std140__more_than_16_alignment.snap b/crates/crevice/tests/snapshots/std140__more_than_16_alignment.snap deleted file mode 100644 index 99f9a97448..0000000000 --- a/crates/crevice/tests/snapshots/std140__more_than_16_alignment.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: tests/std140.rs -expression: "<::Std140Type as TypeLayout>::type_layout()" ---- -name: Std140MoreThan16Alignment -size: 32 -alignment: 8 -fields: - - Field: - name: _doubles_align - ty: "[u8 ; Std140MoreThan16AlignmentAlignment :: _doubles_align()]" - size: 0 - - Field: - name: doubles - ty: "< DVec4 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 32 diff --git a/crates/crevice/tests/snapshots/std140__point_light.snap b/crates/crevice/tests/snapshots/std140__point_light.snap deleted file mode 100644 index d40a1d1b56..0000000000 --- a/crates/crevice/tests/snapshots/std140__point_light.snap +++ /dev/null @@ -1,40 +0,0 @@ ---- -source: tests/std140.rs -expression: "<::Std140Type as TypeLayout>::type_layout()" ---- -name: Std140PointLight -size: 48 -alignment: 4 -fields: - - Field: - name: _position_align - ty: "[u8 ; Std140PointLightAlignment :: _position_align()]" - size: 0 - - Field: - name: position - ty: "< Vec3 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 12 - - Field: - name: _diffuse_align - ty: "[u8 ; Std140PointLightAlignment :: _diffuse_align()]" - size: 4 - - Field: - name: diffuse - ty: "< Vec3 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 12 - - Field: - name: _specular_align - ty: "[u8 ; Std140PointLightAlignment :: _specular_align()]" - size: 4 - - Field: - name: specular - ty: "< Vec3 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 12 - - Field: - name: _brightness_align - ty: "[u8 ; Std140PointLightAlignment :: _brightness_align()]" - size: 0 - - Field: - name: brightness - ty: "< f32 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 4 diff --git a/crates/crevice/tests/snapshots/std140__primitive_f32.snap b/crates/crevice/tests/snapshots/std140__primitive_f32.snap deleted file mode 100644 index dc3b93d4eb..0000000000 --- a/crates/crevice/tests/snapshots/std140__primitive_f32.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: tests/std140.rs -expression: "<::Std140Type as TypeLayout>::layout()" ---- -name: Std140PrimitiveF32 -size: 8 -alignment: 4 -fields: - - Field: - name: _x_align - ty: "[u8 ; Std140PrimitiveF32Alignment :: _x_align()]" - size: 0 - - Field: - name: x - ty: "< f32 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 4 - - Field: - name: _y_align - ty: "[u8 ; Std140PrimitiveF32Alignment :: _y_align()]" - size: 0 - - Field: - name: y - ty: "< f32 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 4 diff --git a/crates/crevice/tests/snapshots/std140__using_vec3_padding.snap b/crates/crevice/tests/snapshots/std140__using_vec3_padding.snap deleted file mode 100644 index 598e6d010a..0000000000 --- a/crates/crevice/tests/snapshots/std140__using_vec3_padding.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: tests/std140.rs -expression: "<::Std140Type as TypeLayout>::layout()" ---- -name: Std140UsingVec3Padding -size: 16 -alignment: 4 -fields: - - Field: - name: _pos_align - ty: "[u8 ; Std140UsingVec3PaddingAlignment :: _pos_align()]" - size: 0 - - Field: - name: pos - ty: "< Vec3 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 12 - - Field: - name: _brightness_align - ty: "[u8 ; Std140UsingVec3PaddingAlignment :: _brightness_align()]" - size: 0 - - Field: - name: brightness - ty: "< f32 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 4 diff --git a/crates/crevice/tests/snapshots/std140__vec3.snap b/crates/crevice/tests/snapshots/std140__vec3.snap deleted file mode 100644 index fbf99885f7..0000000000 --- a/crates/crevice/tests/snapshots/std140__vec3.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: tests/std140.rs -expression: "<::Std140Type as TypeLayout>::layout()" ---- -name: Std140TestVec3 -size: 28 -alignment: 4 -fields: - - Field: - name: _pos_align - ty: "[u8 ; Std140TestVec3Alignment :: _pos_align()]" - size: 0 - - Field: - name: pos - ty: "< Vec3 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 12 - - Field: - name: _velocity_align - ty: "[u8 ; Std140TestVec3Alignment :: _velocity_align()]" - size: 4 - - Field: - name: velocity - ty: "< Vec3 as :: crevice :: std140 :: AsStd140 > :: Std140Type" - size: 12 diff --git a/crates/crevice/tests/std140.rs b/crates/crevice/tests/std140.rs deleted file mode 100644 index 112849acd8..0000000000 --- a/crates/crevice/tests/std140.rs +++ /dev/null @@ -1,121 +0,0 @@ -use insta::assert_yaml_snapshot; -use type_layout::TypeLayout; - -use crevice::std140::{AsStd140, DVec4, Std140, Vec3}; - -#[derive(AsStd140)] -struct PrimitiveF32 { - x: f32, - y: f32, -} - -#[test] -fn primitive_f32() { - assert_yaml_snapshot!(<::Std140Type as TypeLayout>::type_layout()); - - assert_eq!(::Std140Type::ALIGNMENT, 16); - - let value = PrimitiveF32 { x: 1.0, y: 2.0 }; - let _value_std140 = value.as_std140(); -} - -#[derive(AsStd140)] -struct TestVec3 { - pos: Vec3, - velocity: Vec3, -} - -#[test] -fn test_vec3() { - assert_yaml_snapshot!(<::Std140Type as TypeLayout>::type_layout()); - - assert_eq!(::Std140Type::ALIGNMENT, 16); - - let value = TestVec3 { - pos: Vec3 { - x: 1.0, - y: 2.0, - z: 3.0, - }, - velocity: Vec3 { - x: 4.0, - y: 5.0, - z: 6.0, - }, - }; - let _value_std140 = value.as_std140(); -} - -#[derive(AsStd140)] -struct UsingVec3Padding { - pos: Vec3, - brightness: f32, -} - -#[test] -fn using_vec3_padding() { - assert_yaml_snapshot!( - <::Std140Type as TypeLayout>::type_layout() - ); - - assert_eq!(::Std140Type::ALIGNMENT, 16); - - let value = UsingVec3Padding { - pos: Vec3 { - x: 1.0, - y: 2.0, - z: 3.0, - }, - brightness: 4.0, - }; - let _value_std140 = value.as_std140(); -} - -#[derive(AsStd140)] -struct PointLight { - position: Vec3, - diffuse: Vec3, - specular: Vec3, - brightness: f32, -} - -#[test] -fn point_light() { - assert_yaml_snapshot!(<::Std140Type as TypeLayout>::type_layout()); - - assert_eq!(::Std140Type::ALIGNMENT, 16); - - let value = PointLight { - position: Vec3 { - x: 1.0, - y: 2.0, - z: 3.0, - }, - diffuse: Vec3 { - x: 1.0, - y: 2.0, - z: 3.0, - }, - specular: Vec3 { - x: 1.0, - y: 2.0, - z: 3.0, - }, - brightness: 4.0, - }; - let _value_std140 = value.as_std140(); -} - -#[derive(AsStd140)] -struct MoreThan16Alignment { - doubles: DVec4, -} - -#[test] -fn more_than_16_alignment() { - assert_yaml_snapshot!( - <::Std140Type as TypeLayout>::type_layout() - ); - - assert_eq!(::Std140Type::ALIGNMENT, 32); -} diff --git a/examples/3d/3d_scene_pipelined.rs b/examples/3d/3d_scene_pipelined.rs new file mode 100644 index 0000000000..435cdc88ed --- /dev/null +++ b/examples/3d/3d_scene_pipelined.rs @@ -0,0 +1,95 @@ +use bevy::{ + core::Time, + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + ecs::prelude::*, + input::Input, + math::Vec3, + pbr2::{PbrBundle, PointLightBundle, StandardMaterial}, + prelude::{App, Assets, KeyCode, Transform}, + render2::{ + camera::PerspectiveCameraBundle, + color::Color, + mesh::{shape, Mesh}, + }, + wgpu2::diagnostic::WgpuResourceDiagnosticsPlugin, + PipelinedDefaultPlugins, +}; + +fn main() { + App::new() + .add_plugins(PipelinedDefaultPlugins) + .add_plugin(FrameTimeDiagnosticsPlugin::default()) + .add_plugin(LogDiagnosticsPlugin::default()) + .add_plugin(WgpuResourceDiagnosticsPlugin::default()) + .add_startup_system(setup.system()) + .add_system(movement.system()) + .run(); +} + +struct Movable; + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // plane + commands.spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + ..Default::default() + }); + // cube + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + transform: Transform::from_xyz(0.0, 1.0, 0.0), + ..Default::default() + }) + .insert(Movable); + // sphere + commands + .spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::UVSphere { radius: 0.5, ..Default::default() })), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + transform: Transform::from_xyz(1.5, 1.0, 1.5), + ..Default::default() + }) + .insert(Movable); + // light + commands.spawn_bundle(PointLightBundle { + transform: Transform::from_xyz(5.0, 8.0, 2.0), + ..Default::default() + }); + // camera + commands.spawn_bundle(PerspectiveCameraBundle { + transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + ..Default::default() + }); +} + +fn movement( + input: Res>, + time: Res