mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
pipelined rendering proof of concept
This commit is contained in:
parent
a4e5e2790e
commit
4ac2ed7cc6
250 changed files with 16397 additions and 757 deletions
13
Cargo.toml
13
Cargo.toml
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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>()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>>()
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
36
crates/bevy_ecs/src/system/param_state.rs
Normal file
36
crates/bevy_ecs/src/system/param_state.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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"]}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>>()
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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>>()
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
|
|
39
crates/crevice/CHANGELOG.md
Normal file
39
crates/crevice/CHANGELOG.md
Normal 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
27
crates/crevice/Cargo.toml
Normal 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"] }
|
201
crates/crevice/LICENSE-APACHE
Normal file
201
crates/crevice/LICENSE-APACHE
Normal 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.
|
19
crates/crevice/LICENSE-MIT
Normal file
19
crates/crevice/LICENSE-MIT
Normal 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
135
crates/crevice/README.md
Normal 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
15
crates/crevice/README.tpl
Normal 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.
|
78
crates/crevice/src/glam.rs
Normal file
78
crates/crevice/src/glam.rs
Normal 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)
|
||||
}
|
23
crates/crevice/src/internal.rs
Normal file
23
crates/crevice/src/internal.rs
Normal 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
142
crates/crevice/src/lib.rs
Normal 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;
|
78
crates/crevice/src/mint.rs
Normal file
78
crates/crevice/src/mint.rs
Normal 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),
|
||||
}
|
16
crates/crevice/src/std140.rs
Normal file
16
crates/crevice/src/std140.rs
Normal 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;
|
56
crates/crevice/src/std140/dynamic_uniform.rs
Normal file
56
crates/crevice/src/std140/dynamic_uniform.rs
Normal 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);
|
||||
}
|
||||
}
|
151
crates/crevice/src/std140/primitives.rs
Normal file
151
crates/crevice/src/std140/primitives.rs
Normal 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,
|
||||
}
|
||||
}
|
81
crates/crevice/src/std140/sizer.rs
Normal file
81
crates/crevice/src/std140/sizer.rs
Normal 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
|
||||
}
|
||||
}
|
160
crates/crevice/src/std140/traits.rs
Normal file
160
crates/crevice/src/std140/traits.rs
Normal 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()
|
||||
}
|
||||
}
|
162
crates/crevice/src/std140/writer.rs
Normal file
162
crates/crevice/src/std140/writer.rs
Normal 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
|
||||
}
|
||||
}
|
14
crates/crevice/src/std430.rs
Normal file
14
crates/crevice/src/std430.rs
Normal 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;
|
143
crates/crevice/src/std430/primitives.rs
Normal file
143
crates/crevice/src/std430/primitives.rs
Normal 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,
|
||||
}
|
||||
}
|
81
crates/crevice/src/std430/sizer.rs
Normal file
81
crates/crevice/src/std430/sizer.rs
Normal 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
|
||||
}
|
||||
}
|
159
crates/crevice/src/std430/traits.rs
Normal file
159
crates/crevice/src/std430/traits.rs
Normal 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()
|
||||
}
|
||||
}
|
150
crates/crevice/src/std430/writer.rs
Normal file
150
crates/crevice/src/std430/writer.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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
|
40
crates/crevice/tests/snapshots/std140__point_light.snap
Normal file
40
crates/crevice/tests/snapshots/std140__point_light.snap
Normal 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
|
24
crates/crevice/tests/snapshots/std140__primitive_f32.snap
Normal file
24
crates/crevice/tests/snapshots/std140__primitive_f32.snap
Normal 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
|
|
@ -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
|
24
crates/crevice/tests/snapshots/std140__vec3.snap
Normal file
24
crates/crevice/tests/snapshots/std140__vec3.snap
Normal 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
|
121
crates/crevice/tests/std140.rs
Normal file
121
crates/crevice/tests/std140.rs
Normal 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);
|
||||
}
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
};
|
||||
|
||||
fn main() {
|
||||
App::build()
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup)
|
||||
.add_system(velocity_system)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -7,7 +7,7 @@ use bevy::{
|
|||
};
|
||||
|
||||
fn main() {
|
||||
App::build()
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(star)
|
||||
.run();
|
||||
|
|
|
@ -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()
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::build()
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::build()
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup)
|
||||
.add_system(animate_translation)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -5,7 +5,7 @@ use bevy::{
|
|||
};
|
||||
|
||||
fn main() {
|
||||
App::build()
|
||||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.insert_resource(WgpuOptions {
|
||||
features: WgpuFeatures {
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::build().run();
|
||||
App::new().run();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::build().add_plugins(DefaultPlugins).run();
|
||||
App::new().add_plugins(DefaultPlugins).run();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue