pipelined rendering proof of concept

This commit is contained in:
Carter Anderson 2021-04-11 13:13:07 -07:00
parent a4e5e2790e
commit 4ac2ed7cc6
250 changed files with 16397 additions and 757 deletions

View file

@ -17,7 +17,7 @@ repository = "https://github.com/bevyengine/bevy"
[workspace]
exclude = ["benches"]
members = ["crates/*", "examples/ios", "tools/ci"]
members = ["crates/*", "pipelined/*", "examples/ios", "tools/ci"]
[features]
default = [
@ -26,6 +26,9 @@ default = [
"bevy_gilrs",
"bevy_gltf",
"bevy_wgpu",
"bevy_wgpu2",
"bevy_sprite2",
"bevy_render2",
"bevy_winit",
"render",
"png",
@ -48,6 +51,10 @@ bevy_gltf = ["bevy_internal/bevy_gltf"]
bevy_wgpu = ["bevy_internal/bevy_wgpu"]
bevy_winit = ["bevy_internal/bevy_winit"]
bevy_wgpu2 = ["bevy_internal/bevy_wgpu2"]
bevy_render2 = ["bevy_internal/bevy_render2"]
bevy_sprite2 = ["bevy_internal/bevy_sprite2"]
trace_chrome = ["bevy_internal/trace_chrome"]
trace = ["bevy_internal/trace"]
wgpu_trace = ["bevy_internal/wgpu_trace"]
@ -419,6 +426,10 @@ path = "examples/shader/shader_defs.rs"
name = "bevymark"
path = "examples/tools/bevymark.rs"
[[example]]
name = "bevymark_pipelined"
path = "examples/tools/bevymark_pipelined.rs"
# UI (User Interface)
[[example]]
name = "button"

View file

@ -1,10 +1,19 @@
use crate::app_builder::AppBuilder;
use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage};
use bevy_ecs::{
schedule::{Schedule, Stage},
component::{Component, ComponentDescriptor},
prelude::{FromWorld, IntoExclusiveSystem, IntoSystem},
schedule::{
RunOnce, Schedule, Stage, StageLabel, State, SystemDescriptor, SystemSet, SystemStage,
},
world::World,
};
use bevy_utils::tracing::debug;
use std::{fmt::Debug, hash::Hash};
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;
use std::fmt::Debug;
use std::hash::Hash;
#[allow(clippy::needless_doctest_main)]
/// Containers of app logic and data
@ -20,7 +29,7 @@ use bevy_utils::tracing::info_span;
/// # use bevy_ecs::prelude::*;
///
/// fn main() {
/// App::build()
/// App::new()
/// .add_system(hello_world_system.system())
/// .run();
/// }
@ -33,26 +42,46 @@ pub struct App {
pub world: World,
pub runner: Box<dyn Fn(App)>,
pub schedule: Schedule,
sub_apps: Vec<SubApp>,
}
struct SubApp {
app: App,
runner: Box<dyn Fn(&mut World, &mut App)>,
}
impl Default for App {
fn default() -> Self {
let mut app = App::empty();
#[cfg(feature = "bevy_reflect")]
app.init_resource::<bevy_reflect::TypeRegistryArc>();
app.add_default_stages()
.add_event::<AppExit>()
.add_system_to_stage(CoreStage::Last, World::clear_trackers.exclusive_system());
#[cfg(feature = "bevy_ci_testing")]
{
crate::ci_testing::setup_app(&mut app);
}
app
}
}
impl App {
pub fn new() -> App {
App::default()
}
pub fn empty() -> App {
Self {
world: Default::default(),
schedule: Default::default(),
runner: Box::new(run_once),
sub_apps: Vec::new(),
}
}
}
fn run_once(mut app: App) {
app.update();
}
impl App {
pub fn build() -> AppBuilder {
AppBuilder::default()
}
pub fn update(&mut self) {
#[cfg(feature = "trace")]
@ -60,17 +89,528 @@ impl App {
#[cfg(feature = "trace")]
let _bevy_frame_update_guard = bevy_frame_update_span.enter();
self.schedule.run(&mut self.world);
for sub_app in self.sub_apps.iter_mut() {
(sub_app.runner)(&mut self.world, &mut sub_app.app);
}
}
pub fn run(mut self) {
/// Start the application (through main runner)
///
/// Runs the application main loop.
///
/// Usually the main loop is handled by Bevy integrated plugins (`winit`), but
/// but one can also set the runner function through [`App::set_runner`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// App::new()
/// // all required plugin insertions, systems, etc inserted here
/// // finally, call:
/// .run();
/// ```
pub fn run(&mut self) {
#[cfg(feature = "trace")]
let bevy_app_run_span = info_span!("bevy_app");
#[cfg(feature = "trace")]
let _bevy_app_run_guard = bevy_app_run_span.enter();
let runner = std::mem::replace(&mut self.runner, Box::new(run_once));
(runner)(self);
let mut app = std::mem::replace(self, App::empty());
let runner = std::mem::replace(&mut app.runner, Box::new(run_once));
(runner)(app);
}
pub fn add_stage<S: Stage>(&mut self, label: impl StageLabel, stage: S) -> &mut Self {
self.schedule.add_stage(label, stage);
self
}
pub fn add_stage_after<S: Stage>(
&mut self,
target: impl StageLabel,
label: impl StageLabel,
stage: S,
) -> &mut Self {
self.schedule.add_stage_after(target, label, stage);
self
}
pub fn add_stage_before<S: Stage>(
&mut self,
target: impl StageLabel,
label: impl StageLabel,
stage: S,
) -> &mut Self {
self.schedule.add_stage_before(target, label, stage);
self
}
pub fn add_startup_stage<S: Stage>(&mut self, label: impl StageLabel, stage: S) -> &mut Self {
self.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_stage(label, stage)
});
self
}
pub fn add_startup_stage_after<S: Stage>(
&mut self,
target: impl StageLabel,
label: impl StageLabel,
stage: S,
) -> &mut Self {
self.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_stage_after(target, label, stage)
});
self
}
pub fn add_startup_stage_before<S: Stage>(
&mut self,
target: impl StageLabel,
label: impl StageLabel,
stage: S,
) -> &mut Self {
self.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_stage_before(target, label, stage)
});
self
}
pub fn stage<T: Stage, F: FnOnce(&mut T) -> &mut T>(
&mut self,
label: impl StageLabel,
func: F,
) -> &mut Self {
self.schedule.stage(label, func);
self
}
/// Adds a system that runs every time `app.update()` is called by the runner
///
/// Systems are the main building block in the Bevy ECS app model. You can define
/// normal rust functions, and call `.system()` to make them be Bevy systems.
///
/// System functions can have parameters, through which one can query and
/// mutate Bevy ECS states.
/// See [The Bevy Book](https://bevyengine.org/learn/book/introduction/) for more information.
///
/// Systems are run in parallel, and the execution order is not deterministic.
/// If you want more fine-grained control for order, see [`App::add_system_to_stage`].
///
/// For adding a system that runs only at app startup, see [`App::add_startup_system`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// fn my_system(_commands: Commands) {
/// println!("My system, triggered once per frame");
/// }
///
/// App::new()
/// .add_system(my_system.system());
/// ```
pub fn add_system<Params>(&mut self, system: impl IntoSystemDescriptor<Params>) -> &mut Self {
self.add_system_to_stage(CoreStage::Update, system)
}
pub fn add_system_set(&mut self, system_set: SystemSet) -> &mut Self {
self.add_system_set_to_stage(CoreStage::Update, system_set)
}
pub fn add_system_to_stage<Params>(
&mut self,
stage_label: impl StageLabel,
system: impl IntoSystemDescriptor<Params>,
) -> &mut Self {
self.schedule.add_system_to_stage(stage_label, system);
self
}
pub fn add_system_set_to_stage(
&mut self,
stage_label: impl StageLabel,
system_set: SystemSet,
) -> &mut Self {
self.schedule
.add_system_set_to_stage(stage_label, system_set);
self
}
/// Adds a system that is run once at application startup
///
/// Startup systems run exactly once BEFORE all other systems. These are generally used for
/// app initialization code (ex: adding entities and resources).
///
/// * For adding a system that runs for every frame, see [`App::add_system`].
/// * For adding a system to specific stage, see [`App::add_system_to_stage`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// fn my_startup_system(_commands: Commands) {
/// println!("My startup system");
/// }
///
/// App::new()
/// .add_startup_system(my_startup_system.system());
/// ```
pub fn add_startup_system<Params>(
&mut self,
system: impl IntoSystemDescriptor<Params>,
) -> &mut Self {
self.add_startup_system_to_stage(StartupStage::Startup, system)
}
pub fn add_startup_system_set(&mut self, system_set: SystemSet) -> &mut Self {
self.add_startup_system_set_to_stage(StartupStage::Startup, system_set)
}
pub fn add_startup_system_to_stage<Params>(
&mut self,
stage_label: impl StageLabel,
system: impl IntoSystemDescriptor<Params>,
) -> &mut Self {
self.app
.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_system_to_stage(stage_label, system)
});
self
}
pub fn add_startup_system_set_to_stage(
&mut self,
stage_label: impl StageLabel,
system_set: SystemSet,
) -> &mut Self {
self.app
.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_system_set_to_stage(stage_label, system_set)
});
self
}
/// Adds a new [State] with the given `initial` value.
/// This inserts a new `State<T>` resource and adds a new "driver" to [CoreStage::Update].
/// Each stage that uses `State<T>` for system run criteria needs a driver. If you need to use
/// your state in a different stage, consider using [Self::add_state_to_stage] or manually
/// adding [State::get_driver] to additional stages you need it in.
pub fn add_state<T>(&mut self, initial: T) -> &mut Self
where
T: Component + Debug + Clone + Eq + Hash,
{
self.add_state_to_stage(CoreStage::Update, initial)
}
/// Adds a new [State] with the given `initial` value.
/// This inserts a new `State<T>` resource and adds a new "driver" to the given stage.
/// Each stage that uses `State<T>` for system run criteria needs a driver. If you need to use
/// your state in more than one stage, consider manually adding [State::get_driver] to the
/// stages you need it in.
pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self
where
T: Component + Debug + Clone + Eq + Hash,
{
self.insert_resource(State::new(initial))
.add_system_set_to_stage(stage, State::<T>::get_driver())
}
pub fn add_default_stages(&mut self) -> &mut Self {
self.add_stage(CoreStage::First, SystemStage::parallel())
.add_stage(
CoreStage::Startup,
Schedule::default()
.with_run_criteria(RunOnce::default())
.with_stage(StartupStage::PreStartup, SystemStage::parallel())
.with_stage(StartupStage::Startup, SystemStage::parallel())
.with_stage(StartupStage::PostStartup, SystemStage::parallel()),
)
.add_stage(CoreStage::PreUpdate, SystemStage::parallel())
.add_stage(CoreStage::Update, SystemStage::parallel())
.add_stage(CoreStage::PostUpdate, SystemStage::parallel())
.add_stage(CoreStage::Last, SystemStage::parallel())
}
/// Setup the application to manage events of type `T`.
///
/// This is done by adding a `Resource` of type `Events::<T>`,
/// and inserting a `Events::<T>::update_system` system into `CoreStage::First`.
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Component,
{
self.insert_resource(Events::<T>::default())
.add_system_to_stage(CoreStage::First, Events::<T>::update_system.system())
}
/// Inserts a resource to the current [App] and overwrites any resource previously added of the same type.
///
/// A resource in Bevy represents globally unique data. Resources must be added to Bevy Apps
/// before using them. This happens with [`App::insert_resource`].
///
/// See also `init_resource` for resources that implement `Default` or [`FromResources`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// struct MyCounter {
/// counter: usize,
/// }
///
/// App::new()
/// .insert_resource(MyCounter { counter: 0 });
/// ```
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
where
T: Component,
{
self.world.insert_resource(resource);
self
}
/// Inserts a non-send resource to the app
///
/// You usually want to use `insert_resource`, but there are some special cases when a resource must
/// be non-send.
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// struct MyCounter {
/// counter: usize,
/// }
///
/// App::new()
/// .insert_non_send_resource(MyCounter { counter: 0 });
/// ```
pub fn insert_non_send_resource<T>(&mut self, resource: T) -> &mut Self
where
T: 'static,
{
self.world.insert_non_send(resource);
self
}
/// Initialize a resource in the current [App], if it does not exist yet
///
/// Adds a resource that implements `Default` or [`FromResources`] trait.
/// If the resource already exists, `init_resource` does nothing.
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// struct MyCounter {
/// counter: usize,
/// }
///
/// impl Default for MyCounter {
/// fn default() -> MyCounter {
/// MyCounter {
/// counter: 100
/// }
/// }
/// }
///
/// App::new()
/// .init_resource::<MyCounter>();
/// ```
pub fn init_resource<R>(&mut self) -> &mut Self
where
R: FromWorld + Send + Sync + 'static,
{
// PERF: We could avoid double hashing here, since the `from_resources` call is guaranteed
// not to modify the map. However, we would need to be borrowing resources both
// mutably and immutably, so we would need to be extremely certain this is correct
if !self.world.contains_resource::<R>() {
let resource = R::from_world(&mut self.world);
self.insert_resource(resource);
}
self
}
pub fn init_non_send_resource<R>(&mut self) -> &mut Self
where
R: FromWorld + 'static,
{
// See perf comment in init_resource
if self.world.get_non_send_resource::<R>().is_none() {
let resource = R::from_world(&mut self.world);
self.world.insert_non_send(resource);
}
self
}
/// Sets the main runner loop function for this Bevy App
///
/// Usually the main loop is handled by Bevy integrated plugins ([`WinitPlugin`]), but
/// in some cases one might wish to implement their own main loop.
///
/// This method sets the main loop function, overwriting a previous runner if any.
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// fn my_runner(mut app: App) {
/// loop {
/// println!("In main loop");
/// app.update();
/// }
/// }
///
/// App::new()
/// .set_runner(my_runner);
/// ```
pub fn set_runner(&mut self, run_fn: impl Fn(App) + 'static) -> &mut Self {
self.runner = Box::new(run_fn);
self
}
/// Adds a single plugin
///
/// One of Bevy's core principles is modularity. All Bevy engine features are implemented
/// as plugins. This includes internal features like the renderer.
///
/// Bevy also provides a few sets of default plugins. See [`App::add_plugins`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// App::new().add_plugin(bevy_log::LogPlugin::default());
/// ```
pub fn add_plugin<T>(&mut self, plugin: T) -> &mut Self
where
T: Plugin,
{
debug!("added plugin: {}", plugin.name());
plugin.build(self);
self
}
/// Adds a group of plugins
///
/// Bevy plugins can be grouped into a set of plugins. Bevy provides
/// built-in PluginGroups that provide core engine functionality.
///
/// The plugin groups available by default are [`DefaultPlugins`] and [`MinimalPlugins`].
///
/// ## Example
/// ```
/// # use bevy_app::{prelude::*, PluginGroupBuilder};
/// #
/// # // Dummy created to avoid using bevy_internal, which pulls in to many dependencies.
/// # struct MinimalPlugins;
/// # impl PluginGroup for MinimalPlugins {
/// # fn build(&mut self, group: &mut PluginGroupBuilder){;}
/// # }
/// #
/// App::new()
/// .add_plugins(MinimalPlugins);
/// ```
pub fn add_plugins<T: PluginGroup>(&mut self, mut group: T) -> &mut Self {
let mut plugin_group_builder = PluginGroupBuilder::default();
group.build(&mut plugin_group_builder);
plugin_group_builder.finish(self);
self
}
/// Adds a group of plugins with an initializer method
///
/// Can be used to add a group of plugins, where the group is modified
/// before insertion into Bevy application. For example, you can add
/// extra plugins at a specific place in the plugin group, or deactivate
/// specific plugins while keeping the rest.
///
/// ## Example
/// ```
/// # use bevy_app::{prelude::*, PluginGroupBuilder};
/// #
/// # // Dummies created to avoid using bevy_internal which pulls in to many dependencies.
/// # struct DefaultPlugins;
/// # impl PluginGroup for DefaultPlugins {
/// # fn build(&mut self, group: &mut PluginGroupBuilder){
/// # group.add(bevy_log::LogPlugin::default());
/// # }
/// # }
/// #
/// # struct MyOwnPlugin;
/// # impl Plugin for MyOwnPlugin {
/// # fn build(&self, app: &mut AppBuilder){;}
/// # }
/// #
/// App::new()
/// .add_plugins_with(DefaultPlugins, |group| {
/// group.add_before::<bevy_log::LogPlugin, _>(MyOwnPlugin)
/// });
/// ```
pub fn add_plugins_with<T, F>(&mut self, mut group: T, func: F) -> &mut Self
where
T: PluginGroup,
F: FnOnce(&mut PluginGroupBuilder) -> &mut PluginGroupBuilder,
{
let mut plugin_group_builder = PluginGroupBuilder::default();
group.build(&mut plugin_group_builder);
func(&mut plugin_group_builder);
plugin_group_builder.finish(self);
self
}
/// Registers a new component using the given [ComponentDescriptor]. Components do not need to
/// be manually registered. This just provides a way to override default configuration.
/// Attempting to register a component with a type that has already been used by [World]
/// will result in an error.
///
/// See [World::register_component]
pub fn register_component(&mut self, descriptor: ComponentDescriptor) -> &mut Self {
self.world.register_component(descriptor).unwrap();
self
}
#[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
{
let registry = self
.world
.get_resource_mut::<bevy_reflect::TypeRegistryArc>()
.unwrap();
registry.write().register::<T>();
}
self
}
pub fn add_sub_app(
&mut self,
app: App,
f: impl Fn(&mut World, &mut App) + 'static,
) -> &mut Self {
self.sub_apps.push(SubApp {
app,
runner: Box::new(f),
});
self
}
// TODO: use labels instead of indices
pub fn sub_app_mut(&mut self, index: usize) -> &mut App {
&mut self.sub_apps[index].app
}
}
fn run_once(mut app: App) {
app.update();
}
/// An event that indicates the app should exit. This will fully exit the app process.

View file

@ -1,559 +0,0 @@
use crate::{
app::{App, AppExit},
plugin::Plugin,
CoreStage, PluginGroup, PluginGroupBuilder, StartupStage,
};
use bevy_ecs::{
component::{Component, ComponentDescriptor},
event::Events,
schedule::{
IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage,
},
system::{IntoExclusiveSystem, IntoSystem},
world::{FromWorld, World},
};
use bevy_utils::tracing::debug;
use std::{fmt::Debug, hash::Hash};
/// Configure [App]s using the builder pattern
pub struct AppBuilder {
pub app: App,
}
impl Default for AppBuilder {
fn default() -> Self {
let mut app_builder = AppBuilder {
app: App::default(),
};
#[cfg(feature = "bevy_reflect")]
app_builder.init_resource::<bevy_reflect::TypeRegistryArc>();
app_builder
.add_default_stages()
.add_event::<AppExit>()
.add_system_to_stage(CoreStage::Last, World::clear_trackers.exclusive_system());
#[cfg(feature = "bevy_ci_testing")]
{
crate::ci_testing::setup_app(&mut app_builder);
}
app_builder
}
}
impl AppBuilder {
pub fn empty() -> AppBuilder {
AppBuilder {
app: App::default(),
}
}
/// Start the application (through main runner)
///
/// Runs the application main loop.
///
/// Usually the main loop is handled by Bevy integrated plugins (`winit`), but
/// but one can also set the runner function through [`AppBuilder::set_runner`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// App::build()
/// // all required plugin insertions, systems, etc inserted here
/// // finally, call:
/// .run();
/// ```
pub fn run(&mut self) {
let app = std::mem::take(&mut self.app);
app.run();
}
pub fn world(&mut self) -> &World {
&self.app.world
}
pub fn world_mut(&mut self) -> &mut World {
&mut self.app.world
}
pub fn set_world(&mut self, world: World) -> &mut Self {
self.app.world = world;
self
}
pub fn add_stage<S: Stage>(&mut self, label: impl StageLabel, stage: S) -> &mut Self {
self.app.schedule.add_stage(label, stage);
self
}
pub fn add_stage_after<S: Stage>(
&mut self,
target: impl StageLabel,
label: impl StageLabel,
stage: S,
) -> &mut Self {
self.app.schedule.add_stage_after(target, label, stage);
self
}
pub fn add_stage_before<S: Stage>(
&mut self,
target: impl StageLabel,
label: impl StageLabel,
stage: S,
) -> &mut Self {
self.app.schedule.add_stage_before(target, label, stage);
self
}
pub fn add_startup_stage<S: Stage>(&mut self, label: impl StageLabel, stage: S) -> &mut Self {
self.app
.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_stage(label, stage)
});
self
}
pub fn add_startup_stage_after<S: Stage>(
&mut self,
target: impl StageLabel,
label: impl StageLabel,
stage: S,
) -> &mut Self {
self.app
.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_stage_after(target, label, stage)
});
self
}
pub fn add_startup_stage_before<S: Stage>(
&mut self,
target: impl StageLabel,
label: impl StageLabel,
stage: S,
) -> &mut Self {
self.app
.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_stage_before(target, label, stage)
});
self
}
pub fn stage<T: Stage, F: FnOnce(&mut T) -> &mut T>(
&mut self,
label: impl StageLabel,
func: F,
) -> &mut Self {
self.app.schedule.stage(label, func);
self
}
/// Adds a system that runs every time `app.update()` is called by the runner
///
/// Systems are the main building block in the Bevy ECS app model. You can define
/// normal rust functions, and call `.system()` to make them be Bevy systems.
///
/// System functions can have parameters, through which one can query and
/// mutate Bevy ECS states.
/// See [The Bevy Book](https://bevyengine.org/learn/book/introduction/) for more information.
///
/// Systems are run in parallel, and the execution order is not deterministic.
/// If you want more fine-grained control for order, see [`AppBuilder::add_system_to_stage`].
///
/// For adding a system that runs only at app startup, see [`AppBuilder::add_startup_system`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// fn my_system(_commands: Commands) {
/// println!("My system, triggered once per frame");
/// }
///
/// App::build()
/// .add_system(my_system.system());
/// ```
pub fn add_system<Params>(&mut self, system: impl IntoSystemDescriptor<Params>) -> &mut Self {
self.add_system_to_stage(CoreStage::Update, system)
}
pub fn add_system_set(&mut self, system_set: SystemSet) -> &mut Self {
self.add_system_set_to_stage(CoreStage::Update, system_set)
}
pub fn add_system_to_stage<Params>(
&mut self,
stage_label: impl StageLabel,
system: impl IntoSystemDescriptor<Params>,
) -> &mut Self {
self.app.schedule.add_system_to_stage(stage_label, system);
self
}
pub fn add_system_set_to_stage(
&mut self,
stage_label: impl StageLabel,
system_set: SystemSet,
) -> &mut Self {
self.app
.schedule
.add_system_set_to_stage(stage_label, system_set);
self
}
/// Adds a system that is run once at application startup
///
/// Startup systems run exactly once BEFORE all other systems. These are generally used for
/// app initialization code (ex: adding entities and resources).
///
/// * For adding a system that runs for every frame, see [`AppBuilder::add_system`].
/// * For adding a system to specific stage, see [`AppBuilder::add_system_to_stage`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// fn my_startup_system(_commands: Commands) {
/// println!("My startup system");
/// }
///
/// App::build()
/// .add_startup_system(my_startup_system.system());
/// ```
pub fn add_startup_system<Params>(
&mut self,
system: impl IntoSystemDescriptor<Params>,
) -> &mut Self {
self.add_startup_system_to_stage(StartupStage::Startup, system)
}
pub fn add_startup_system_set(&mut self, system_set: SystemSet) -> &mut Self {
self.add_startup_system_set_to_stage(StartupStage::Startup, system_set)
}
pub fn add_startup_system_to_stage<Params>(
&mut self,
stage_label: impl StageLabel,
system: impl IntoSystemDescriptor<Params>,
) -> &mut Self {
self.app
.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_system_to_stage(stage_label, system)
});
self
}
pub fn add_startup_system_set_to_stage(
&mut self,
stage_label: impl StageLabel,
system_set: SystemSet,
) -> &mut Self {
self.app
.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.add_system_set_to_stage(stage_label, system_set)
});
self
}
/// Adds a new [State] with the given `initial` value.
/// This inserts a new `State<T>` resource and adds a new "driver" to [CoreStage::Update].
/// Each stage that uses `State<T>` for system run criteria needs a driver. If you need to use
/// your state in a different stage, consider using [Self::add_state_to_stage] or manually
/// adding [State::get_driver] to additional stages you need it in.
pub fn add_state<T>(&mut self, initial: T) -> &mut Self
where
T: Component + Debug + Clone + Eq + Hash,
{
self.add_state_to_stage(CoreStage::Update, initial)
}
/// Adds a new [State] with the given `initial` value.
/// This inserts a new `State<T>` resource and adds a new "driver" to the given stage.
/// Each stage that uses `State<T>` for system run criteria needs a driver. If you need to use
/// your state in more than one stage, consider manually adding [State::get_driver] to the
/// stages you need it in.
pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self
where
T: Component + Debug + Clone + Eq + Hash,
{
self.insert_resource(State::new(initial))
.add_system_set_to_stage(stage, State::<T>::get_driver())
}
pub fn add_default_stages(&mut self) -> &mut Self {
self.add_stage(CoreStage::First, SystemStage::parallel())
.add_stage(
CoreStage::Startup,
Schedule::default()
.with_run_criteria(RunOnce::default())
.with_stage(StartupStage::PreStartup, SystemStage::parallel())
.with_stage(StartupStage::Startup, SystemStage::parallel())
.with_stage(StartupStage::PostStartup, SystemStage::parallel()),
)
.add_stage(CoreStage::PreUpdate, SystemStage::parallel())
.add_stage(CoreStage::Update, SystemStage::parallel())
.add_stage(CoreStage::PostUpdate, SystemStage::parallel())
.add_stage(CoreStage::Last, SystemStage::parallel())
}
/// Setup the application to manage events of type `T`.
///
/// This is done by adding a `Resource` of type `Events::<T>`,
/// and inserting a `Events::<T>::update_system` system into `CoreStage::First`.
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Component,
{
self.insert_resource(Events::<T>::default())
.add_system_to_stage(CoreStage::First, Events::<T>::update_system.system())
}
/// Inserts a resource to the current [App] and overwrites any resource previously added of the same type.
///
/// A resource in Bevy represents globally unique data. Resources must be added to Bevy Apps
/// before using them. This happens with [`AppBuilder::insert_resource`].
///
/// See also `init_resource` for resources that implement `Default` or [`FromResources`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// struct MyCounter {
/// counter: usize,
/// }
///
/// App::build()
/// .insert_resource(MyCounter { counter: 0 });
/// ```
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
where
T: Component,
{
self.app.world.insert_resource(resource);
self
}
/// Inserts a non-send resource to the app
///
/// You usually want to use `insert_resource`, but there are some special cases when a resource must
/// be non-send.
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// struct MyCounter {
/// counter: usize,
/// }
///
/// App::build()
/// .insert_non_send_resource(MyCounter { counter: 0 });
/// ```
pub fn insert_non_send_resource<T>(&mut self, resource: T) -> &mut Self
where
T: 'static,
{
self.app.world.insert_non_send(resource);
self
}
/// Initialize a resource in the current [App], if it does not exist yet
///
/// Adds a resource that implements `Default` or [`FromResources`] trait.
/// If the resource already exists, `init_resource` does nothing.
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// struct MyCounter {
/// counter: usize,
/// }
///
/// impl Default for MyCounter {
/// fn default() -> MyCounter {
/// MyCounter {
/// counter: 100
/// }
/// }
/// }
///
/// App::build()
/// .init_resource::<MyCounter>();
/// ```
pub fn init_resource<R>(&mut self) -> &mut Self
where
R: FromWorld + Send + Sync + 'static,
{
// PERF: We could avoid double hashing here, since the `from_resources` call is guaranteed
// not to modify the map. However, we would need to be borrowing resources both
// mutably and immutably, so we would need to be extremely certain this is correct
if !self.world_mut().contains_resource::<R>() {
let resource = R::from_world(self.world_mut());
self.insert_resource(resource);
}
self
}
pub fn init_non_send_resource<R>(&mut self) -> &mut Self
where
R: FromWorld + 'static,
{
// See perf comment in init_resource
if self.app.world.get_non_send_resource::<R>().is_none() {
let resource = R::from_world(self.world_mut());
self.app.world.insert_non_send(resource);
}
self
}
/// Sets the main runner loop function for this Bevy App
///
/// Usually the main loop is handled by Bevy integrated plugins ([`WinitPlugin`]), but
/// in some cases one might wish to implement their own main loop.
///
/// This method sets the main loop function, overwriting a previous runner if any.
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// fn my_runner(mut app: App) {
/// loop {
/// println!("In main loop");
/// app.update();
/// }
/// }
///
/// App::build()
/// .set_runner(my_runner);
/// ```
pub fn set_runner(&mut self, run_fn: impl Fn(App) + 'static) -> &mut Self {
self.app.runner = Box::new(run_fn);
self
}
/// Adds a single plugin
///
/// One of Bevy's core principles is modularity. All Bevy engine features are implemented
/// as plugins. This includes internal features like the renderer.
///
/// Bevy also provides a few sets of default plugins. See [`AppBuilder::add_plugins`].
///
/// ## Example
/// ```
/// # use bevy_app::prelude::*;
/// #
/// App::build().add_plugin(bevy_log::LogPlugin::default());
/// ```
pub fn add_plugin<T>(&mut self, plugin: T) -> &mut Self
where
T: Plugin,
{
debug!("added plugin: {}", plugin.name());
plugin.build(self);
self
}
/// Adds a group of plugins
///
/// Bevy plugins can be grouped into a set of plugins. Bevy provides
/// built-in PluginGroups that provide core engine functionality.
///
/// The plugin groups available by default are [`DefaultPlugins`] and [`MinimalPlugins`].
///
/// ## Example
/// ```
/// # use bevy_app::{prelude::*, PluginGroupBuilder};
/// #
/// # // Dummy created to avoid using bevy_internal, which pulls in to many dependencies.
/// # struct MinimalPlugins;
/// # impl PluginGroup for MinimalPlugins {
/// # fn build(&mut self, group: &mut PluginGroupBuilder){;}
/// # }
/// #
/// App::build()
/// .add_plugins(MinimalPlugins);
/// ```
pub fn add_plugins<T: PluginGroup>(&mut self, mut group: T) -> &mut Self {
let mut plugin_group_builder = PluginGroupBuilder::default();
group.build(&mut plugin_group_builder);
plugin_group_builder.finish(self);
self
}
/// Adds a group of plugins with an initializer method
///
/// Can be used to add a group of plugins, where the group is modified
/// before insertion into Bevy application. For example, you can add
/// extra plugins at a specific place in the plugin group, or deactivate
/// specific plugins while keeping the rest.
///
/// ## Example
/// ```
/// # use bevy_app::{prelude::*, PluginGroupBuilder};
/// #
/// # // Dummies created to avoid using bevy_internal which pulls in to many dependencies.
/// # struct DefaultPlugins;
/// # impl PluginGroup for DefaultPlugins {
/// # fn build(&mut self, group: &mut PluginGroupBuilder){
/// # group.add(bevy_log::LogPlugin::default());
/// # }
/// # }
/// #
/// # struct MyOwnPlugin;
/// # impl Plugin for MyOwnPlugin {
/// # fn build(&self, app: &mut AppBuilder){;}
/// # }
/// #
/// App::build()
/// .add_plugins_with(DefaultPlugins, |group| {
/// group.add_before::<bevy_log::LogPlugin, _>(MyOwnPlugin)
/// });
/// ```
pub fn add_plugins_with<T, F>(&mut self, mut group: T, func: F) -> &mut Self
where
T: PluginGroup,
F: FnOnce(&mut PluginGroupBuilder) -> &mut PluginGroupBuilder,
{
let mut plugin_group_builder = PluginGroupBuilder::default();
group.build(&mut plugin_group_builder);
func(&mut plugin_group_builder);
plugin_group_builder.finish(self);
self
}
/// Registers a new component using the given [ComponentDescriptor]. Components do not need to
/// be manually registered. This just provides a way to override default configuration.
/// Attempting to register a component with a type that has already been used by [World]
/// will result in an error.
///
/// See [World::register_component]
pub fn register_component(&mut self, descriptor: ComponentDescriptor) -> &mut Self {
self.world_mut().register_component(descriptor).unwrap();
self
}
#[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
{
let registry = self
.world_mut()
.get_resource_mut::<bevy_reflect::TypeRegistryArc>()
.unwrap();
registry.write().register::<T>();
}
self
}
}

View file

@ -1,5 +1,4 @@
mod app;
mod app_builder;
mod plugin;
mod plugin_group;
mod schedule_runner;
@ -8,7 +7,6 @@ mod schedule_runner;
mod ci_testing;
pub use app::*;
pub use app_builder::*;
pub use bevy_derive::DynamicPlugin;
pub use bevy_ecs::event::*;
pub use plugin::*;
@ -18,7 +16,7 @@ pub use schedule_runner::*;
pub mod prelude {
#[doc(hidden)]
pub use crate::{
app::App, app_builder::AppBuilder, CoreStage, DynamicPlugin, Plugin, PluginGroup,
app::App, CoreStage, DynamicPlugin, Plugin, PluginGroup,
StartupStage,
};
}

View file

@ -1,12 +1,12 @@
use crate::AppBuilder;
use crate::App;
use std::any::Any;
/// A collection of Bevy App logic and configuration
///
/// Plugins use [AppBuilder] to configure an [App](crate::App). When an [App](crate::App) registers
/// Plugins configure an [App](crate::App). When an [App](crate::App) registers
/// a plugin, the plugin's [Plugin::build] function is run.
pub trait Plugin: Any + Send + Sync {
fn build(&self, app: &mut AppBuilder);
fn build(&self, app: &mut App);
fn name(&self) -> &str {
std::any::type_name::<Self>()
}

View file

@ -1,4 +1,4 @@
use crate::{AppBuilder, Plugin};
use crate::{App, Plugin};
use bevy_utils::{tracing::debug, HashMap};
use std::any::TypeId;
@ -96,7 +96,7 @@ impl PluginGroupBuilder {
self
}
pub fn finish(self, app: &mut AppBuilder) {
pub fn finish(self, app: &mut App) {
for ty in self.order.iter() {
if let Some(entry) = self.plugins.get(ty) {
if entry.enabled {

View file

@ -1,5 +1,8 @@
use super::{App, AppBuilder};
use crate::{app::AppExit, plugin::Plugin, ManualEventReader};
use crate::{
app::{App, AppExit},
plugin::Plugin,
ManualEventReader,
};
use bevy_ecs::event::Events;
use bevy_utils::{Duration, Instant};
@ -48,9 +51,9 @@ impl ScheduleRunnerSettings {
pub struct ScheduleRunnerPlugin {}
impl Plugin for ScheduleRunnerPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
let settings = app
.world_mut()
.world
.get_resource_or_insert_with(ScheduleRunnerSettings::default)
.to_owned();
app.set_runner(move |mut app: App| {

View file

@ -2,7 +2,7 @@ use crate::{
update_asset_storage_system, Asset, AssetLoader, AssetServer, AssetStage, Handle, HandleId,
RefChange,
};
use bevy_app::{AppBuilder, EventWriter, Events};
use bevy_app::{App, EventWriter, Events};
use bevy_ecs::{
system::{IntoSystem, ResMut},
world::FromWorld,
@ -206,13 +206,13 @@ pub trait AddAsset {
T: AssetLoader;
}
impl AddAsset for AppBuilder {
impl AddAsset for App {
fn add_asset<T>(&mut self) -> &mut Self
where
T: Asset,
{
let assets = {
let asset_server = self.world().get_resource::<AssetServer>().unwrap();
let asset_server = self.world.get_resource::<AssetServer>().unwrap();
asset_server.register_asset_type::<T>()
};
@ -233,7 +233,7 @@ impl AddAsset for AppBuilder {
where
T: AssetLoader + FromWorld,
{
let result = T::from_world(self.world_mut());
let result = T::from_world(&mut self.world);
self.add_asset_loader(result)
}
@ -241,7 +241,7 @@ impl AddAsset for AppBuilder {
where
T: AssetLoader,
{
self.world_mut()
self.world
.get_resource_mut::<AssetServer>()
.expect("AssetServer does not exist. Consider adding it as a resource.")
.add_loader(loader);

View file

@ -17,7 +17,7 @@ impl<T: Asset> Default for AssetCountDiagnosticsPlugin<T> {
}
impl<T: Asset> Plugin for AssetCountDiagnosticsPlugin<T> {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_startup_system(Self::setup_system.system())
.add_system(Self::diagnostic_system.system());
}

View file

@ -26,7 +26,7 @@ pub use io::*;
pub use loader::*;
pub use path::*;
use bevy_app::{prelude::Plugin, AppBuilder};
use bevy_app::{prelude::Plugin, App};
use bevy_ecs::{
schedule::{StageLabel, SystemStage},
system::IntoSystem,
@ -61,9 +61,9 @@ impl Default for AssetServerSettings {
///
/// This is useful when providing a custom `AssetIo` instance that needs to
/// delegate to the default `AssetIo` for the platform.
pub fn create_platform_default_asset_io(app: &mut AppBuilder) -> Box<dyn AssetIo> {
pub fn create_platform_default_asset_io(app: &mut App) -> Box<dyn AssetIo> {
let settings = app
.world_mut()
.world
.get_resource_or_insert_with(AssetServerSettings::default);
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
@ -77,10 +77,10 @@ pub fn create_platform_default_asset_io(app: &mut AppBuilder) -> Box<dyn AssetIo
}
impl Plugin for AssetPlugin {
fn build(&self, app: &mut AppBuilder) {
if app.world().get_resource::<AssetServer>().is_none() {
fn build(&self, app: &mut App) {
if app.world.get_resource::<AssetServer>().is_none() {
let task_pool = app
.world()
.world
.get_resource::<IoTaskPool>()
.expect("`IoTaskPool` resource not found.")
.0

View file

@ -20,7 +20,7 @@ use bevy_ecs::system::IntoExclusiveSystem;
pub struct AudioPlugin;
impl Plugin for AudioPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.init_non_send_resource::<AudioOutput<AudioSource>>()
.add_asset::<AudioSource>()
.init_resource::<Audio<AudioSource>>()

View file

@ -38,13 +38,13 @@ pub enum CoreSystem {
}
impl Plugin for CorePlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
// Setup the default bevy task pools
app.world_mut()
app.world
.get_resource::<DefaultTaskPoolOptions>()
.cloned()
.unwrap_or_else(DefaultTaskPoolOptions::default)
.create_default_pools(app.world_mut());
.create_default_pools(&mut app.world);
app.init_resource::<Time>()
.init_resource::<EntityLabels>()
@ -70,7 +70,7 @@ impl Plugin for CorePlugin {
}
}
fn register_rust_types(app: &mut AppBuilder) {
fn register_rust_types(app: &mut App) {
app.register_type::<bool>()
.register_type::<u8>()
.register_type::<u16>()
@ -90,7 +90,7 @@ fn register_rust_types(app: &mut AppBuilder) {
.register_type::<Option<String>>();
}
fn register_math_types(app: &mut AppBuilder) {
fn register_math_types(app: &mut App) {
app.register_type::<bevy_math::IVec2>()
.register_type::<bevy_math::IVec3>()
.register_type::<bevy_math::IVec4>()

View file

@ -1,4 +1,4 @@
use bevy_app::{AppBuilder, Plugin};
use bevy_app::{App, Plugin};
use bevy_ecs::{
system::{IntoExclusiveSystem, IntoSystem, ResMut},
world::World,
@ -11,7 +11,7 @@ use crate::{Diagnostic, DiagnosticId, Diagnostics};
pub struct EntityCountDiagnosticsPlugin;
impl Plugin for EntityCountDiagnosticsPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_startup_system(Self::setup_system.system())
.add_system(Self::diagnostic_system.exclusive_system());
}

View file

@ -12,7 +12,7 @@ pub struct FrameTimeDiagnosticsState {
}
impl Plugin for FrameTimeDiagnosticsPlugin {
fn build(&self, app: &mut bevy_app::AppBuilder) {
fn build(&self, app: &mut bevy_app::App) {
app.add_startup_system(Self::setup_system.system())
.insert_resource(FrameTimeDiagnosticsState { frame_count: 0.0 })
.add_system(Self::diagnostic_system.system());

View file

@ -14,7 +14,7 @@ use bevy_app::prelude::*;
pub struct DiagnosticsPlugin;
impl Plugin for DiagnosticsPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.init_resource::<Diagnostics>();
}
}

View file

@ -29,7 +29,7 @@ impl Default for LogDiagnosticsPlugin {
}
impl Plugin for LogDiagnosticsPlugin {
fn build(&self, app: &mut bevy_app::AppBuilder) {
fn build(&self, app: &mut App) {
app.insert_resource(LogDiagnosticsState {
timer: Timer::new(self.wait_duration, true),
filter: self.filter.clone(),

View file

@ -1,6 +1,6 @@
use libloading::{Library, Symbol};
use bevy_app::{AppBuilder, CreatePlugin, Plugin};
use bevy_app::{App, CreatePlugin, Plugin};
/// Dynamically links a plugin a the given path. The plugin must export a function with the
/// [`CreatePlugin`] signature named `_bevy_create_plugin`.
@ -24,7 +24,7 @@ pub trait DynamicPluginExt {
unsafe fn load_plugin(&mut self, path: &str) -> &mut Self;
}
impl DynamicPluginExt for AppBuilder {
impl DynamicPluginExt for App {
unsafe fn load_plugin(&mut self, path: &str) -> &mut Self {
let (lib, plugin) = dynamically_load_plugin(path);
std::mem::forget(lib); // Ensure that the library is not automatically unloaded

View file

@ -309,6 +309,11 @@ impl Archetype {
.get(component_id)
.map(|info| info.archetype_component_id)
}
pub(crate) fn clear_entities(&mut self) {
self.entities.clear();
self.table_info.entity_rows.clear();
}
}
/// A generational id that changes every time the set of archetypes changes
@ -519,6 +524,12 @@ impl Archetypes {
pub fn archetype_components_len(&self) -> usize {
self.archetype_component_count
}
pub fn clear_entities(&mut self) {
for archetype in self.archetypes.iter_mut() {
archetype.clear_entities();
}
}
}
impl Index<ArchetypeId> for Archetypes {

View file

@ -339,6 +339,7 @@ impl Entities {
self.meta.clear();
self.pending.clear();
*self.free_cursor.get_mut() = 0;
self.len = 0;
}
/// Access the location storage of an entity.

View file

@ -1226,4 +1226,26 @@ mod tests {
assert_eq!(dropped1.load(Ordering::Relaxed), 1);
assert_eq!(dropped2.load(Ordering::Relaxed), 1);
}
fn clear_entities() {
let mut world = World::default();
world.register_component(ComponentDescriptor::new::<f32>(StorageType::SparseSet)).unwrap();
world.insert_resource::<i32>(0);
world.spawn().insert(1u32);
world.spawn().insert(1.0f32);
let mut q1 = world.query::<&u32>();
let mut q2 = world.query::<&f32>();
assert_eq!(q1.iter(&world).len(), 1);
assert_eq!(q2.iter(&world).len(), 1);
assert_eq!(world.entities().len(), 2);
world.clear_entities();
assert_eq!(q1.iter(&world).len(), 0, "world should not contain table components");
assert_eq!(q2.iter(&world).len(), 0, "world should not contain sparse set components");
assert_eq!(world.entities().len(), 0, "world should not have any entities");
assert_eq!(*world.get_resource::<i32>().unwrap(), 0, "world should still contain resources");
}
}

View file

@ -83,6 +83,7 @@ pub struct SystemStage {
uninitialized_parallel: Vec<usize>,
/// Saves the value of the World change_tick during the last tick check
last_tick_check: u32,
apply_buffers: bool,
}
impl SystemStage {
@ -104,6 +105,7 @@ impl SystemStage {
uninitialized_before_commands: vec![],
uninitialized_at_end: vec![],
last_tick_check: Default::default(),
apply_buffers: true,
}
}
@ -203,6 +205,16 @@ impl SystemStage {
}
}
pub fn apply_buffers(&mut self, world: &mut World) {
for container in self.parallel.iter_mut() {
container.system_mut().apply_buffers(world);
}
}
pub fn set_apply_buffers(&mut self, apply_buffers: bool) {
self.apply_buffers = apply_buffers;
}
/// Topologically sorted parallel systems.
///
/// Note that systems won't be fully-formed until the stage has been run at least once.
@ -828,9 +840,11 @@ impl Stage for SystemStage {
}
// Apply parallel systems' buffers.
for container in &mut self.parallel {
if container.should_run {
container.system_mut().apply_buffers(world);
if self.apply_buffers {
for container in &mut self.parallel {
if container.should_run {
container.system_mut().apply_buffers(world);
}
}
}

View file

@ -82,6 +82,10 @@ impl<I: SparseSetIndex, V> SparseArray<I, V> {
*value = Some(func());
value.as_mut().unwrap()
}
pub fn clear(&mut self) {
self.values.clear();
}
}
#[derive(Debug)]
@ -102,6 +106,13 @@ impl ComponentSparseSet {
}
}
pub fn clear(&mut self) {
self.dense.clear();
self.ticks.get_mut().clear();
self.entities.clear();
self.sparse.clear();
}
#[inline]
pub fn len(&self) -> usize {
self.dense.len()
@ -400,6 +411,12 @@ impl SparseSets {
self.sets.get_mut(component_id)
}
pub fn clear(&mut self) {
for set in self.sets.values_mut() {
set.clear();
}
}
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
for set in self.sets.values_mut() {
set.check_change_ticks(change_tick);

View file

@ -179,6 +179,11 @@ impl Column {
self.ticks.get_unchecked(row).get()
}
pub fn clear(&mut self) {
self.data.clear();
self.ticks.get_mut().clear();
}
#[inline]
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
for component_ticks in &mut self.ticks {
@ -396,6 +401,13 @@ impl Table {
pub fn iter(&self) -> impl Iterator<Item = &Column> {
self.columns.values()
}
pub fn clear(&mut self) {
self.entities.clear();
for column in self.columns.values_mut() {
column.clear();
}
}
}
pub struct Tables {
@ -475,6 +487,16 @@ impl Tables {
self.tables.iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Table> {
self.tables.iter_mut()
}
pub fn clear(&mut self) {
for table in self.tables.iter_mut() {
table.clear();
}
}
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
for table in self.tables.iter_mut() {
table.check_change_ticks(change_tick);

View file

@ -58,6 +58,13 @@ impl<'a> Commands<'a> {
}
}
// 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"
pub fn spawn_and_forget(&mut self, bundle: impl Bundle) {
self.queue.push(Spawn { bundle })
}
/// Creates a new entity with the components contained in `bundle`.
///
/// This returns an [`EntityCommands`] builder, which enables inserting more components and

View file

@ -24,7 +24,7 @@ pub struct SystemMeta {
}
impl SystemMeta {
fn new<T>() -> Self {
pub fn new<T>() -> Self {
Self {
name: std::any::type_name::<T>().into(),
archetype_component_access: Access::default(),

View file

@ -0,0 +1,36 @@
use crate::system::{SystemParam, SystemParamFetch, SystemParamState, SystemState};
use crate::world::World;
pub struct ParamState<Param: SystemParam> {
state: SystemState,
param_state: <Param as SystemParam>::Fetch,
change_tick: u32,
}
impl<Param: SystemParam> ParamState<Param> {
pub fn new(world: &mut World) -> Self {
let mut state = SystemState::new::<Param>();
let config = <Param::Fetch as SystemParamState>::default_config();
let param_state = <Param::Fetch as SystemParamState>::init(world, &mut state, config);
Self {
state,
param_state,
change_tick: 0,
}
}
// TODO: THIS IS SUPER UNSAFE PLEASE DON'T MERGE UNTIL IT IS CONSTRAINED TO READ-ONLY PARAMS
pub fn get<'a>(&'a mut self, world: &'a World) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
let change_tick = world.increment_change_tick();
self.change_tick = change_tick;
// TODO: add/implement ReadOnlySystemParam and constrain param here
unsafe {
<Param::Fetch as SystemParamFetch>::get_param(
&mut self.param_state,
&mut self.state,
world,
change_tick,
)
}
}
}

View file

@ -917,6 +917,13 @@ impl World {
column.check_change_ticks(change_tick);
}
}
pub fn clear_entities(&mut self) {
self.storages.tables.clear();
self.storages.sparse_sets.clear();
self.archetypes.clear_entities();
self.entities.clear();
}
}
impl fmt::Debug for World {

View file

@ -1,7 +1,7 @@
mod converter;
mod gilrs_system;
use bevy_app::{AppBuilder, CoreStage, Plugin, StartupStage};
use bevy_app::{App, CoreStage, Plugin, StartupStage};
use bevy_ecs::system::IntoExclusiveSystem;
use bevy_utils::tracing::error;
use gilrs::GilrsBuilder;
@ -11,7 +11,7 @@ use gilrs_system::{gilrs_event_startup_system, gilrs_event_system};
pub struct GilrsPlugin;
impl Plugin for GilrsPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
match GilrsBuilder::new()
.with_default_filters(false)
.set_update_state(false)

View file

@ -15,7 +15,7 @@ use bevy_scene::Scene;
pub struct GltfPlugin;
impl Plugin for GltfPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.init_asset_loader::<GltfLoader>()
.add_asset::<Gltf>()
.add_asset::<GltfNode>()

View file

@ -45,7 +45,7 @@ pub struct InputPlugin;
pub struct InputSystem;
impl Plugin for InputPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app
// keyboard
.add_event::<KeyboardInput>()

View file

@ -19,12 +19,12 @@ trace = [ "bevy_app/trace", "bevy_ecs/trace" ]
trace_chrome = [ "bevy_log/tracing-chrome" ]
# Image format support for texture loading (PNG and HDR are enabled by default)
hdr = ["bevy_render/hdr"]
png = ["bevy_render/png"]
dds = ["bevy_render/dds"]
tga = ["bevy_render/tga"]
jpeg = ["bevy_render/jpeg"]
bmp = ["bevy_render/bmp"]
hdr = ["bevy_render/hdr", "bevy_render2/hdr" ]
png = ["bevy_render/png", "bevy_render2/png" ]
dds = ["bevy_render/dds", "bevy_render2/dds" ]
tga = ["bevy_render/tga", "bevy_render2/tga" ]
jpeg = ["bevy_render/jpeg", "bevy_render2/jpeg" ]
bmp = ["bevy_render/bmp", "bevy_render2/bmp" ]
# Audio format support (MP3 is enabled by default)
flac = ["bevy_audio/flac"]
@ -69,13 +69,17 @@ 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_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" }
bevy_sprite = { path = "../bevy_sprite", optional = true, version = "0.5.0" }
bevy_sprite2 = { path = "../../pipelined/bevy_sprite2", optional = true, version = "0.5.0" }
bevy_text = { path = "../bevy_text", optional = true, version = "0.5.0" }
bevy_ui = { path = "../bevy_ui", optional = true, version = "0.5.0" }
bevy_wgpu = { path = "../bevy_wgpu", optional = true, version = "0.5.0" }
bevy_wgpu2 = { path = "../../pipelined/bevy_wgpu2", optional = true, version = "0.5.0" }
bevy_winit = { path = "../bevy_winit", optional = true, version = "0.5.0" }
bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.5.0" }
[target.'cfg(target_os = "android")'.dependencies]
ndk-glue = {version = "0.2", features = ["logger"]}

View file

@ -105,3 +105,29 @@ impl PluginGroup for MinimalPlugins {
group.add(ScheduleRunnerPlugin::default());
}
}
pub struct PipelinedDefaultPlugins;
impl PluginGroup for PipelinedDefaultPlugins {
fn build(&mut self, group: &mut PluginGroupBuilder) {
group.add(bevy_log::LogPlugin::default());
group.add(bevy_core::CorePlugin::default());
group.add(bevy_transform::TransformPlugin::default());
group.add(bevy_diagnostic::DiagnosticsPlugin::default());
group.add(bevy_input::InputPlugin::default());
group.add(bevy_window::WindowPlugin::default());
group.add(bevy_asset::AssetPlugin::default());
#[cfg(feature = "bevy_render2")]
group.add(bevy_render2::RenderPlugin::default());
#[cfg(feature = "bevy_winit")]
group.add(bevy_winit::WinitPlugin::default());
#[cfg(feature = "bevy_wgpu2")]
group.add(bevy_wgpu2::WgpuPlugin::default());
#[cfg(feature = "bevy_sprite2")]
group.add(bevy_sprite2::SpritePlugin::default());
}
}

View file

@ -105,12 +105,24 @@ pub mod render {
pub use bevy_render::*;
}
#[cfg(feature = "bevy_render2")]
pub mod render2 {
//! Cameras, meshes, textures, shaders, and pipelines.
pub use bevy_render2::*;
}
#[cfg(feature = "bevy_sprite")]
pub mod sprite {
//! Items for sprites, rects, texture atlases, etc.
pub use bevy_sprite::*;
}
#[cfg(feature = "bevy_sprite2")]
pub mod sprite2 {
//! Items for sprites, rects, texture atlases, etc.
pub use bevy_sprite2::*;
}
#[cfg(feature = "bevy_text")]
pub mod text {
//! Text drawing, styling, and font assets.
@ -134,6 +146,12 @@ pub mod wgpu {
pub use bevy_wgpu::*;
}
#[cfg(feature = "bevy_wgpu2")]
pub mod wgpu2 {
//! A render backend utilizing [wgpu](https://github.com/gfx-rs/wgpu-rs).
pub use bevy_wgpu2::*;
}
#[cfg(feature = "bevy_dynamic_plugin")]
pub mod dynamic_plugin {
pub use bevy_dynamic_plugin::*;

View file

@ -12,7 +12,7 @@ pub use bevy_utils::tracing::{
Level,
};
use bevy_app::{AppBuilder, Plugin};
use bevy_app::{App, Plugin};
#[cfg(feature = "tracing-chrome")]
use tracing_subscriber::fmt::{format::DefaultFields, FormattedFields};
use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter};
@ -81,11 +81,9 @@ impl Default for LogSettings {
}
impl Plugin for LogPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
let default_filter = {
let settings = app
.world_mut()
.get_resource_or_insert_with(LogSettings::default);
let settings = app.world.get_resource_or_insert_with(LogSettings::default);
format!("{},{}", settings.level, settings.filter)
};

View file

@ -29,7 +29,7 @@ use render_graph::add_pbr_graph;
pub struct PbrPlugin;
impl Plugin for PbrPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_asset::<StandardMaterial>()
.register_type::<PointLight>()
.add_system_to_stage(
@ -37,11 +37,11 @@ impl Plugin for PbrPlugin {
shader::asset_shader_defs_system::<StandardMaterial>.system(),
)
.init_resource::<AmbientLight>();
add_pbr_graph(app.world_mut());
add_pbr_graph(&mut app.world);
// add default StandardMaterial
let mut materials = app
.world_mut()
.world
.get_resource_mut::<Assets<StandardMaterial>>()
.unwrap();
materials.set_untracked(

View file

@ -228,8 +228,8 @@ impl Plugin for RenderPlugin {
);
if let Some(ref config) = self.base_render_graph_config {
crate::base::add_base_graph(config, app.world_mut());
let mut active_cameras = app.world_mut().get_resource_mut::<ActiveCameras>().unwrap();
crate::base::add_base_graph(config, &mut app.world);
let mut active_cameras = app.world.get_resource_mut::<ActiveCameras>().unwrap();
if config.add_3d_camera {
active_cameras.add(base::camera::CAMERA_3D);
}

View file

@ -25,10 +25,10 @@ pub const WIREFRAME_PIPELINE_HANDLE: HandleUntyped =
pub struct WireframePlugin;
impl Plugin for WireframePlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.init_resource::<WireframeConfig>()
.add_system_to_stage(crate::RenderStage::Draw, draw_wireframes_system.system());
let world = app.world_mut().cell();
let world = app.world.cell();
let mut shaders = world.get_resource_mut::<Assets<Shader>>().unwrap();
let mut pipelines = world
.get_resource_mut::<Assets<PipelineDescriptor>>()

View file

@ -26,7 +26,7 @@ use bevy_ecs::{schedule::ExclusiveSystemDescriptorCoercion, system::IntoExclusiv
pub struct ScenePlugin;
impl Plugin for ScenePlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_asset::<DynamicScene>()
.add_asset::<Scene>()
.init_asset_loader::<SceneLoader>()

View file

@ -67,7 +67,7 @@ pub const QUAD_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Mesh::TYPE_UUID, 14240461981130137526);
impl Plugin for SpritePlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_asset::<ColorMaterial>()
.add_asset::<TextureAtlas>()
.register_type::<Sprite>()
@ -83,7 +83,7 @@ impl Plugin for SpritePlugin {
);
let sprite_settings = app
.world_mut()
.world
.get_resource_or_insert_with(SpriteSettings::default)
.clone();
if sprite_settings.frustum_culling_enabled {
@ -96,14 +96,13 @@ impl Plugin for SpritePlugin {
frustum_culling::atlas_frustum_culling_system.system(),
);
}
let world = app.world_mut();
world
app.world
.register_component(ComponentDescriptor::new::<OutsideFrustum>(
StorageType::SparseSet,
))
.unwrap();
let world_cell = world.cell();
let world_cell = app.world.cell();
let mut render_graph = world_cell.get_resource_mut::<RenderGraph>().unwrap();
let mut pipelines = world_cell
.get_resource_mut::<Assets<PipelineDescriptor>>()

View file

@ -38,7 +38,7 @@ pub type DefaultTextPipeline = TextPipeline<Entity>;
pub struct TextPlugin;
impl Plugin for TextPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_asset::<Font>()
.add_asset::<FontAtlasSet>()
.init_asset_loader::<FontLoader>()

View file

@ -24,7 +24,7 @@ pub enum TransformSystem {
}
impl Plugin for TransformPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.register_type::<Children>()
.register_type::<Parent>()
.register_type::<PreviousParent>()

View file

@ -43,7 +43,7 @@ pub enum UiSystem {
}
impl Plugin for UiPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.init_resource::<FlexSurface>()
.register_type::<AlignContent>()
.register_type::<AlignItems>()
@ -92,6 +92,6 @@ impl Plugin for UiPlugin {
)
.add_system_to_stage(RenderStage::Draw, widget::draw_text_system.system());
crate::render::add_ui_graph(app.world_mut());
crate::render::add_ui_graph(&mut app.world);
}
}

View file

@ -8,7 +8,7 @@ use bevy_render::renderer::RenderResourceContext;
pub struct WgpuResourceDiagnosticsPlugin;
impl Plugin for WgpuResourceDiagnosticsPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_startup_system(Self::setup_system.system())
.add_system(Self::diagnostic_system.system());
}

View file

@ -104,8 +104,8 @@ impl Default for WgpuLimits {
pub struct WgpuPlugin;
impl Plugin for WgpuPlugin {
fn build(&self, app: &mut AppBuilder) {
let render_system = get_wgpu_render_system(app.world_mut());
fn build(&self, app: &mut App) {
let render_system = get_wgpu_render_system(&mut app.world);
app.add_system_to_stage(RenderStage::Render, render_system.exclusive_system())
.add_system_to_stage(
RenderStage::PostRender,

View file

@ -34,7 +34,7 @@ impl Default for WindowPlugin {
}
impl Plugin for WindowPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_event::<WindowResized>()
.add_event::<CreateWindow>()
.add_event::<WindowCreated>()
@ -52,12 +52,15 @@ impl Plugin for WindowPlugin {
.init_resource::<Windows>();
if self.add_primary_window {
let world = app.world_mut();
let window_descriptor = world
let window_descriptor = app
.world
.get_resource::<WindowDescriptor>()
.map(|descriptor| (*descriptor).clone())
.unwrap_or_else(WindowDescriptor::default);
let mut create_window_event = world.get_resource_mut::<Events<CreateWindow>>().unwrap();
let mut create_window_event = app
.world
.get_resource_mut::<Events<CreateWindow>>()
.unwrap();
create_window_event.send(CreateWindow {
id: WindowId::primary(),
descriptor: window_descriptor,

View file

@ -10,7 +10,7 @@ use bevy_input::{
pub use winit_config::*;
pub use winit_windows::*;
use bevy_app::{App, AppBuilder, AppExit, CoreStage, Events, ManualEventReader, Plugin};
use bevy_app::{App, AppExit, CoreStage, Events, ManualEventReader, Plugin};
use bevy_ecs::{system::IntoExclusiveSystem, world::World};
use bevy_math::{ivec2, Vec2};
use bevy_utils::tracing::{error, trace, warn};
@ -39,7 +39,7 @@ use winit::platform::unix::EventLoopExtUnix;
pub struct WinitPlugin;
impl Plugin for WinitPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.init_resource::<WinitWindows>()
.set_runner(winit_runner)
.add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system());

View file

@ -0,0 +1,39 @@
# Crevice Changelog
## Unreleased Changes
## [0.6.0][0.6.0] (2021-02-24)
* Added `std430` support. Most APIs between `std140` and `std430` are the same!
* Added the `WriteStd140` trait. This trait is more general than `AsStd140` and is automatically implemented for all existing `AsStd140` implementers.
* Added `Writer::write_std140` to write a type that implements `Std140`.
* Added `AsStd140::std140_size_static`. This is similar to the old size method, `std140_size`, but no longer requires a value to be passed. For size measurements that depend on a value, use `WriteStd140::std140_size` instead.
* Deprecated `Writer::write_slice`, as `Writer::write` now accepts slices.
* Changed bounds of some functions, like `Writer::write` to use `WriteStd140` instead of `AsStd140`. This should affect no existing consumers.
* Moved `std140_size` from `AsStd140` to `WriteStd140`. Some existing consumers may need to import the other trait to access this m ethod.
[0.6.0]: https://github.com/LPGhatguy/crevice/releases/tag/v0.6.0
## 0.5.0 (2020-10-18)
* Added f64-based std140 types: `DVec2`, `DVec3`, `DVec4`, `DMat2`, `DMat3`, and `DMat4`.
* Added support for std140 structs with alignment greater than 16.
* Fixed padding for std140 matrices; they were previously missing trailing padding.
## 0.4.0 (2020-10-01)
* Added `AsStd140::std140_size` for easily pre-sizing buffers.
* `Writer::write` and `Sizer::add` now return the offset the value is or would be written to.
* Added `std140::DynamicUniform` for aligning dynamic uniform members.
* Added `Writer::write_slice` for writing multiple values in a row.
## 0.3.0 (2020-09-22)
* Added `Std140::as_bytes`, reducing the need to work with bytemuck directly.
* Removed public re-export of bytemuck.
## 0.2.0 (2020-09-22)
* Added documentation for everything in the crate.
* Removed `type_layout` being exposed except for internal tests.
* Fixed alignment offset not taking into account previously added alignment.
* Added `std140::Writer`, for writing dynamically laid out types to buffers.
* Added `std140::Sizer`, for pre-calculating buffer sizes.
## 0.1.0 (2020-09-18)
* Initial MVP release

27
crates/crevice/Cargo.toml Normal file
View file

@ -0,0 +1,27 @@
[package]
name = "crevice"
description = "Create GLSL-compatible versions of structs with explicitly-initialized padding"
version = "0.6.0"
edition = "2018"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
documentation = "https://docs.rs/crevice"
homepage = "https://github.com/LPGhatguy/crevice"
repository = "https://github.com/LPGhatguy/crevice"
readme = "README.md"
keywords = ["glsl", "std140", "std430"]
license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crevice-derive = "0.6.0"
bytemuck = "1.4.1"
mint = { version = "0.5.5", optional = true }
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"] }

View file

@ -0,0 +1,201 @@
i Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,19 @@
Copyright (c) 2020 Lucien Greathouse
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

135
crates/crevice/README.md Normal file
View file

@ -0,0 +1,135 @@
# Crevice
[![GitHub CI Status](https://github.com/LPGhatguy/crevice/workflows/CI/badge.svg)](https://github.com/LPGhatguy/crevice/actions)
[![crevice on crates.io](https://img.shields.io/crates/v/crevice.svg)](https://crates.io/crates/crevice)
[![crevice docs](https://img.shields.io/badge/docs-docs.rs-orange.svg)](https://docs.rs/crevice)
Crevice creates GLSL-compatible versions of types through the power of derive
macros. Generated structures provide an [`as_bytes`][std140::Std140::as_bytes]
method to allow safely packing data into buffers for uploading.
Generated structs also implement [`bytemuck::Zeroable`] and
[`bytemuck::Pod`] for use with other libraries.
Crevice is similar to [`glsl-layout`][glsl-layout], but supports `mint` types
and explicitly initializes padding to remove one source of undefined behavior.
Examples in this crate use cgmath, but any math crate that works with the mint
crate will also work. Some other crates include nalgebra, ultraviolet, glam, and
vek.
### Examples
#### Single Value
Uploading many types can be done by deriving `AsStd140` and using
[`as_std140`][std140::AsStd140::as_std140] and
[`as_bytes`][std140::Std140::as_bytes] to turn the result into bytes.
```glsl
uniform MAIN {
mat3 orientation;
vec3 position;
float scale;
} main;
```
```rust
use crevice::std140::{AsStd140, Std140};
use cgmath::prelude::*;
use cgmath::{Matrix3, Vector3};
#[derive(AsStd140)]
struct MainUniform {
orientation: mint::ColumnMatrix3<f32>,
position: mint::Vector3<f32>,
scale: f32,
}
let value = MainUniform {
orientation: Matrix3::identity().into(),
position: Vector3::new(1.0, 2.0, 3.0).into(),
scale: 4.0,
};
let value_std140 = value.as_std140();
upload_data_to_gpu(value_std140.as_bytes());
```
#### Sequential Types
More complicated data can be uploaded using the std140 `Writer` type.
```glsl
struct PointLight {
vec3 position;
vec3 color;
float brightness;
};
buffer POINT_LIGHTS {
uint len;
PointLight[] lights;
} point_lights;
```
```rust
use crevice::std140::{self, AsStd140};
#[derive(AsStd140)]
struct PointLight {
position: mint::Vector3<f32>,
color: mint::Vector3<f32>,
brightness: f32,
}
let lights = vec![
PointLight {
position: [0.0, 1.0, 0.0].into(),
color: [1.0, 0.0, 0.0].into(),
brightness: 0.6,
},
PointLight {
position: [0.0, 4.0, 3.0].into(),
color: [1.0, 1.0, 1.0].into(),
brightness: 1.0,
},
];
let target_buffer = map_gpu_buffer_for_write();
let mut writer = std140::Writer::new(target_buffer);
let light_count = lights.len() as u32;
writer.write(&light_count)?;
// Crevice will automatically insert the required padding to align the
// PointLight structure correctly. In this case, there will be 12 bytes of
// padding between the length field and the light list.
writer.write(lights.as_slice())?;
unmap_gpu_buffer();
```
### Minimum Supported Rust Version (MSRV)
Crevice supports Rust 1.46.0 and newer due to use of new `const fn` features.
[glsl-layout]: https://github.com/rustgd/glsl-layout
[Zeroable]: https://docs.rs/bytemuck/latest/bytemuck/trait.Zeroable.html
[Pod]: https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html
[TypeLayout]: https://docs.rs/type-layout/latest/type_layout/trait.TypeLayout.html
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

15
crates/crevice/README.tpl Normal file
View file

@ -0,0 +1,15 @@
# Crevice
{{readme}}
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

View file

@ -0,0 +1,78 @@
use bytemuck::Zeroable;
use crate::std140::{self, AsStd140};
use crate::std430::{self, AsStd430};
macro_rules! glam_vectors {
( $( $glam_ty:ty, $std_name:ident, ( $($field:ident),* ), )* ) => {
$(
impl AsStd140 for $glam_ty {
type Std140Type = std140::$std_name;
fn as_std140(&self) -> Self::Std140Type {
std140::$std_name {
$(
$field: self.$field,
)*
}
}
}
impl AsStd430 for $glam_ty {
type Std430Type = std430::$std_name;
fn as_std430(&self) -> Self::Std430Type {
std430::$std_name {
$(
$field: self.$field,
)*
}
}
}
)*
};
}
glam_vectors! {
glam::Vec2, Vec2, (x, y),
glam::Vec3, Vec3, (x, y, z),
glam::Vec4, Vec4, (x, y, z, w),
}
macro_rules! glam_matrices {
( $( $glam_ty:ty, $std_name:ident, ( $($glam_field:ident),* ), ( $($field:ident),* ))* ) => {
$(
impl AsStd140 for $glam_ty {
type Std140Type = std140::$std_name;
fn as_std140(&self) -> Self::Std140Type {
std140::$std_name {
$(
$field: self.$glam_field.as_std140(),
)*
..Zeroable::zeroed()
}
}
}
impl AsStd430 for $glam_ty {
type Std430Type = std430::$std_name;
fn as_std430(&self) -> Self::Std430Type {
std430::$std_name {
$(
$field: self.$glam_field.as_std430(),
)*
..Zeroable::zeroed()
}
}
}
)*
};
}
glam_matrices! {
glam::Mat2, Mat2, (x_axis, y_axis), (x, y)
glam::Mat3, Mat3, (x_axis, y_axis, z_axis), (x, y, z)
glam::Mat4, Mat4, (x_axis, y_axis, z_axis, w_axis), (x, y, z, w)
}

View file

@ -0,0 +1,23 @@
//! This module is internal to crevice but used by its derive macro. No
//! guarantees are made about its contents.
pub use bytemuck;
/// Align the given struct offset up to the given alignment.
pub const fn align_offset(offset: usize, alignment: usize) -> usize {
if offset % alignment == 0 {
0
} else {
alignment - offset % alignment
}
}
/// Max of two `usize`. Implemented because the `max` method from `Ord` cannot
/// be used in const fns.
pub const fn max(a: usize, b: usize) -> usize {
if a > b {
a
} else {
b
}
}

142
crates/crevice/src/lib.rs Normal file
View file

@ -0,0 +1,142 @@
/*!
[![GitHub CI Status](https://github.com/LPGhatguy/crevice/workflows/CI/badge.svg)](https://github.com/LPGhatguy/crevice/actions)
[![crevice on crates.io](https://img.shields.io/crates/v/crevice.svg)](https://crates.io/crates/crevice)
[![crevice docs](https://img.shields.io/badge/docs-docs.rs-orange.svg)](https://docs.rs/crevice)
Crevice creates GLSL-compatible versions of types through the power of derive
macros. Generated structures provide an [`as_bytes`][std140::Std140::as_bytes]
method to allow safely packing data into buffers for uploading.
Generated structs also implement [`bytemuck::Zeroable`] and
[`bytemuck::Pod`] for use with other libraries.
Crevice is similar to [`glsl-layout`][glsl-layout], but supports `mint` types
and explicitly initializes padding to remove one source of undefined behavior.
Examples in this crate use cgmath, but any math crate that works with the mint
crate will also work. Some other crates include nalgebra, ultraviolet, glam, and
vek.
## Examples
### Single Value
Uploading many types can be done by deriving `AsStd140` and using
[`as_std140`][std140::AsStd140::as_std140] and
[`as_bytes`][std140::Std140::as_bytes] to turn the result into bytes.
```glsl
uniform MAIN {
mat3 orientation;
vec3 position;
float scale;
} main;
```
```rust
use crevice::std140::{AsStd140, Std140};
use cgmath::prelude::*;
use cgmath::{Matrix3, Vector3};
#[derive(AsStd140)]
struct MainUniform {
orientation: mint::ColumnMatrix3<f32>,
position: mint::Vector3<f32>,
scale: f32,
}
let value = MainUniform {
orientation: Matrix3::identity().into(),
position: Vector3::new(1.0, 2.0, 3.0).into(),
scale: 4.0,
};
let value_std140 = value.as_std140();
# fn upload_data_to_gpu(_value: &[u8]) {}
upload_data_to_gpu(value_std140.as_bytes());
```
### Sequential Types
More complicated data can be uploaded using the std140 `Writer` type.
```glsl
struct PointLight {
vec3 position;
vec3 color;
float brightness;
};
buffer POINT_LIGHTS {
uint len;
PointLight[] lights;
} point_lights;
```
```rust
use crevice::std140::{self, AsStd140};
#[derive(AsStd140)]
struct PointLight {
position: mint::Vector3<f32>,
color: mint::Vector3<f32>,
brightness: f32,
}
let lights = vec![
PointLight {
position: [0.0, 1.0, 0.0].into(),
color: [1.0, 0.0, 0.0].into(),
brightness: 0.6,
},
PointLight {
position: [0.0, 4.0, 3.0].into(),
color: [1.0, 1.0, 1.0].into(),
brightness: 1.0,
},
];
# fn map_gpu_buffer_for_write() -> &'static mut [u8] {
# Box::leak(vec![0; 1024].into_boxed_slice())
# }
let target_buffer = map_gpu_buffer_for_write();
let mut writer = std140::Writer::new(target_buffer);
let light_count = lights.len() as u32;
writer.write(&light_count)?;
// Crevice will automatically insert the required padding to align the
// PointLight structure correctly. In this case, there will be 12 bytes of
// padding between the length field and the light list.
writer.write(lights.as_slice())?;
# fn unmap_gpu_buffer() {}
unmap_gpu_buffer();
# Ok::<(), std::io::Error>(())
```
## Minimum Supported Rust Version (MSRV)
Crevice supports Rust 1.46.0 and newer due to use of new `const fn` features.
[glsl-layout]: https://github.com/rustgd/glsl-layout
[Zeroable]: https://docs.rs/bytemuck/latest/bytemuck/trait.Zeroable.html
[Pod]: https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html
[TypeLayout]: https://docs.rs/type-layout/latest/type_layout/trait.TypeLayout.html
*/
#![deny(missing_docs)]
pub mod std140;
pub mod std430;
#[doc(hidden)]
pub mod internal;
#[cfg(feature = "mint")]
mod mint;
mod glam;

View file

@ -0,0 +1,78 @@
use bytemuck::Zeroable;
use crate::std140::{self, AsStd140};
use crate::std430::{self, AsStd430};
macro_rules! mint_vectors {
( $( $mint_ty:ty, $std_name:ident, ( $($field:ident),* ), )* ) => {
$(
impl AsStd140 for $mint_ty {
type Std140Type = std140::$std_name;
fn as_std140(&self) -> Self::Std140Type {
std140::$std_name {
$(
$field: self.$field,
)*
}
}
}
impl AsStd430 for $mint_ty {
type Std430Type = std430::$std_name;
fn as_std430(&self) -> Self::Std430Type {
std430::$std_name {
$(
$field: self.$field,
)*
}
}
}
)*
};
}
mint_vectors! {
mint::Vector2<f32>, Vec2, (x, y),
mint::Vector3<f32>, Vec3, (x, y, z),
mint::Vector4<f32>, Vec4, (x, y, z, w),
}
macro_rules! mint_matrices {
( $( $mint_ty:ty, $std_name:ident, ( $($field:ident),* ), )* ) => {
$(
impl AsStd140 for $mint_ty {
type Std140Type = std140::$std_name;
fn as_std140(&self) -> Self::Std140Type {
std140::$std_name {
$(
$field: self.$field.as_std140(),
)*
..Zeroable::zeroed()
}
}
}
impl AsStd430 for $mint_ty {
type Std430Type = std430::$std_name;
fn as_std430(&self) -> Self::Std430Type {
std430::$std_name {
$(
$field: self.$field.as_std430(),
)*
..Zeroable::zeroed()
}
}
}
)*
};
}
mint_matrices! {
mint::ColumnMatrix2<f32>, Mat2, (x, y),
mint::ColumnMatrix3<f32>, Mat3, (x, y, z),
mint::ColumnMatrix4<f32>, Mat4, (x, y, z, w),
}

View file

@ -0,0 +1,16 @@
//! Defines traits and types for working with data adhering to GLSL's `std140`
//! layout specification.
mod dynamic_uniform;
mod primitives;
mod sizer;
mod traits;
mod writer;
pub use self::dynamic_uniform::*;
pub use self::primitives::*;
pub use self::sizer::*;
pub use self::traits::*;
pub use self::writer::*;
pub use crevice_derive::AsStd140;

View file

@ -0,0 +1,56 @@
use bytemuck::{Pod, Zeroable};
use crate::internal::max;
use crate::std140::{AsStd140, Std140};
/// Wrapper type that aligns the inner type to at least 256 bytes.
///
/// This type is useful for ensuring correct alignment when creating dynamic
/// uniform buffers in APIs like WebGPU.
pub struct DynamicUniform<T>(pub T);
impl<T: AsStd140> AsStd140 for DynamicUniform<T> {
type Std140Type = DynamicUniformStd140<<T as AsStd140>::Std140Type>;
fn as_std140(&self) -> Self::Std140Type {
DynamicUniformStd140(self.0.as_std140())
}
}
/// std140 variant of [`DynamicUniform`].
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct DynamicUniformStd140<T>(T);
unsafe impl<T: Std140> Std140 for DynamicUniformStd140<T> {
const ALIGNMENT: usize = max(256, T::ALIGNMENT);
}
unsafe impl<T: Zeroable> Zeroable for DynamicUniformStd140<T> {}
unsafe impl<T: Pod> Pod for DynamicUniformStd140<T> {}
#[cfg(test)]
mod test {
use super::*;
use crate::std140::{self, WriteStd140};
#[test]
fn size_is_unchanged() {
let dynamic_f32 = DynamicUniform(0.0f32);
assert_eq!(dynamic_f32.std140_size(), 0.0f32.std140_size());
}
#[test]
fn alignment_applies() {
let mut output = Vec::new();
let mut writer = std140::Writer::new(&mut output);
writer.write(&DynamicUniform(0.0f32)).unwrap();
assert_eq!(writer.len(), 4);
writer.write(&DynamicUniform(1.0f32)).unwrap();
assert_eq!(writer.len(), 260);
}
}

View file

@ -0,0 +1,151 @@
use bytemuck::{Pod, Zeroable};
use crate::std140::Std140;
unsafe impl Std140 for f32 {
const ALIGNMENT: usize = 4;
}
unsafe impl Std140 for f64 {
const ALIGNMENT: usize = 8;
}
unsafe impl Std140 for i32 {
const ALIGNMENT: usize = 4;
}
unsafe impl Std140 for u32 {
const ALIGNMENT: usize = 4;
}
macro_rules! vectors {
(
$(
#[$doc:meta] align($align:literal) $name:ident <$prim:ident> ($($field:ident),+)
)+
) => {
$(
#[$doc]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
pub struct $name {
$(pub $field: $prim,)+
}
unsafe impl Zeroable for $name {}
unsafe impl Pod for $name {}
unsafe impl Std140 for $name {
const ALIGNMENT: usize = $align;
}
)+
};
}
vectors! {
#[doc = "Corresponds to a GLSL `vec2` in std140 layout."] align(8) Vec2<f32>(x, y)
#[doc = "Corresponds to a GLSL `vec3` in std140 layout."] align(16) Vec3<f32>(x, y, z)
#[doc = "Corresponds to a GLSL `vec4` in std140 layout."] align(16) Vec4<f32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `ivec2` in std140 layout."] align(8) IVec2<i32>(x, y)
#[doc = "Corresponds to a GLSL `ivec3` in std140 layout."] align(16) IVec3<i32>(x, y, z)
#[doc = "Corresponds to a GLSL `ivec4` in std140 layout."] align(16) IVec4<i32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `uvec2` in std140 layout."] align(8) UVec2<u32>(x, y)
#[doc = "Corresponds to a GLSL `uvec3` in std140 layout."] align(16) UVec3<u32>(x, y, z)
#[doc = "Corresponds to a GLSL `uvec4` in std140 layout."] align(16) UVec4<u32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `bvec2` in std140 layout."] align(8) BVec2<bool>(x, y)
#[doc = "Corresponds to a GLSL `bvec3` in std140 layout."] align(16) BVec3<bool>(x, y, z)
#[doc = "Corresponds to a GLSL `bvec4` in std140 layout."] align(16) BVec4<bool>(x, y, z, w)
#[doc = "Corresponds to a GLSL `dvec2` in std140 layout."] align(16) DVec2<f64>(x, y)
#[doc = "Corresponds to a GLSL `dvec3` in std140 layout."] align(32) DVec3<f64>(x, y, z)
#[doc = "Corresponds to a GLSL `dvec4` in std140 layout."] align(32) DVec4<f64>(x, y, z, w)
}
macro_rules! matrices {
(
$(
#[$doc:meta]
align($align:literal)
$name:ident {
$($field:ident: $field_ty:ty,)+
}
)+
) => {
$(
#[$doc]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
pub struct $name {
$(pub $field: $field_ty,)+
}
unsafe impl Zeroable for $name {}
unsafe impl Pod for $name {}
unsafe impl Std140 for $name {
const ALIGNMENT: usize = $align;
}
)+
};
}
matrices! {
#[doc = "Corresponds to a GLSL `mat2` in std140 layout."]
align(16)
Mat2 {
x: Vec2,
_pad_x: [f32; 2],
y: Vec2,
_pad_y: [f32; 2],
}
#[doc = "Corresponds to a GLSL `mat3` in std140 layout."]
align(16)
Mat3 {
x: Vec3,
_pad_x: f32,
y: Vec3,
_pad_y: f32,
z: Vec3,
_pad_z: f32,
}
#[doc = "Corresponds to a GLSL `mat4` in std140 layout."]
align(16)
Mat4 {
x: Vec4,
y: Vec4,
z: Vec4,
w: Vec4,
}
#[doc = "Corresponds to a GLSL `dmat2` in std140 layout."]
align(16)
DMat2 {
x: DVec2,
y: DVec2,
}
#[doc = "Corresponds to a GLSL `dmat3` in std140 layout."]
align(32)
DMat3 {
x: DVec3,
_pad_x: f64,
y: DVec3,
_pad_y: f64,
z: DVec3,
_pad_z: f64,
}
#[doc = "Corresponds to a GLSL `dmat3` in std140 layout."]
align(32)
DMat4 {
x: DVec4,
y: DVec4,
z: DVec4,
w: DVec4,
}
}

View file

@ -0,0 +1,81 @@
use std::mem::size_of;
use crate::internal::align_offset;
use crate::std140::{AsStd140, Std140};
/**
Type that computes the buffer size needed by a series of `std140` types laid
out.
This type works well well when paired with `Writer`, precomputing a buffer's
size to alleviate the need to dynamically re-allocate buffers.
## Example
```glsl
struct Frob {
vec3 size;
float frobiness;
}
buffer FROBS {
uint len;
Frob[] frobs;
} frobs;
```
```
use crevice::std140::{self, AsStd140};
#[derive(AsStd140)]
struct Frob {
size: mint::Vector3<f32>,
frobiness: f32,
}
// Many APIs require that buffers contain at least enough space for all
// fixed-size bindiongs to a buffer as well as one element of any arrays, if
// there are any.
let mut sizer = std140::Sizer::new();
sizer.add::<u32>();
sizer.add::<Frob>();
# fn create_buffer_with_size(size: usize) {}
let buffer = create_buffer_with_size(sizer.len());
# assert_eq!(sizer.len(), 32);
```
*/
pub struct Sizer {
offset: usize,
}
impl Sizer {
/// Create a new `Sizer`.
pub fn new() -> Self {
Self { offset: 0 }
}
/// Add a type's necessary padding and size to the `Sizer`. Returns the
/// offset into the buffer where that type would be written.
pub fn add<T>(&mut self) -> usize
where
T: AsStd140,
{
let size = size_of::<<T as AsStd140>::Std140Type>();
let alignment = <T as AsStd140>::Std140Type::ALIGNMENT;
let padding = align_offset(self.offset, alignment);
self.offset += padding;
let write_here = self.offset;
self.offset += size;
write_here
}
/// Returns the number of bytes required to contain all the types added to
/// the `Sizer`.
pub fn len(&self) -> usize {
self.offset
}
}

View file

@ -0,0 +1,160 @@
use std::io::{self, Write};
use std::mem::size_of;
use bytemuck::{bytes_of, Pod, Zeroable};
use crate::std140::Writer;
/// Trait implemented for all `std140` primitives. Generally should not be
/// implemented outside this crate.
pub unsafe trait Std140: Copy + Zeroable + Pod {
/// The required alignment of the type. Must be a power of two.
///
/// This is distinct from the value returned by `std::mem::align_of` because
/// `AsStd140` structs do not use Rust's alignment. This enables them to
/// control and zero their padding bytes, making converting them to and from
/// slices safe.
const ALIGNMENT: usize;
/// Casts the type to a byte array. Implementors should not override this
/// method.
///
/// # Safety
/// This is always safe due to the requirements of [`bytemuck::Pod`] being a
/// prerequisite for this trait.
fn as_bytes(&self) -> &[u8] {
bytes_of(self)
}
}
/**
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`.
Types from the mint crate implement `AsStd140`, making them convenient for use
in uniform types. Most Rust geometry crates, like cgmath, nalgebra, and
ultraviolet support mint.
## Example
```glsl
uniform CAMERA {
mat4 view;
mat4 projection;
} camera;
```
```
use cgmath::prelude::*;
use cgmath::{Matrix4, Deg, perspective};
use crevice::std140::{AsStd140, Std140};
#[derive(AsStd140)]
struct CameraUniform {
view: mint::ColumnMatrix4<f32>,
projection: mint::ColumnMatrix4<f32>,
}
let camera = CameraUniform {
view: Matrix4::identity().into(),
projection: perspective(Deg(60.0), 16.0/9.0, 0.01, 100.0).into(),
};
# fn write_to_gpu_buffer(bytes: &[u8]) {}
let camera_std140 = camera.as_std140();
write_to_gpu_buffer(camera_std140.as_bytes());
```
*/
pub trait AsStd140 {
/// The `std140` version of this value.
type Std140Type: Std140;
/// Convert this value into the `std140` version of itself.
fn as_std140(&self) -> Self::Std140Type;
/// Returns the size of the `std140` version of this type. Useful for
/// pre-sizing buffers.
fn std140_size_static() -> usize {
size_of::<Self::Std140Type>()
}
}
impl<T> AsStd140 for T
where
T: Std140,
{
type Std140Type = Self;
fn as_std140(&self) -> Self {
*self
}
}
/// Trait implemented for all types that can be written into a buffer as
/// `std140` bytes. This type is more general than [`AsStd140`]: all `AsStd140`
/// types implement `WriteStd140`, but not the other way around.
///
/// While `AsStd140` requires implementers to return a type that implements the
/// `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.
pub trait WriteStd140 {
/// Writes this value into the given [`Writer`] using `std140` layout rules.
///
/// Should return the offset of the first byte of this type, as returned by
/// the first call to [`Writer::write`].
fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize>;
/// The space required to write this value using `std140` layout rules. This
/// does not include alignment padding that may be needed before or after
/// this type when written as part of a larger buffer.
fn std140_size(&self) -> usize {
let mut writer = Writer::new(io::sink());
self.write_std140(&mut writer).unwrap();
writer.len()
}
}
impl<T> WriteStd140 for T
where
T: AsStd140,
{
fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
writer.write_std140(&self.as_std140())
}
fn std140_size(&self) -> usize {
size_of::<<Self as AsStd140>::Std140Type>()
}
}
impl<T> WriteStd140 for [T]
where
T: WriteStd140,
{
fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
// if no items are written, offset is current position of the writer
let mut offset = writer.len();
let mut iter = self.iter();
if let Some(item) = iter.next() {
offset = item.write_std140(writer)?;
}
for item in iter {
item.write_std140(writer)?;
}
Ok(offset)
}
fn std140_size(&self) -> usize {
let mut writer = Writer::new(io::sink());
self.write_std140(&mut writer).unwrap();
writer.len()
}
}

View file

@ -0,0 +1,162 @@
use std::io::{self, Write};
use std::mem::size_of;
use bytemuck::bytes_of;
use crate::internal::align_offset;
use crate::std140::{AsStd140, Std140, WriteStd140};
/**
Type that enables writing correctly aligned `std140` values to a buffer.
`Writer` is useful when many values need to be laid out in a row that cannot be
represented by a struct alone, like dynamically sized arrays or dynamically
laid-out values.
## Example
In this example, we'll write a length-prefixed list of lights to a buffer.
`std140::Writer` helps align correctly, even across multiple structs, which can
be tricky and error-prone otherwise.
```glsl
struct PointLight {
vec3 position;
vec3 color;
float brightness;
};
buffer POINT_LIGHTS {
uint len;
PointLight[] lights;
} point_lights;
```
```
use crevice::std140::{self, AsStd140};
#[derive(AsStd140)]
struct PointLight {
position: mint::Vector3<f32>,
color: mint::Vector3<f32>,
brightness: f32,
}
let lights = vec![
PointLight {
position: [0.0, 1.0, 0.0].into(),
color: [1.0, 0.0, 0.0].into(),
brightness: 0.6,
},
PointLight {
position: [0.0, 4.0, 3.0].into(),
color: [1.0, 1.0, 1.0].into(),
brightness: 1.0,
},
];
# fn map_gpu_buffer_for_write() -> &'static mut [u8] {
# Box::leak(vec![0; 1024].into_boxed_slice())
# }
let target_buffer = map_gpu_buffer_for_write();
let mut writer = std140::Writer::new(target_buffer);
let light_count = lights.len() as u32;
writer.write(&light_count)?;
// Crevice will automatically insert the required padding to align the
// PointLight structure correctly. In this case, there will be 12 bytes of
// padding between the length field and the light list.
writer.write(lights.as_slice())?;
# fn unmap_gpu_buffer() {}
unmap_gpu_buffer();
# Ok::<(), std::io::Error>(())
```
*/
pub struct Writer<W> {
writer: W,
offset: usize,
}
impl<W: Write> Writer<W> {
/// Create a new `Writer`, wrapping a buffer, file, or other type that
/// implements [`std::io::Write`].
pub fn new(writer: W) -> Self {
Self { writer, offset: 0 }
}
/// Write a new value to the underlying buffer, writing zeroed padding where
/// necessary.
///
/// Returns the offset into the buffer that the value was written to.
pub fn write<T>(&mut self, value: &T) -> io::Result<usize>
where
T: WriteStd140 + ?Sized,
{
value.write_std140(self)
}
/// Write an iterator of values to the underlying buffer.
///
/// Returns the offset into the buffer that the first value was written to.
/// If no values were written, returns the `len()`.
pub fn write_iter<I, T>(&mut self, iter: I) -> io::Result<usize>
where
I: IntoIterator<Item = T>,
T: WriteStd140,
{
let mut offset = self.offset;
let mut iter = iter.into_iter();
if let Some(item) = iter.next() {
offset = item.write_std140(self)?;
}
for item in iter {
item.write_std140(self)?;
}
Ok(offset)
}
/// Write an `Std140` type to the underlying buffer.
pub fn write_std140<T>(&mut self, value: &T) -> io::Result<usize>
where
T: Std140,
{
let padding = align_offset(self.offset, T::ALIGNMENT);
for _ in 0..padding {
self.writer.write_all(&[0])?;
}
self.offset += padding;
let value = value.as_std140();
self.writer.write_all(bytes_of(&value))?;
let write_here = self.offset;
self.offset += size_of::<T>();
Ok(write_here)
}
/// Write a slice of values to the underlying buffer.
#[deprecated(
since = "0.6.0",
note = "Use `write` instead -- it now works on slices."
)]
pub fn write_slice<T>(&mut self, slice: &[T]) -> io::Result<usize>
where
T: AsStd140,
{
self.write(slice)
}
/// Returns the amount of data written by this `Writer`.
pub fn len(&self) -> usize {
self.offset
}
}

View file

@ -0,0 +1,14 @@
//! Defines traits and types for working with data adhering to GLSL's `std140`
//! layout specification.
mod primitives;
mod sizer;
mod traits;
mod writer;
pub use self::primitives::*;
pub use self::sizer::*;
pub use self::traits::*;
pub use self::writer::*;
pub use crevice_derive::AsStd430;

View file

@ -0,0 +1,143 @@
use bytemuck::{Pod, Zeroable};
use crate::std430::Std430;
unsafe impl Std430 for f32 {
const ALIGNMENT: usize = 4;
}
unsafe impl Std430 for f64 {
const ALIGNMENT: usize = 8;
}
unsafe impl Std430 for i32 {
const ALIGNMENT: usize = 4;
}
unsafe impl Std430 for u32 {
const ALIGNMENT: usize = 4;
}
macro_rules! vectors {
(
$(
#[$doc:meta] align($align:literal) $name:ident <$prim:ident> ($($field:ident),+)
)+
) => {
$(
#[$doc]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
pub struct $name {
$(pub $field: $prim,)+
}
unsafe impl Zeroable for $name {}
unsafe impl Pod for $name {}
unsafe impl Std430 for $name {
const ALIGNMENT: usize = $align;
}
)+
};
}
vectors! {
#[doc = "Corresponds to a GLSL `vec2` in std430 layout."] align(8) Vec2<f32>(x, y)
#[doc = "Corresponds to a GLSL `vec3` in std430 layout."] align(16) Vec3<f32>(x, y, z)
#[doc = "Corresponds to a GLSL `vec4` in std430 layout."] align(16) Vec4<f32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `ivec2` in std140 layout."] align(8) IVec2<i32>(x, y)
#[doc = "Corresponds to a GLSL `ivec3` in std140 layout."] align(16) IVec3<i32>(x, y, z)
#[doc = "Corresponds to a GLSL `ivec4` in std140 layout."] align(16) IVec4<i32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `uvec2` in std140 layout."] align(8) UVec2<u32>(x, y)
#[doc = "Corresponds to a GLSL `uvec3` in std140 layout."] align(16) UVec3<u32>(x, y, z)
#[doc = "Corresponds to a GLSL `uvec4` in std140 layout."] align(16) UVec4<u32>(x, y, z, w)
#[doc = "Corresponds to a GLSL `bvec2` in std140 layout."] align(8) BVec2<bool>(x, y)
#[doc = "Corresponds to a GLSL `bvec3` in std140 layout."] align(16) BVec3<bool>(x, y, z)
#[doc = "Corresponds to a GLSL `bvec4` in std140 layout."] align(16) BVec4<bool>(x, y, z, w)
#[doc = "Corresponds to a GLSL `dvec2` in std430 layout."] align(16) DVec2<f64>(x, y)
#[doc = "Corresponds to a GLSL `dvec3` in std430 layout."] align(32) DVec3<f64>(x, y, z)
#[doc = "Corresponds to a GLSL `dvec4` in std430 layout."] align(32) DVec4<f64>(x, y, z, w)
}
macro_rules! matrices {
(
$(
#[$doc:meta]
align($align:literal)
$name:ident {
$($field:ident: $field_ty:ty,)+
}
)+
) => {
$(
#[$doc]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
pub struct $name {
$(pub $field: $field_ty,)+
}
unsafe impl Zeroable for $name {}
unsafe impl Pod for $name {}
unsafe impl Std430 for $name {
const ALIGNMENT: usize = $align;
}
)+
};
}
matrices! {
#[doc = "Corresponds to a GLSL `mat2` in std430 layout."]
align(16)
Mat2 {
x: Vec2,
y: Vec2,
}
#[doc = "Corresponds to a GLSL `mat3` in std430 layout."]
align(16)
Mat3 {
x: Vec3,
y: Vec3,
z: Vec3,
}
#[doc = "Corresponds to a GLSL `mat4` in std430 layout."]
align(16)
Mat4 {
x: Vec4,
y: Vec4,
z: Vec4,
w: Vec4,
}
#[doc = "Corresponds to a GLSL `dmat2` in std430 layout."]
align(16)
DMat2 {
x: DVec2,
y: DVec2,
}
#[doc = "Corresponds to a GLSL `dmat3` in std430 layout."]
align(32)
DMat3 {
x: DVec3,
y: DVec3,
z: DVec3,
}
#[doc = "Corresponds to a GLSL `dmat3` in std430 layout."]
align(32)
DMat4 {
x: DVec4,
y: DVec4,
z: DVec4,
w: DVec4,
}
}

View file

@ -0,0 +1,81 @@
use std::mem::size_of;
use crate::internal::align_offset;
use crate::std430::{AsStd430, Std430};
/**
Type that computes the buffer size needed by a series of `std430` types laid
out.
This type works well well when paired with `Writer`, precomputing a buffer's
size to alleviate the need to dynamically re-allocate buffers.
## Example
```glsl
struct Frob {
vec3 size;
float frobiness;
}
buffer FROBS {
uint len;
Frob[] frobs;
} frobs;
```
```
use crevice::std430::{self, AsStd430};
#[derive(AsStd430)]
struct Frob {
size: mint::Vector3<f32>,
frobiness: f32,
}
// Many APIs require that buffers contain at least enough space for all
// fixed-size bindiongs to a buffer as well as one element of any arrays, if
// there are any.
let mut sizer = std430::Sizer::new();
sizer.add::<u32>();
sizer.add::<Frob>();
# fn create_buffer_with_size(size: usize) {}
let buffer = create_buffer_with_size(sizer.len());
# assert_eq!(sizer.len(), 32);
```
*/
pub struct Sizer {
offset: usize,
}
impl Sizer {
/// Create a new `Sizer`.
pub fn new() -> Self {
Self { offset: 0 }
}
/// Add a type's necessary padding and size to the `Sizer`. Returns the
/// offset into the buffer where that type would be written.
pub fn add<T>(&mut self) -> usize
where
T: AsStd430,
{
let size = size_of::<<T as AsStd430>::Std430Type>();
let alignment = <T as AsStd430>::Std430Type::ALIGNMENT;
let padding = align_offset(self.offset, alignment);
self.offset += padding;
let write_here = self.offset;
self.offset += size;
write_here
}
/// Returns the number of bytes required to contain all the types added to
/// the `Sizer`.
pub fn len(&self) -> usize {
self.offset
}
}

View file

@ -0,0 +1,159 @@
use std::io::{self, Write};
use std::mem::size_of;
use bytemuck::{bytes_of, Pod, Zeroable};
use crate::std430::Writer;
/// Trait implemented for all `std430` primitives. Generally should not be
/// implemented outside this crate.
pub unsafe trait Std430: Copy + Zeroable + Pod {
/// The required alignment of the type. Must be a power of two.
///
/// This is distinct from the value returned by `std::mem::align_of` because
/// `AsStd430` structs do not use Rust's alignment. This enables them to
/// control and zero their padding bytes, making converting them to and from
/// slices safe.
const ALIGNMENT: usize;
/// Casts the type to a byte array. Implementors should not override this
/// method.
///
/// # Safety
/// This is always safe due to the requirements of [`bytemuck::Pod`] being a
/// prerequisite for this trait.
fn as_bytes(&self) -> &[u8] {
bytes_of(self)
}
}
/**
Trait implemented for all types that can be turned into `std430` values.
This trait can often be `#[derive]`'d instead of manually implementing it. Any
struct which contains only fields that also implement `AsStd430` can derive
`AsStd430`.
Types from the mint crate implement `AsStd430`, making them convenient for use
in uniform types. Most Rust geometry crates, like cgmath, nalgebra, and
ultraviolet support mint.
## Example
```glsl
uniform CAMERA {
mat4 view;
mat4 projection;
} camera;
```
```skip
use cgmath::prelude::*;
use cgmath::{Matrix4, Deg, perspective};
use crevice::std430::{AsStd430, Std430};
#[derive(AsStd430)]
struct CameraUniform {
view: mint::ColumnMatrix4<f32>,
projection: mint::ColumnMatrix4<f32>,
}
let camera = CameraUniform {
view: Matrix4::identity().into(),
projection: perspective(Deg(60.0), 16.0/9.0, 0.01, 100.0).into(),
};
# fn write_to_gpu_buffer(bytes: &[u8]) {}
let camera_std430 = camera.as_std430();
write_to_gpu_buffer(camera_std430.as_bytes());
```
*/
pub trait AsStd430 {
/// The `std430` version of this value.
type Std430Type: Std430;
/// Convert this value into the `std430` version of itself.
fn as_std430(&self) -> Self::Std430Type;
/// Returns the size of the `std430` version of this type. Useful for
/// pre-sizing buffers.
fn std430_size_static() -> usize {
size_of::<Self::Std430Type>()
}
}
impl<T> AsStd430 for T
where
T: Std430,
{
type Std430Type = Self;
fn as_std430(&self) -> Self {
*self
}
}
/// Trait implemented for all types that can be written into a buffer as
/// `std430` bytes. This type is more general than [`AsStd430`]: all `AsStd430`
/// types implement `WriteStd430`, but not the other way around.
///
/// While `AsStd430` requires implementers to return a type that implements the
/// `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.
pub trait WriteStd430 {
/// Writes this value into the given [`Writer`] using `std430` layout rules.
///
/// Should return the offset of the first byte of this type, as returned by
/// the first call to [`Writer::write`].
fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize>;
/// The space required to write this value using `std430` layout rules. This
/// does not include alignment padding that may be needed before or after
/// this type when written as part of a larger buffer.
fn std430_size(&self) -> usize {
let mut writer = Writer::new(io::sink());
self.write_std430(&mut writer).unwrap();
writer.len()
}
}
impl<T> WriteStd430 for T
where
T: AsStd430,
{
fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
writer.write_std430(&self.as_std430())
}
fn std430_size(&self) -> usize {
size_of::<<Self as AsStd430>::Std430Type>()
}
}
impl<T> WriteStd430 for [T]
where
T: WriteStd430,
{
fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
let mut offset = writer.len();
let mut iter = self.iter();
if let Some(item) = iter.next() {
offset = item.write_std430(writer)?;
}
for item in self.iter() {
item.write_std430(writer)?;
}
Ok(offset)
}
fn std430_size(&self) -> usize {
let mut writer = Writer::new(io::sink());
self.write_std430(&mut writer).unwrap();
writer.len()
}
}

View file

@ -0,0 +1,150 @@
use std::io::{self, Write};
use std::mem::size_of;
use bytemuck::bytes_of;
use crate::internal::align_offset;
use crate::std430::{AsStd430, Std430, WriteStd430};
/**
Type that enables writing correctly aligned `std430` values to a buffer.
`Writer` is useful when many values need to be laid out in a row that cannot be
represented by a struct alone, like dynamically sized arrays or dynamically
laid-out values.
## Example
In this example, we'll write a length-prefixed list of lights to a buffer.
`std430::Writer` helps align correctly, even across multiple structs, which can
be tricky and error-prone otherwise.
```glsl
struct PointLight {
vec3 position;
vec3 color;
float brightness;
};
buffer POINT_LIGHTS {
uint len;
PointLight[] lights;
} point_lights;
```
```
use crevice::std430::{self, AsStd430};
#[derive(AsStd430)]
struct PointLight {
position: mint::Vector3<f32>,
color: mint::Vector3<f32>,
brightness: f32,
}
let lights = vec![
PointLight {
position: [0.0, 1.0, 0.0].into(),
color: [1.0, 0.0, 0.0].into(),
brightness: 0.6,
},
PointLight {
position: [0.0, 4.0, 3.0].into(),
color: [1.0, 1.0, 1.0].into(),
brightness: 1.0,
},
];
# fn map_gpu_buffer_for_write() -> &'static mut [u8] {
# Box::leak(vec![0; 1024].into_boxed_slice())
# }
let target_buffer = map_gpu_buffer_for_write();
let mut writer = std430::Writer::new(target_buffer);
let light_count = lights.len() as u32;
writer.write(&light_count)?;
// Crevice will automatically insert the required padding to align the
// PointLight structure correctly. In this case, there will be 12 bytes of
// padding between the length field and the light list.
writer.write(lights.as_slice())?;
# fn unmap_gpu_buffer() {}
unmap_gpu_buffer();
# Ok::<(), std::io::Error>(())
```
*/
pub struct Writer<W> {
writer: W,
offset: usize,
}
impl<W: Write> Writer<W> {
/// Create a new `Writer`, wrapping a buffer, file, or other type that
/// implements [`std::io::Write`].
pub fn new(writer: W) -> Self {
Self { writer, offset: 0 }
}
/// Write a new value to the underlying buffer, writing zeroed padding where
/// necessary.
///
/// Returns the offset into the buffer that the value was written to.
pub fn write<T>(&mut self, value: &T) -> io::Result<usize>
where
T: WriteStd430 + ?Sized,
{
value.write_std430(self)
}
/// Write an iterator of values to the underlying buffer.
///
/// Returns the offset into the buffer that the first value was written to.
/// If no values were written, returns the `len()`.
pub fn write_iter<I, T>(&mut self, iter: I) -> io::Result<usize>
where
I: IntoIterator<Item = T>,
T: WriteStd430,
{
let mut offset = self.offset;
let mut iter = iter.into_iter();
if let Some(item) = iter.next() {
offset = item.write_std430(self)?;
}
for item in iter {
item.write_std430(self)?;
}
Ok(offset)
}
/// Write an `Std430` type to the underlying buffer.
pub fn write_std430<T>(&mut self, value: &T) -> io::Result<usize>
where
T: Std430,
{
let padding = align_offset(self.offset, T::ALIGNMENT);
for _ in 0..padding {
self.writer.write_all(&[0])?;
}
self.offset += padding;
let value = value.as_std430();
self.writer.write_all(bytes_of(&value))?;
let write_here = self.offset;
self.offset += size_of::<T>();
Ok(write_here)
}
/// Returns the amount of data written by this `Writer`.
pub fn len(&self) -> usize {
self.offset
}
}

View file

@ -0,0 +1,16 @@
---
source: tests/std140.rs
expression: "<<MoreThan16Alignment as AsStd140>::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

View file

@ -0,0 +1,40 @@
---
source: tests/std140.rs
expression: "<<PointLight as AsStd140>::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

View file

@ -0,0 +1,24 @@
---
source: tests/std140.rs
expression: "<<PrimitiveF32 as AsStd140>::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

View file

@ -0,0 +1,24 @@
---
source: tests/std140.rs
expression: "<<UsingVec3Padding as AsStd140>::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

View file

@ -0,0 +1,24 @@
---
source: tests/std140.rs
expression: "<<TestVec3 as AsStd140>::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

View file

@ -0,0 +1,121 @@
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!(<<PrimitiveF32 as AsStd140>::Std140Type as TypeLayout>::type_layout());
assert_eq!(<PrimitiveF32 as AsStd140>::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!(<<TestVec3 as AsStd140>::Std140Type as TypeLayout>::type_layout());
assert_eq!(<TestVec3 as AsStd140>::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!(
<<UsingVec3Padding as AsStd140>::Std140Type as TypeLayout>::type_layout()
);
assert_eq!(<UsingVec3Padding as AsStd140>::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!(<<PointLight as AsStd140>::Std140Type as TypeLayout>::type_layout());
assert_eq!(<PointLight as AsStd140>::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!(
<<MoreThan16Alignment as AsStd140>::Std140Type as TypeLayout>::type_layout()
);
assert_eq!(<MoreThan16Alignment as AsStd140>::Std140Type::ALIGNMENT, 32);
}

View file

@ -7,7 +7,7 @@ use std::{
};
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(velocity_system)

View file

@ -15,7 +15,7 @@ pub struct Position(Transform);
/// This example is for performance testing purposes.
/// See https://github.com/bevyengine/bevy/pull/1492
fn main() {
App::build()
App::new()
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.insert_resource(SpriteSettings {

View file

@ -7,7 +7,7 @@ use bevy::{
};
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(star)
.run();

View file

@ -1,7 +1,7 @@
use bevy::prelude::*;
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.run();
@ -10,12 +10,12 @@ fn main() {
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
// mut materials: ResMut<Assets<ColorMaterial>>,
) {
let texture_handle = asset_server.load("branding/icon.png");
// let texture_handle = asset_server.load("branding/icon.png");
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle(SpriteBundle {
material: materials.add(texture_handle.into()),
// material: materials.add(texture_handle.into()),
..Default::default()
});
}

View file

@ -1,7 +1,7 @@
use bevy::prelude::*;
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.run();

View file

@ -1,7 +1,7 @@
use bevy::prelude::*;
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(animate_sprite_system)

View file

@ -1,7 +1,7 @@
use bevy::prelude::*;
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(animate_translation)

View file

@ -3,7 +3,7 @@ use bevy::{asset::LoadState, prelude::*, sprite::TextureAtlasBuilder};
/// In this example we generate a new texture atlas (sprite sheet) from a folder containing
/// individual sprites
fn main() {
App::build()
App::new()
.init_resource::<RpgSpriteHandles>()
.add_plugins(DefaultPlugins)
.add_state(AppState::Setup)

View file

@ -1,7 +1,7 @@
use bevy::prelude::*;
fn main() {
App::build()
App::new()
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())

View file

@ -1,7 +1,7 @@
use bevy::{pbr::AmbientLight, prelude::*};
fn main() {
App::build()
App::new()
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 1.0 / 5.0f32,

View file

@ -5,7 +5,7 @@ use bevy::prelude::*;
/// range should generally be somewhere between 1 (no multi sampling, but cheap) to 8 (crisp but
/// expensive)
fn main() {
App::build()
App::new()
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())

View file

@ -1,7 +1,7 @@
use bevy::prelude::*;
fn main() {
App::build()
App::new()
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())

View file

@ -3,7 +3,7 @@ use bevy::prelude::*;
/// This example illustrates how to create parent->child relationships between entities how parent
/// transforms are propagated to their descendants
fn main() {
App::build()
App::new()
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())

View file

@ -2,7 +2,7 @@ use bevy::prelude::*;
/// This example shows how to configure Physically Based Rendering (PBR) parameters.
fn main() {
App::build()
App::new()
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())

View file

@ -13,7 +13,7 @@ use rand::{rngs::StdRng, Rng, SeedableRng};
/// NOTE: Bevy still has a number of optimizations to do in this area. Expect the
/// performance here to go way up in the future
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default())

View file

@ -2,7 +2,7 @@ use bevy::prelude::*;
/// This example shows various ways to configure texture materials in 3D
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.run();

View file

@ -1,7 +1,7 @@
use bevy::{prelude::*, scene::InstanceId};
fn main() {
App::build()
App::new()
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.insert_resource(SceneInstance::default())

View file

@ -5,7 +5,7 @@ use bevy::{
};
fn main() {
App::build()
App::new()
.insert_resource(Msaa { samples: 4 })
.insert_resource(WgpuOptions {
features: WgpuFeatures {

View file

@ -9,7 +9,7 @@ use bevy::{
/// This example visualizes camera z-ordering by setting the material of rotating cubes to their
/// distance from the camera
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.add_system(rotator_system.system())

View file

@ -3,7 +3,7 @@ use bevy::prelude::*;
// the `bevy_main` proc_macro generates the required android boilerplate
#[bevy_main]
fn main() {
App::build()
App::new()
.insert_resource(Msaa { samples: 2 })
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())

View file

@ -21,7 +21,7 @@ fn print_system(input: Res<Input>) {
}
fn main() {
App::build()
App::new()
.insert_resource(Input(String::new()))
.set_runner(my_runner)
.add_system(print_system.system())

View file

@ -1,7 +1,7 @@
use bevy::prelude::*;
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
.add_system(file_drag_and_drop_system.system())
.run();

View file

@ -1,5 +1,5 @@
use bevy::prelude::*;
fn main() {
App::build().run();
App::new().run();
}

View file

@ -1,5 +1,5 @@
use bevy::prelude::*;
fn main() {
App::build().add_plugins(DefaultPlugins).run();
App::new().add_plugins(DefaultPlugins).run();
}

View file

@ -10,14 +10,14 @@ use bevy::{app::ScheduleRunnerSettings, prelude::*, utils::Duration};
fn main() {
// this app runs once
App::build()
App::new()
.insert_resource(ScheduleRunnerSettings::run_once())
.add_plugins(MinimalPlugins)
.add_system(hello_world_system.system())
.run();
// this app loops forever at 60 fps
App::build()
App::new()
.insert_resource(ScheduleRunnerSettings::run_loop(Duration::from_secs_f64(
1.0 / 60.0,
)))

View file

@ -2,7 +2,7 @@ use bevy::prelude::*;
/// This example illustrates how to use logs in bevy
fn main() {
App::build()
App::new()
// Uncomment this to override the default log settings:
// .insert_resource(bevy::log::LogSettings {
// level: bevy::log::Level::TRACE,

View file

@ -4,7 +4,7 @@ use bevy::{prelude::*, utils::Duration};
/// that provide a specific piece of functionality (generally the smaller the scope, the better).
/// This example illustrates how to create a simple plugin that prints out a message.
fn main() {
App::build()
App::new()
.add_plugins(DefaultPlugins)
// plugins are registered as part of the "app building" process
.add_plugin(PrintMessagePlugin {
@ -23,7 +23,7 @@ pub struct PrintMessagePlugin {
impl Plugin for PrintMessagePlugin {
// this is where we set up our plugin
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
let state = PrintMessageState {
message: self.message.clone(),
timer: Timer::new(self.wait_duration, true),

Some files were not shown because too many files have changed in this diff Show more