bevy_render now uses wgpu directly

This commit is contained in:
Carter Anderson 2021-06-21 16:28:52 -07:00
parent 01116b1fdb
commit 13ca00178a
105 changed files with 2234 additions and 6710 deletions

View file

@ -26,7 +26,6 @@ default = [
"bevy_gilrs",
"bevy_gltf",
"bevy_wgpu",
"bevy_wgpu2",
"bevy_sprite2",
"bevy_render2",
"bevy_pbr2",
@ -52,7 +51,6 @@ 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"]
bevy_pbr2 = ["bevy_internal/bevy_pbr2"]

View file

@ -187,22 +187,24 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
let queries = get_idents(|i| format!("Q{}", i), max_queries);
let filters = get_idents(|i| format!("F{}", i), max_queries);
let lifetimes = get_lifetimes(|i| format!("'q{}", i), max_queries);
let state_lifetimes = get_lifetimes(|i| format!("'qs{}", i), max_queries);
let mut query_fns = Vec::new();
let mut query_fn_muts = Vec::new();
for i in 0..max_queries {
let query = &queries[i];
let filter = &filters[i];
let lifetime = &lifetimes[i];
let state_lifetime = &state_lifetimes[i];
let fn_name = Ident::new(&format!("q{}", i), Span::call_site());
let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site());
let index = Index::from(i);
query_fns.push(quote! {
pub fn #fn_name(&self) -> &Query<#lifetime, #query, #filter> {
pub fn #fn_name(&self) -> &Query<#lifetime, #state_lifetime, #query, #filter> {
&self.0.#index
}
});
query_fn_muts.push(quote! {
pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #query, #filter> {
pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #state_lifetime, #query, #filter> {
&mut self.0.#index
}
});
@ -212,10 +214,11 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
let query = &queries[0..query_count];
let filter = &filters[0..query_count];
let lifetime = &lifetimes[0..query_count];
let state_lifetime = &state_lifetimes[0..query_count];
let query_fn = &query_fns[0..query_count];
let query_fn_mut = &query_fn_muts[0..query_count];
tokens.extend(TokenStream::from(quote! {
impl<#(#lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, #query, #filter>,)*)>
impl<'s, #(#lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, 's, #query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
{
type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>;
@ -268,16 +271,16 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
fn default_config() {}
}
impl<'a, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'a> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
impl<'s, 'w, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'s, 'w> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
{
type Item = QuerySet<(#(Query<'a, #query, #filter>,)*)>;
type Item = QuerySet<(#(Query<'w, 's, #query, #filter>,)*)>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
let (#(#query,)*) = &state.0;
@ -285,7 +288,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
}
}
impl<#(#lifetime,)* #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<(#(Query<#lifetime, #query, #filter>,)*)>
impl<#(#state_lifetime,)* #(#lifetime,)* #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<(#(Query<#lifetime, #state_lifetime, #query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
{
#(#query_fn)*
@ -413,12 +416,12 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
}
}
impl #impl_generics #path::system::SystemParamFetch<'a> for #fetch_struct_name <(#(<#field_types as SystemParam>::Fetch,)*), #punctuated_generic_idents> {
impl #impl_generics #path::system::SystemParamFetch<'s, 'w> for #fetch_struct_name <(#(<#field_types as SystemParam>::Fetch,)*), #punctuated_generic_idents> {
type Item = #struct_name#ty_generics;
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &#path::system::SystemMeta,
world: &'a #path::world::World,
world: &'w #path::world::World,
change_tick: u32,
) -> Self::Item {
#struct_name {

View file

@ -151,18 +151,21 @@ fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
/// Reads events of type `T` in order and tracks which events have already been read.
#[derive(SystemParam)]
pub struct EventReader<'a, T: Component> {
last_event_count: Local<'a, (usize, PhantomData<T>)>,
events: Res<'a, Events<T>>,
pub struct EventReader<'s, 'w, T: Component> {
last_event_count: Local<'s, (usize, PhantomData<T>)>,
events: Res<'w, Events<T>>,
}
/// Sends events of type `T`.
#[derive(SystemParam)]
pub struct EventWriter<'a, T: Component> {
events: ResMut<'a, Events<T>>,
pub struct EventWriter<'s, 'w, T: Component> {
events: ResMut<'w, Events<T>>,
// TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
#[system_param(ignore)]
marker: PhantomData<&'s usize>,
}
impl<'a, T: Component> EventWriter<'a, T> {
impl<'s, 'w, T: Component> EventWriter<'s, 'w, T> {
pub fn send(&mut self, event: T) {
self.events.send(event);
}
@ -252,7 +255,7 @@ fn internal_event_reader<'a, T>(
}
}
impl<'a, T: Component> EventReader<'a, T> {
impl<'s, 'w, T: Component> EventReader<'s, 'w, T> {
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened
/// before now.

View file

@ -41,11 +41,11 @@ use std::{
///
/// [`Or`]: crate::query::Or
pub trait WorldQuery {
type Fetch: for<'a> Fetch<'a, State = Self::State>;
type Fetch: for<'world, 'state> Fetch<'world, 'state, State = Self::State>;
type State: FetchState;
}
pub trait Fetch<'w>: Sized {
pub trait Fetch<'world, 'state>: Sized {
type Item;
type State: FetchState;
@ -173,7 +173,7 @@ unsafe impl FetchState for EntityState {
}
}
impl<'w> Fetch<'w> for EntityFetch {
impl<'w, 's> Fetch<'w, 's> for EntityFetch {
type Item = Entity;
type State = EntityState;
@ -296,7 +296,7 @@ impl<T> Clone for ReadFetch<T> {
/// SAFETY: access is read only
unsafe impl<T> ReadOnlyFetch for ReadFetch<T> {}
impl<'w, T: Component> Fetch<'w> for ReadFetch<T> {
impl<'w, 's, T: Component> Fetch<'w, 's> for ReadFetch<T> {
type Item = &'w T;
type State = ReadState<T>;
@ -459,7 +459,7 @@ unsafe impl<T: Component> FetchState for WriteState<T> {
}
}
impl<'w, T: Component> Fetch<'w> for WriteFetch<T> {
impl<'w, 's, T: Component> Fetch<'w, 's> for WriteFetch<T> {
type Item = Mut<'w, T>;
type State = WriteState<T>;
@ -619,7 +619,7 @@ unsafe impl<T: FetchState> FetchState for OptionState<T> {
}
}
impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch<T> {
impl<'w, 's, T: Fetch<'w, 's>> Fetch<'w, 's> for OptionFetch<T> {
type Item = Option<T::Item>;
type State = OptionState<T::State>;
@ -810,7 +810,7 @@ pub struct ChangeTrackersFetch<T> {
/// SAFETY: access is read only
unsafe impl<T> ReadOnlyFetch for ChangeTrackersFetch<T> {}
impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<T> {
impl<'w, 's, T: Component> Fetch<'w, 's> for ChangeTrackersFetch<T> {
type Item = ChangeTrackers<T>;
type State = ChangeTrackersState<T>;
@ -913,7 +913,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<T> {
macro_rules! impl_tuple_fetch {
($(($name: ident, $state: ident)),*) => {
#[allow(non_snake_case)]
impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) {
impl<'w, 's, $($name: Fetch<'w, 's>),*> Fetch<'w, 's> for ($($name,)*) {
type Item = ($($name::Item,)*);
type State = ($($name::State,)*);

View file

@ -12,7 +12,7 @@ use std::{cell::UnsafeCell, marker::PhantomData, ptr};
/// Extension trait for [`Fetch`] containing methods used by query filters.
/// This trait exists to allow "short circuit" behaviors for relevant query filter fetches.
pub trait FilterFetch: for<'a> Fetch<'a> {
pub trait FilterFetch: for<'world, 'state> Fetch<'world, 'state> {
/// # Safety
///
/// Must always be called _after_ [`Fetch::set_archetype`]. `archetype_index` must be in the range
@ -28,7 +28,7 @@ pub trait FilterFetch: for<'a> Fetch<'a> {
impl<T> FilterFetch for T
where
T: for<'a> Fetch<'a, Item = bool>,
T: for<'w, 's> Fetch<'w, 's, Item = bool>,
{
#[inline]
unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool {
@ -119,7 +119,7 @@ unsafe impl<T: Component> FetchState for WithState<T> {
}
}
impl<'a, T: Component> Fetch<'a> for WithFetch<T> {
impl<'w, 's, T: Component> Fetch<'w, 's> for WithFetch<T> {
type Item = bool;
type State = WithState<T>;
@ -238,7 +238,7 @@ unsafe impl<T: Component> FetchState for WithoutState<T> {
}
}
impl<'a, T: Component> Fetch<'a> for WithoutFetch<T> {
impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch<T> {
type Item = bool;
type State = WithoutState<T>;
@ -338,7 +338,7 @@ unsafe impl<T: Bundle> FetchState for WithBundleState<T> {
}
}
impl<'a, T: Bundle> Fetch<'a> for WithBundleFetch<T> {
impl<'w, 's, T: Bundle> Fetch<'w, 's> for WithBundleFetch<T> {
type Item = bool;
type State = WithBundleState<T>;
@ -446,8 +446,8 @@ macro_rules! impl_query_filter_tuple {
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<'a, $($filter: FilterFetch),*> Fetch<'a> for Or<($(OrFetch<$filter>,)*)> {
type State = Or<($(<$filter as Fetch<'a>>::State,)*)>;
impl<'w, 's, $($filter: FilterFetch),*> Fetch<'w, 's> for Or<($(OrFetch<$filter>,)*)> {
type State = Or<($(<$filter as Fetch<'w, 's>>::State,)*)>;
type Item = bool;
unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self {
@ -612,7 +612,7 @@ macro_rules! impl_tick_filter {
}
}
impl<'a, T: Component> Fetch<'a> for $fetch_name<T> {
impl<'w, 's, T: Component> Fetch<'w, 's> for $fetch_name<T> {
type State = $state_name<T>;
type Item = bool;

View file

@ -4,7 +4,7 @@ use crate::{
storage::{TableId, Tables},
world::World,
};
use std::mem::MaybeUninit;
use std::{marker::PhantomData, mem::MaybeUninit};
/// An [`Iterator`] over query results of a [`Query`](crate::system::Query).
///
@ -131,7 +131,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Iterator for QueryIter<'w, 's, Q, F>
where
F::Fetch: FilterFetch,
{
type Item = <Q::Fetch as Fetch<'w>>::Item;
type Item = <Q::Fetch as Fetch<'w, 's>>::Item;
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
@ -217,7 +217,7 @@ where
archetypes: &'w Archetypes,
query_state: &'s QueryState<Q, F>,
world: &'w World,
cursors: [QueryIterationCursor<'s, Q, F>; K],
cursors: [QueryIterationCursor<'w, 's, Q, F>; K],
}
impl<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> QueryCombinationIter<'w, 's, Q, F, K>
@ -239,7 +239,7 @@ where
// There is no FromIterator on arrays, so instead initialize it manually with MaybeUninit
// TODO: use MaybeUninit::uninit_array if it stabilizes
let mut cursors: [MaybeUninit<QueryIterationCursor<'s, Q, F>>; K] =
let mut cursors: [MaybeUninit<QueryIterationCursor<'w, 's, Q, F>>; K] =
MaybeUninit::uninit().assume_init();
for (i, cursor) in cursors.iter_mut().enumerate() {
match i {
@ -259,8 +259,8 @@ where
}
// TODO: use MaybeUninit::array_assume_init if it stabilizes
let cursors: [QueryIterationCursor<'s, Q, F>; K] =
(&cursors as *const _ as *const [QueryIterationCursor<'s, Q, F>; K]).read();
let cursors: [QueryIterationCursor<'w, 's, Q, F>; K] =
(&cursors as *const _ as *const [QueryIterationCursor<'w, 's, Q, F>; K]).read();
QueryCombinationIter {
world,
@ -277,9 +277,9 @@ where
/// references to the same component, leading to unique reference aliasing.
///.
/// It is always safe for shared access.
unsafe fn fetch_next_aliased_unchecked<'a>(
unsafe fn fetch_next_aliased_unchecked(
&mut self,
) -> Option<[<Q::Fetch as Fetch<'a>>::Item; K]>
) -> Option<[<Q::Fetch as Fetch<'w, 's>>::Item; K]>
where
Q::Fetch: Clone,
F::Fetch: Clone,
@ -313,7 +313,7 @@ where
}
// TODO: use MaybeUninit::uninit_array if it stabilizes
let mut values: [MaybeUninit<<Q::Fetch as Fetch<'a>>::Item>; K] =
let mut values: [MaybeUninit<<Q::Fetch as Fetch<'w, 's>>::Item>; K] =
MaybeUninit::uninit().assume_init();
for (value, cursor) in values.iter_mut().zip(&mut self.cursors) {
@ -321,15 +321,15 @@ where
}
// TODO: use MaybeUninit::array_assume_init if it stabilizes
let values: [<Q::Fetch as Fetch<'a>>::Item; K] =
(&values as *const _ as *const [<Q::Fetch as Fetch<'a>>::Item; K]).read();
let values: [<Q::Fetch as Fetch<'w, 's>>::Item; K] =
(&values as *const _ as *const [<Q::Fetch as Fetch<'w, 's>>::Item; K]).read();
Some(values)
}
/// Get next combination of queried components
#[inline]
pub fn fetch_next(&mut self) -> Option<[<Q::Fetch as Fetch<'_>>::Item; K]>
pub fn fetch_next(&mut self) -> Option<[<Q::Fetch as Fetch<'w, 's>>::Item; K]>
where
Q::Fetch: Clone,
F::Fetch: Clone,
@ -350,7 +350,7 @@ where
Q::Fetch: Clone + ReadOnlyFetch,
F::Fetch: Clone + FilterFetch + ReadOnlyFetch,
{
type Item = [<Q::Fetch as Fetch<'w>>::Item; K];
type Item = [<Q::Fetch as Fetch<'w, 's>>::Item; K];
#[inline]
fn next(&mut self) -> Option<Self::Item> {
@ -404,7 +404,7 @@ impl<'w, 's, Q: WorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, ()> {
}
}
struct QueryIterationCursor<'s, Q: WorldQuery, F: WorldQuery> {
struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> {
table_id_iter: std::slice::Iter<'s, TableId>,
archetype_id_iter: std::slice::Iter<'s, ArchetypeId>,
fetch: Q::Fetch,
@ -412,9 +412,10 @@ struct QueryIterationCursor<'s, Q: WorldQuery, F: WorldQuery> {
current_len: usize,
current_index: usize,
is_dense: bool,
marker: PhantomData<&'w usize>,
}
impl<'s, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'s, Q, F>
impl<'w, 's, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'w, 's, Q, F>
where
Q::Fetch: Clone,
F::Fetch: Clone,
@ -428,11 +429,12 @@ where
current_len: self.current_len,
current_index: self.current_index,
is_dense: self.is_dense,
marker: PhantomData,
}
}
}
impl<'s, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'s, Q, F>
impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F>
where
F::Fetch: FilterFetch,
{
@ -475,12 +477,13 @@ where
archetype_id_iter: query_state.matched_archetype_ids.iter(),
current_len: 0,
current_index: 0,
marker: PhantomData,
}
}
/// retrieve item returned from most recent `next` call again.
#[inline]
unsafe fn peek_last<'w>(&mut self) -> Option<<Q::Fetch as Fetch<'w>>::Item> {
unsafe fn peek_last(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
if self.current_index > 0 {
if self.is_dense {
Some(self.fetch.table_fetch(self.current_index - 1))
@ -496,12 +499,12 @@ where
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
// We can't currently reuse QueryIterationCursor in QueryIter for performance reasons. See #1763 for context.
#[inline(always)]
unsafe fn next<'w>(
unsafe fn next(
&mut self,
tables: &'w Tables,
archetypes: &'w Archetypes,
query_state: &'s QueryState<Q, F>,
) -> Option<<Q::Fetch as Fetch<'w>>::Item> {
) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
if self.is_dense {
loop {
if self.current_index == self.current_len {

View file

@ -122,11 +122,11 @@ where
}
#[inline]
pub fn get<'w>(
&mut self,
pub fn get<'w, 's>(
&'s mut self,
world: &'w World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w>>::Item, QueryEntityError>
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError>
where
Q::Fetch: ReadOnlyFetch,
{
@ -135,11 +135,11 @@ where
}
#[inline]
pub fn get_manual<'w>(
&self,
pub fn get_manual<'w, 's>(
&'s self,
world: &'w World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w>>::Item, QueryEntityError>
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError>
where
Q::Fetch: ReadOnlyFetch,
{
@ -156,11 +156,11 @@ where
}
#[inline]
pub fn get_mut<'w>(
&mut self,
pub fn get_mut<'w, 's>(
&'s mut self,
world: &'w mut World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w>>::Item, QueryEntityError> {
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
// SAFETY: query has unique world access
unsafe { self.get_unchecked(world, entity) }
}
@ -170,11 +170,11 @@ where
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
#[inline]
pub unsafe fn get_unchecked<'w>(
&mut self,
pub unsafe fn get_unchecked<'w, 's>(
&'s mut self,
world: &'w World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w>>::Item, QueryEntityError> {
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
self.update_archetypes(world);
self.get_unchecked_manual(
world,
@ -187,13 +187,13 @@ where
/// # Safety
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
pub unsafe fn get_unchecked_manual<'w>(
&self,
pub unsafe fn get_unchecked_manual<'w, 's>(
&'s self,
world: &'w World,
entity: Entity,
last_change_tick: u32,
change_tick: u32,
) -> Result<<Q::Fetch as Fetch<'w>>::Item, QueryEntityError> {
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
let location = world
.entities
.get(entity)
@ -328,10 +328,10 @@ where
}
#[inline]
pub fn for_each<'w>(
&mut self,
pub fn for_each<'w, 's>(
&'s mut self,
world: &'w World,
func: impl FnMut(<Q::Fetch as Fetch<'w>>::Item),
func: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item),
) where
Q::Fetch: ReadOnlyFetch,
{
@ -342,10 +342,10 @@ where
}
#[inline]
pub fn for_each_mut<'w>(
&mut self,
pub fn for_each_mut<'w, 's>(
&'s mut self,
world: &'w mut World,
func: impl FnMut(<Q::Fetch as Fetch<'w>>::Item),
func: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item),
) {
// SAFETY: query has unique world access
unsafe {
@ -358,10 +358,10 @@ where
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
#[inline]
pub unsafe fn for_each_unchecked<'w>(
&mut self,
pub unsafe fn for_each_unchecked<'w, 's>(
&'s mut self,
world: &'w World,
func: impl FnMut(<Q::Fetch as Fetch<'w>>::Item),
func: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item),
) {
self.update_archetypes(world);
self.for_each_unchecked_manual(
@ -373,12 +373,12 @@ where
}
#[inline]
pub fn par_for_each<'w>(
&mut self,
pub fn par_for_each<'w, 's>(
&'s mut self,
world: &'w World,
task_pool: &TaskPool,
batch_size: usize,
func: impl Fn(<Q::Fetch as Fetch<'w>>::Item) + Send + Sync + Clone,
func: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
) where
Q::Fetch: ReadOnlyFetch,
{
@ -389,12 +389,12 @@ where
}
#[inline]
pub fn par_for_each_mut<'w>(
&mut self,
pub fn par_for_each_mut<'w, 's>(
&'s mut self,
world: &'w mut World,
task_pool: &TaskPool,
batch_size: usize,
func: impl Fn(<Q::Fetch as Fetch<'w>>::Item) + Send + Sync + Clone,
func: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
) {
// SAFETY: query has unique world access
unsafe {
@ -407,12 +407,12 @@ where
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
#[inline]
pub unsafe fn par_for_each_unchecked<'w>(
&mut self,
pub unsafe fn par_for_each_unchecked<'w, 's>(
&'s mut self,
world: &'w World,
task_pool: &TaskPool,
batch_size: usize,
func: impl Fn(<Q::Fetch as Fetch<'w>>::Item) + Send + Sync + Clone,
func: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
) {
self.update_archetypes(world);
self.par_for_each_unchecked_manual(
@ -434,7 +434,7 @@ where
pub(crate) unsafe fn for_each_unchecked_manual<'w, 's>(
&'s self,
world: &'w World,
mut func: impl FnMut(<Q::Fetch as Fetch<'w>>::Item),
mut func: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item),
last_change_tick: u32,
change_tick: u32,
) {
@ -488,7 +488,7 @@ where
world: &'w World,
task_pool: &TaskPool,
batch_size: usize,
func: impl Fn(<Q::Fetch as Fetch<'w>>::Item) + Send + Sync + Clone,
func: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
last_change_tick: u32,
change_tick: u32,
) {

View file

@ -16,14 +16,14 @@ pub trait Command: Send + Sync + 'static {
}
/// A list of commands that will be run to modify a [`World`].
pub struct Commands<'a> {
queue: &'a mut CommandQueue,
entities: &'a Entities,
pub struct Commands<'s, 'w> {
queue: &'s mut CommandQueue,
entities: &'w Entities,
}
impl<'a> Commands<'a> {
impl<'s, 'w> Commands<'s, 'w> {
/// Create a new `Commands` from a queue and a world.
pub fn new(queue: &'a mut CommandQueue, world: &'a World) -> Self {
pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self {
Self {
queue,
entities: world.entities(),
@ -50,7 +50,7 @@ impl<'a> Commands<'a> {
/// }
/// # example_system.system();
/// ```
pub fn spawn(&mut self) -> EntityCommands<'a, '_> {
pub fn spawn(&mut self) -> EntityCommands<'s, 'w, '_> {
let entity = self.entities.reserve_entity();
EntityCommands {
entity,
@ -58,7 +58,7 @@ impl<'a> Commands<'a> {
}
}
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'a, '_> {
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'s, 'w, '_> {
self.add(GetOrSpawn { entity });
EntityCommands {
entity,
@ -113,7 +113,7 @@ impl<'a> Commands<'a> {
/// }
/// # example_system.system();
/// ```
pub fn spawn_bundle<'b, T: Bundle>(&'b mut self, bundle: T) -> EntityCommands<'a, 'b> {
pub fn spawn_bundle<T: Bundle>(&mut self, bundle: T) -> EntityCommands<'s, 'w, '_> {
let mut e = self.spawn();
e.insert_bundle(bundle);
e
@ -138,7 +138,7 @@ impl<'a> Commands<'a> {
/// }
/// # example_system.system();
/// ```
pub fn entity(&mut self, entity: Entity) -> EntityCommands<'a, '_> {
pub fn entity(&mut self, entity: Entity) -> EntityCommands<'s, 'w, '_> {
EntityCommands {
entity,
commands: self,
@ -174,12 +174,12 @@ impl<'a> Commands<'a> {
}
/// A list of commands that will be run to modify an [`Entity`].
pub struct EntityCommands<'a, 'b> {
pub struct EntityCommands<'s, 'w, 'a> {
entity: Entity,
commands: &'b mut Commands<'a>,
commands: &'a mut Commands<'s, 'w>,
}
impl<'a, 'b> EntityCommands<'a, 'b> {
impl<'s, 'w, 'a> EntityCommands<'s, 'w, 'a> {
/// Retrieves the current entity's unique [`Entity`] id.
#[inline]
pub fn id(&self) -> Entity {
@ -267,7 +267,7 @@ impl<'a, 'b> EntityCommands<'a, 'b> {
}
/// Returns the underlying `[Commands]`.
pub fn commands(&mut self) -> &mut Commands<'a> {
pub fn commands(&mut self) -> &mut Commands<'s, 'w> {
self.commands
}
}

View file

@ -87,7 +87,7 @@ impl<Param: SystemParam> SystemState<Param> {
/// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
#[inline]
pub fn get<'a>(&'a mut self, world: &'a World) -> <Param::Fetch as SystemParamFetch<'a>>::Item
pub fn get<'s, 'w>(&'s mut self, world: &'w World) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item
where
Param::Fetch: ReadOnlySystemParamFetch,
{
@ -98,10 +98,10 @@ impl<Param: SystemParam> SystemState<Param> {
/// Retrieve the mutable [`SystemParam`] values.
#[inline]
pub fn get_mut<'a>(
&'a mut self,
world: &'a mut World,
) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
pub fn get_mut<'s, 'w>(
&'s mut self,
world: &'w mut World,
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item {
self.validate_world_and_update_archetypes(world);
// SAFE: World is uniquely borrowed and matches the World this SystemState was created with.
unsafe { self.get_unchecked_manual(world) }
@ -142,10 +142,10 @@ impl<Param: SystemParam> SystemState<Param> {
/// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
/// created with.
#[inline]
pub unsafe fn get_unchecked_manual<'a>(
&'a mut self,
world: &'a World,
) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
pub unsafe fn get_unchecked_manual<'s, 'w>(
&'s mut self,
world: &'w World,
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item {
let change_tick = world.increment_change_tick();
let param = <Param::Fetch as SystemParamFetch>::get_param(
&mut self.param_state,

View file

@ -107,17 +107,17 @@ use thiserror::Error;
///
/// This touches all the basics of queries, make sure to check out all the [`WorldQueries`](WorldQuery)
/// bevy has to offer.
pub struct Query<'w, Q: WorldQuery, F: WorldQuery = ()>
pub struct Query<'w, 's, Q: WorldQuery, F: WorldQuery = ()>
where
F::Fetch: FilterFetch,
{
pub(crate) world: &'w World,
pub(crate) state: &'w QueryState<Q, F>,
pub(crate) state: &'s QueryState<Q, F>,
pub(crate) last_change_tick: u32,
pub(crate) change_tick: u32,
}
impl<'w, Q: WorldQuery, F: WorldQuery> Query<'w, Q, F>
impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F>
where
F::Fetch: FilterFetch,
{
@ -130,7 +130,7 @@ where
#[inline]
pub(crate) unsafe fn new(
world: &'w World,
state: &'w QueryState<Q, F>,
state: &'s QueryState<Q, F>,
last_change_tick: u32,
change_tick: u32,
) -> Self {
@ -146,7 +146,7 @@ where
///
/// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries.
#[inline]
pub fn iter(&self) -> QueryIter<'_, '_, Q, F>
pub fn iter(&self) -> QueryIter<'w, 's, Q, F>
where
Q::Fetch: ReadOnlyFetch,
{
@ -166,7 +166,7 @@ where
/// - if K < N: all possible K-sized combinations of query results, without repetition
/// - if K > N: empty set (no K-sized combinations exist)
#[inline]
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'_, '_, Q, F, K>
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'w, 's, Q, F, K>
where
Q::Fetch: ReadOnlyFetch,
{
@ -183,7 +183,7 @@ where
/// Returns an [`Iterator`] over the query results.
#[inline]
pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> {
pub fn iter_mut(&mut self) -> QueryIter<'w, 's, Q, F> {
// SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
@ -216,7 +216,7 @@ where
#[inline]
pub fn iter_combinations_mut<const K: usize>(
&mut self,
) -> QueryCombinationIter<'_, '_, Q, F, K> {
) -> QueryCombinationIter<'w, 's, Q, F, K> {
// SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
@ -235,7 +235,7 @@ where
/// This function makes it possible to violate Rust's aliasing guarantees. You must make sure
/// this call does not result in multiple mutable references to the same component
#[inline]
pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, '_, Q, F> {
pub unsafe fn iter_unsafe(&self) -> QueryIter<'w, 's, Q, F> {
// SEMI-SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
self.state
@ -251,7 +251,7 @@ where
#[inline]
pub unsafe fn iter_combinations_unsafe<const K: usize>(
&self,
) -> QueryCombinationIter<'_, '_, Q, F, K> {
) -> QueryCombinationIter<'w, 's, Q, F, K> {
// SEMI-SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
self.state.iter_combinations_unchecked_manual(
@ -266,7 +266,7 @@ where
///
/// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries.
#[inline]
pub fn for_each(&self, f: impl FnMut(<Q::Fetch as Fetch<'w>>::Item))
pub fn for_each(&self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item))
where
Q::Fetch: ReadOnlyFetch,
{
@ -285,7 +285,7 @@ where
/// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot
/// be chained like a normal [`Iterator`].
#[inline]
pub fn for_each_mut(&mut self, f: impl FnMut(<Q::Fetch as Fetch<'w>>::Item)) {
pub fn for_each_mut(&mut self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item)) {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe {
@ -307,7 +307,7 @@ where
&self,
task_pool: &TaskPool,
batch_size: usize,
f: impl Fn(<Q::Fetch as Fetch<'w>>::Item) + Send + Sync + Clone,
f: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
) where
Q::Fetch: ReadOnlyFetch,
{
@ -331,7 +331,7 @@ where
&mut self,
task_pool: &TaskPool,
batch_size: usize,
f: impl Fn(<Q::Fetch as Fetch<'w>>::Item) + Send + Sync + Clone,
f: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
) {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
@ -351,7 +351,7 @@ where
///
/// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries.
#[inline]
pub fn get(&self, entity: Entity) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError>
pub fn get(&self, entity: Entity) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError>
where
Q::Fetch: ReadOnlyFetch,
{
@ -372,7 +372,7 @@ where
pub fn get_mut(
&mut self,
entity: Entity,
) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
) -> Result<<Q::Fetch as Fetch<'w,'s>>::Item, QueryEntityError> {
// SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
@ -393,7 +393,7 @@ where
/// this call does not result in multiple mutable references to the same component
#[inline]
pub unsafe fn get_unchecked(
&self,
&'s self,
entity: Entity,
) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
// SEMI-SAFE: system runs without conflicts with other systems.
@ -406,7 +406,7 @@ where
/// entity does not have the given component type or if the given component type does not match
/// this query.
#[inline]
pub fn get_component<T: Component>(&self, entity: Entity) -> Result<&T, QueryComponentError> {
pub fn get_component<T: Component>(&self, entity: Entity) -> Result<&'w T, QueryComponentError> {
let world = self.world;
let entity_ref = world
.get_entity(entity)
@ -439,7 +439,7 @@ where
pub fn get_component_mut<T: Component>(
&mut self,
entity: Entity,
) -> Result<Mut<'_, T>, QueryComponentError> {
) -> Result<Mut<'w, T>, QueryComponentError> {
// SAFE: unique access to query (preventing aliased access)
unsafe { self.get_component_unchecked_mut(entity) }
}
@ -456,7 +456,7 @@ where
pub unsafe fn get_component_unchecked_mut<T: Component>(
&self,
entity: Entity,
) -> Result<Mut<'_, T>, QueryComponentError> {
) -> Result<Mut<'w, T>, QueryComponentError> {
let world = self.world;
let entity_ref = world
.get_entity(entity)
@ -511,7 +511,7 @@ where
/// ```
///
/// This can only be called for read-only queries, see [`Self::single_mut`] for write-queries.
pub fn single(&self) -> Result<<Q::Fetch as Fetch<'_>>::Item, QuerySingleError>
pub fn single(&self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError>
where
Q::Fetch: ReadOnlyFetch,
{
@ -530,7 +530,7 @@ where
/// Gets the query result if it is only a single result, otherwise returns a
/// [`QuerySingleError`].
pub fn single_mut(&mut self) -> Result<<Q::Fetch as Fetch<'_>>::Item, QuerySingleError> {
pub fn single_mut(&mut self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError> {
let mut query = self.iter_mut();
let first = query.next();
let extra = query.next().is_some();

View file

@ -41,7 +41,7 @@ use std::{
/// # my_system.system();
/// ```
pub trait SystemParam: Sized {
type Fetch: for<'a> SystemParamFetch<'a>;
type Fetch: for<'s, 'w> SystemParamFetch<'s, 'w>;
}
/// The state of a [`SystemParam`].
@ -80,21 +80,21 @@ pub unsafe trait SystemParamState: Send + Sync + 'static {
/// This must only be implemented for [`SystemParamFetch`] impls that exclusively read the World passed in to [`SystemParamFetch::get_param`]
pub unsafe trait ReadOnlySystemParamFetch {}
pub trait SystemParamFetch<'a>: SystemParamState {
pub trait SystemParamFetch<'s, 'w>: SystemParamState {
type Item;
/// # Safety
///
/// This call might access any of the input parameters in an unsafe way. Make sure the data
/// access is safe in the context of the system scheduler.
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item;
}
impl<'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'a, Q, F>
impl<'s, 'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F>
where
F::Fetch: FilterFetch,
{
@ -146,17 +146,17 @@ where
fn default_config() {}
}
impl<'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'a> for QueryState<Q, F>
impl<'s,'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'s, 'w> for QueryState<Q, F>
where
F::Fetch: FilterFetch,
{
type Item = Query<'a, Q, F>;
type Item = Query<'w, 's, Q, F>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
Query::new(world, state, system_meta.last_change_tick, change_tick)
@ -228,6 +228,10 @@ impl<'w, T: Component> Res<'w, T> {
self.ticks
.is_changed(self.last_change_tick, self.change_tick)
}
pub fn into_inner(self) -> &'w T {
self.value
}
}
impl<'w, T: Component> Deref for Res<'w, T> {
@ -286,14 +290,14 @@ unsafe impl<T: Component> SystemParamState for ResState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for ResState<T> {
type Item = Res<'a, T>;
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResState<T> {
type Item = Res<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
let column = world
@ -334,14 +338,14 @@ unsafe impl<T: Component> SystemParamState for OptionResState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for OptionResState<T> {
type Item = Option<Res<'a, T>>;
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResState<T> {
type Item = Option<Res<'w, T>>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world
@ -400,14 +404,14 @@ unsafe impl<T: Component> SystemParamState for ResMutState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for ResMutState<T> {
type Item = ResMut<'a, T>;
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResMutState<T> {
type Item = ResMut<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
let value = world
@ -447,14 +451,14 @@ unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for OptionResMutState<T> {
type Item = Option<ResMut<'a, T>>;
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResMutState<T> {
type Item = Option<ResMut<'w, T>>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world
@ -470,7 +474,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResMutState<T> {
}
}
impl<'a> SystemParam for Commands<'a> {
impl<'s, 'w> SystemParam for Commands<'s, 'w> {
type Fetch = CommandQueue;
}
@ -492,14 +496,14 @@ unsafe impl SystemParamState for CommandQueue {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for CommandQueue {
type Item = Commands<'a>;
impl<'s, 'w> SystemParamFetch<'s, 'w> for CommandQueue {
type Item = Commands<'s, 'w>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
Commands::new(state, world)
@ -582,14 +586,14 @@ unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> {
}
}
impl<'a, T: Component + FromWorld> SystemParamFetch<'a> for LocalState<T> {
type Item = Local<'a, T>;
impl<'s, 'w, T: Component + FromWorld> SystemParamFetch<'s, 'w> for LocalState<T> {
type Item = Local<'s, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
_system_meta: &SystemMeta,
_world: &'a World,
_world: &'w World,
_change_tick: u32,
) -> Self::Item {
Local(&mut state.0)
@ -655,14 +659,14 @@ unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for RemovedComponentsState<T> {
type Item = RemovedComponents<'a, T>;
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for RemovedComponentsState<T> {
type Item = RemovedComponents<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
RemovedComponents {
@ -770,14 +774,14 @@ unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
fn default_config() {}
}
impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState<T> {
type Item = NonSend<'a, T>;
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendState<T> {
type Item = NonSend<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world.validate_non_send_access::<T>();
@ -889,14 +893,14 @@ unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
fn default_config() {}
}
impl<'a, T: 'static> SystemParamFetch<'a> for NonSendMutState<T> {
type Item = NonSendMut<'a, T>;
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendMutState<T> {
type Item = NonSendMut<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world.validate_non_send_access::<T>();
@ -982,14 +986,14 @@ unsafe impl SystemParamState for ArchetypesState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for ArchetypesState {
type Item = &'a Archetypes;
impl<'s, 'w> SystemParamFetch<'s, 'w> for ArchetypesState {
type Item = &'w Archetypes;
#[inline]
unsafe fn get_param(
_state: &'a mut Self,
_state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
world.archetypes()
@ -1017,14 +1021,14 @@ unsafe impl SystemParamState for ComponentsState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for ComponentsState {
type Item = &'a Components;
impl<'s, 'w> SystemParamFetch<'s, 'w> for ComponentsState {
type Item = &'w Components;
#[inline]
unsafe fn get_param(
_state: &'a mut Self,
_state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
world.components()
@ -1052,14 +1056,14 @@ unsafe impl SystemParamState for EntitiesState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for EntitiesState {
type Item = &'a Entities;
impl<'s, 'w> SystemParamFetch<'s, 'w> for EntitiesState {
type Item = &'w Entities;
#[inline]
unsafe fn get_param(
_state: &'a mut Self,
_state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
world.entities()
@ -1087,14 +1091,14 @@ unsafe impl SystemParamState for BundlesState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for BundlesState {
type Item = &'a Bundles;
impl<'s, 'w> SystemParamFetch<'s, 'w> for BundlesState {
type Item = &'w Bundles;
#[inline]
unsafe fn get_param(
_state: &'a mut Self,
_state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
world.bundles()
@ -1127,13 +1131,13 @@ unsafe impl SystemParamState for SystemChangeTickState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for SystemChangeTickState {
impl<'s, 'w> SystemParamFetch<'s, 'w> for SystemChangeTickState {
type Item = SystemChangeTick;
unsafe fn get_param(
_state: &mut Self,
_state: &'s mut Self,
system_meta: &SystemMeta,
_world: &World,
_world: &'w World,
change_tick: u32,
) -> Self::Item {
SystemChangeTick {
@ -1154,14 +1158,14 @@ macro_rules! impl_system_param_tuple {
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<'a, $($param: SystemParamFetch<'a>),*> SystemParamFetch<'a> for ($($param,)*) {
impl<'s, 'w, $($param: SystemParamFetch<'s, 'w>),*> SystemParamFetch<'s, 'w> for ($($param,)*) {
type Item = ($($param::Item,)*);
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {

View file

@ -77,7 +77,6 @@ bevy_sprite2 = { path = "../../pipelined/bevy_sprite2", optional = true, version
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" }

View file

@ -127,9 +127,6 @@ impl PluginGroup for PipelinedDefaultPlugins {
#[cfg(feature = "bevy_winit")]
group.add(bevy_winit::WinitPlugin::default());
#[cfg(feature = "bevy_wgpu2")]
group.add(bevy_wgpu2::WgpuPlugin::default());
#[cfg(feature = "bevy_sprite2")]
group.add(bevy_sprite2::SpritePlugin::default());

View file

@ -152,12 +152,6 @@ pub mod wgpu {
pub use bevy_wgpu::*;
}
#[cfg(feature = "bevy_wgpu2")]
pub mod wgpu2 {
//! A render backend utilizing [wgpu](https://github.com/gfx-rs/wgpu-rs).
pub use bevy_wgpu2::*;
}
#[cfg(feature = "bevy_dynamic_plugin")]
pub mod dynamic_plugin {
pub use bevy_dynamic_plugin::*;

View file

@ -14,7 +14,7 @@ use bevy_ecs::{
system::{Query, Res, ResMut, SystemParam},
};
use bevy_reflect::Reflect;
use std::{ops::Range, sync::Arc};
use std::{marker::PhantomData, ops::Range, sync::Arc};
use thiserror::Error;
/// A queued command for the renderer
@ -164,18 +164,20 @@ pub enum DrawError {
}
#[derive(SystemParam)]
pub struct DrawContext<'a> {
pub pipelines: ResMut<'a, Assets<PipelineDescriptor>>,
pub shaders: ResMut<'a, Assets<Shader>>,
pub asset_render_resource_bindings: ResMut<'a, AssetRenderResourceBindings>,
pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
pub shared_buffers: ResMut<'a, SharedBuffers>,
pub struct DrawContext<'s, 'w> {
pub pipelines: ResMut<'w, Assets<PipelineDescriptor>>,
pub shaders: ResMut<'w, Assets<Shader>>,
pub asset_render_resource_bindings: ResMut<'w, AssetRenderResourceBindings>,
pub pipeline_compiler: ResMut<'w, PipelineCompiler>,
pub render_resource_context: Res<'w, Box<dyn RenderResourceContext>>,
pub shared_buffers: ResMut<'w, SharedBuffers>,
#[system_param(ignore)]
pub current_pipeline: Option<Handle<PipelineDescriptor>>,
#[system_param(ignore)]
marker: PhantomData<&'s usize>,
}
impl<'a> DrawContext<'a> {
impl<'s, 'w> DrawContext<'s, 'w> {
pub fn get_uniform_buffer<T: RenderResource>(
&mut self,
render_resource: &T,

View file

@ -23,7 +23,7 @@ pub trait SpawnSceneCommands {
fn spawn_scene(&mut self, scene: Handle<Scene>);
}
impl<'a> SpawnSceneCommands for Commands<'a> {
impl<'s, 'w> SpawnSceneCommands for Commands<'s, 'w> {
fn spawn_scene(&mut self, scene_handle: Handle<Scene>) {
self.add(SpawnScene { scene_handle });
}
@ -45,7 +45,7 @@ pub trait SpawnSceneAsChildCommands {
fn spawn_scene(&mut self, scene: Handle<Scene>) -> &mut Self;
}
impl<'a, 'b> SpawnSceneAsChildCommands for ChildBuilder<'a, 'b> {
impl<'s, 'w, 'a> SpawnSceneAsChildCommands for ChildBuilder<'s, 'w, 'a> {
fn spawn_scene(&mut self, scene_handle: Handle<Scene>) -> &mut Self {
self.add_command(SpawnSceneAsChild {
scene_handle,

View file

@ -40,8 +40,8 @@ pub struct PushChildren {
children: SmallVec<[Entity; 8]>,
}
pub struct ChildBuilder<'a, 'b> {
commands: &'b mut Commands<'a>,
pub struct ChildBuilder<'s, 'w, 'a> {
commands: &'a mut Commands<'s, 'w>,
push_children: PushChildren,
}
@ -71,14 +71,14 @@ impl Command for PushChildren {
}
}
impl<'a, 'b> ChildBuilder<'a, 'b> {
pub fn spawn_bundle(&mut self, bundle: impl Bundle) -> EntityCommands<'a, '_> {
impl<'s, 'w, 'a> ChildBuilder<'s, 'w, 'a> {
pub fn spawn_bundle(&mut self, bundle: impl Bundle) -> EntityCommands<'s,'w, '_> {
let e = self.commands.spawn_bundle(bundle);
self.push_children.children.push(e.id());
e
}
pub fn spawn(&mut self) -> EntityCommands<'a, '_> {
pub fn spawn(&mut self) -> EntityCommands<'s, 'w, '_> {
let e = self.commands.spawn();
self.push_children.children.push(e.id());
e
@ -100,7 +100,7 @@ pub trait BuildChildren {
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self;
}
impl<'a, 'b> BuildChildren for EntityCommands<'a, 'b> {
impl<'s, 'w, 'a> BuildChildren for EntityCommands<'s, 'w, 'a> {
fn with_children(&mut self, spawn_children: impl FnOnce(&mut ChildBuilder)) -> &mut Self {
let parent = self.id();
let push_children = {

View file

@ -47,7 +47,7 @@ pub trait DespawnRecursiveExt {
fn despawn_recursive(&mut self);
}
impl<'a, 'b> DespawnRecursiveExt for EntityCommands<'a, 'b> {
impl<'s, 'w, 'a> DespawnRecursiveExt for EntityCommands<'s, 'w, 'a> {
/// Despawns the provided entity and its children.
fn despawn_recursive(&mut self) {
let entity = self.id();

View file

@ -11,7 +11,6 @@ use bevy::{
color::Color,
mesh::{shape, Mesh},
},
wgpu2::diagnostic::WgpuResourceDiagnosticsPlugin,
PipelinedDefaultPlugins,
};
@ -20,7 +19,6 @@ fn main() {
.add_plugins(PipelinedDefaultPlugins)
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(WgpuResourceDiagnosticsPlugin::default())
.add_startup_system(setup.system())
.add_system(movement.system())
.run();

View file

@ -17,12 +17,12 @@ pub struct PlayerCount(usize);
///
/// In this example, it includes a query and a mutable resource.
#[derive(SystemParam)]
struct PlayerCounter<'a> {
players: Query<'a, &'static Player>,
count: ResMut<'a, PlayerCount>,
struct PlayerCounter<'s, 'w> {
players: Query<'w, 's, &'static Player>,
count: ResMut<'w, PlayerCount>,
}
impl<'a> PlayerCounter<'a> {
impl<'s, 'w> PlayerCounter<'s, 'w> {
fn count(&mut self) {
self.count.0 = self.players.iter().len();
}

View file

@ -5,9 +5,8 @@ use bevy::{
input::Input,
math::{Vec2, Vec3},
prelude::{App, AssetServer, Handle, MouseButton, Transform},
render2::{camera::OrthographicCameraBundle, color::Color, texture::Texture},
render2::{camera::OrthographicCameraBundle, color::Color, texture::Image},
sprite2::{PipelinedSpriteBundle, Sprite},
wgpu2::diagnostic::WgpuResourceDiagnosticsPlugin,
window::WindowDescriptor,
PipelinedDefaultPlugins,
};
@ -52,7 +51,7 @@ fn main() {
.add_plugins(PipelinedDefaultPlugins)
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(WgpuResourceDiagnosticsPlugin::default())
// .add_plugin(WgpuResourceDiagnosticsPlugin::default())
.insert_resource(BevyCounter { count: 0 })
// .init_resource::<BirdMaterial>()
.add_startup_system(setup.system())
@ -63,7 +62,7 @@ fn main() {
.run();
}
struct BirdTexture(Handle<Texture>);
struct BirdTexture(Handle<Image>);
fn setup(
mut commands: Commands,
@ -168,7 +167,7 @@ fn spawn_birds(
window: &WindowDescriptor,
counter: &mut BevyCounter,
spawn_count: u128,
texture: Handle<Texture>,
texture: Handle<Image>,
) {
let bird_x = (window.width / -2.) + HALF_BIRD_SIZE;
let bird_y = (window.height / 2.) - HALF_BIRD_SIZE;

View file

@ -46,7 +46,6 @@ impl Plugin for PbrPlugin {
RenderStage::PhaseSort,
sort_phase_system::<ShadowPhase>.system(),
)
.add_system_to_stage(RenderStage::Cleanup, render::cleanup_view_lights.system())
.init_resource::<PbrShaders>()
.init_resource::<ShadowShaders>()
.init_resource::<MaterialMeta>()

View file

@ -3,19 +3,14 @@ use bevy_asset::{AddAsset, AssetEvent, Assets, Handle};
use bevy_ecs::prelude::*;
use bevy_math::Vec4;
use bevy_reflect::TypeUuid;
use bevy_render2::{
color::Color,
render_command::RenderCommandQueue,
render_resource::{BufferId, BufferInfo, BufferUsage},
renderer::{RenderResourceContext, RenderResources},
};
use bevy_render2::{color::Color, render_resource::{Buffer, BufferId, BufferInitDescriptor, BufferUsage}, renderer::{RenderDevice, RenderQueue}};
use bevy_utils::HashSet;
use crevice::std140::{AsStd140, Std140};
// TODO: this shouldn't live in the StandardMaterial type
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct StandardMaterialGpuData {
pub buffer: BufferId,
pub buffer: Buffer,
}
/// A material with "standard" properties used in PBR lighting
@ -107,13 +102,12 @@ impl Plugin for StandardMaterialPlugin {
}
pub fn standard_material_resource_system(
render_resource_context: Res<RenderResources>,
mut render_command_queue: ResMut<RenderCommandQueue>,
render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut material_events: EventReader<AssetEvent<StandardMaterial>>,
) {
let mut changed_materials = HashSet::default();
let render_resource_context = &**render_resource_context;
for event in material_events.iter() {
match event {
AssetEvent::Created { ref handle } => {
@ -125,7 +119,6 @@ pub fn standard_material_resource_system(
// remove_current_material_resources(render_resource_context, handle, &mut materials);
}
AssetEvent::Removed { ref handle } => {
remove_current_material_resources(render_resource_context, handle, &mut materials);
// if material was modified and removed in the same update, ignore the modification
// events are ordered so future modification events are ok
changed_materials.remove(handle);
@ -153,35 +146,13 @@ pub fn standard_material_resource_system(
let size = StandardMaterialUniformData::std140_size_static();
let staging_buffer = render_resource_context.create_buffer_with_data(
BufferInfo {
size,
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
mapped_at_creation: true,
},
value_std140.as_bytes(),
);
let buffer = render_resource_context.create_buffer(BufferInfo {
size,
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
mapped_at_creation: false,
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
label: None,
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
contents: value_std140.as_bytes(),
});
render_command_queue.copy_buffer_to_buffer(staging_buffer, 0, buffer, 0, size as u64);
render_command_queue.free_buffer(staging_buffer);
material.gpu_data = Some(StandardMaterialGpuData { buffer });
}
}
}
fn remove_current_material_resources(
render_resource_context: &dyn RenderResourceContext,
handle: &Handle<StandardMaterial>,
materials: &mut Assets<StandardMaterial>,
) {
if let Some(gpu_data) = materials.get_mut(handle).and_then(|t| t.gpu_data.take()) {
render_resource_context.remove_buffer(gpu_data.buffer);
}
}

View file

@ -1,18 +1,15 @@
use crate::{render::MeshViewBindGroups, ExtractedMeshes, PointLight};
use crate::{ExtractedMeshes, MeshMeta, PbrShaders, PointLight};
use bevy_ecs::{prelude::*, system::SystemState};
use bevy_math::{Mat4, Vec3, Vec4};
use bevy_render2::{
color::Color,
core_pipeline::Transparent3dPhase,
pass::*,
pipeline::*,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass},
render_resource::{DynamicUniformVec, SamplerId, TextureId, TextureViewId},
renderer::{RenderContext, RenderResources},
shader::{Shader, ShaderStage, ShaderStages},
render_resource::*,
renderer::{RenderContext, RenderDevice},
texture::*,
view::{ExtractedView, ViewUniform},
view::{ExtractedView, ViewUniform, ViewUniformOffset},
};
use bevy_transform::components::GlobalTransform;
use crevice::std140::AsStd140;
@ -53,56 +50,75 @@ pub const SHADOW_SIZE: Extent3d = Extent3d {
pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float;
pub struct ShadowShaders {
pub pipeline: PipelineId,
pub pipeline_descriptor: RenderPipelineDescriptor,
pub light_sampler: SamplerId,
pub pipeline: RenderPipeline,
pub view_layout: BindGroupLayout,
pub light_sampler: Sampler,
}
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
impl FromWorld for ShadowShaders {
fn from_world(world: &mut World) -> Self {
let render_resources = world.get_resource::<RenderResources>().unwrap();
let vertex_shader = Shader::from_glsl(ShaderStage::Vertex, include_str!("pbr.vert"))
.get_spirv_shader(None)
.unwrap();
let vertex_layout = vertex_shader.reflect_layout(true).unwrap();
let render_device = world.get_resource::<RenderDevice>().unwrap();
let pbr_shaders = world.get_resource::<PbrShaders>().unwrap();
let mut pipeline_layout = PipelineLayout::from_shader_layouts(&mut [vertex_layout]);
let vertex = render_resources.create_shader_module(&vertex_shader);
pipeline_layout.vertex_buffer_descriptors = vec![VertexBufferLayout {
stride: 32,
name: "Vertex".into(),
step_mode: InputStepMode::Vertex,
attributes: vec![
// GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically)
VertexAttribute {
name: "Vertex_Position".into(),
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
VertexAttribute {
name: "Vertex_Normals".into(),
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
VertexAttribute {
name: "Vertex_Uv".into(),
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
// View
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::VERTEX | ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
// TODO: verify this is correct
min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64),
},
count: None,
},
],
}];
label: None,
});
pipeline_layout.bind_group_mut(0).bindings[0].set_dynamic(true);
pipeline_layout.bind_group_mut(1).bindings[0].set_dynamic(true);
pipeline_layout.update_bind_group_ids();
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
push_constant_ranges: &[],
bind_group_layouts: &[
&view_layout,
&pbr_shaders.mesh_layout,
],
});
let pipeline_descriptor = RenderPipelineDescriptor {
let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
vertex: VertexState {
buffers: &[VertexBufferLayout {
array_stride: 32,
step_mode: InputStepMode::Vertex,
attributes: &[
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
],
}],
module: &pbr_shaders.vertex_shader_module,
entry_point: "main",
},
fragment: None,
depth_stencil: Some(DepthStencilState {
format: SHADOW_FORMAT,
depth_write_enabled: true,
@ -119,36 +135,30 @@ impl FromWorld for ShadowShaders {
clamp: 0.0,
},
}),
layout: Some(&pipeline_layout),
multisample: MultisampleState::default(),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: Some(Face::Back),
// TODO: detect if this feature is enabled
polygon_mode: PolygonMode::Fill,
clamp_depth: false,
..Default::default()
conservative: false,
},
color_target_states: vec![],
..RenderPipelineDescriptor::new(
ShaderStages {
vertex,
fragment: None,
},
pipeline_layout,
)
};
let pipeline = render_resources.create_render_pipeline(&pipeline_descriptor);
});
ShadowShaders {
pipeline,
pipeline_descriptor,
light_sampler: render_resources.create_sampler(&SamplerDescriptor {
view_layout,
light_sampler: render_device.create_sampler(&SamplerDescriptor {
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Nearest,
compare_function: Some(CompareFunction::LessEqual),
compare: Some(CompareFunction::LessEqual),
..Default::default()
}),
}
@ -172,12 +182,12 @@ pub fn extract_lights(
}
pub struct ViewLight {
pub depth_texture: TextureViewId,
pub depth_texture: TextureView,
}
pub struct ViewLights {
pub light_depth_texture: TextureId,
pub light_depth_texture_view: TextureViewId,
pub light_depth_texture: Texture,
pub light_depth_texture_view: TextureView,
pub lights: Vec<Entity>,
pub gpu_light_binding_index: u32,
}
@ -185,12 +195,13 @@ pub struct ViewLights {
#[derive(Default)]
pub struct LightMeta {
pub view_gpu_lights: DynamicUniformVec<GpuLights>,
pub shadow_view_bind_group: Option<BindGroup>,
}
pub fn prepare_lights(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
render_resources: Res<RenderResources>,
render_device: Res<RenderDevice>,
mut light_meta: ResMut<LightMeta>,
views: Query<Entity, With<RenderPhase<Transparent3dPhase>>>,
lights: Query<&ExtractedPointLight>,
@ -198,12 +209,12 @@ pub fn prepare_lights(
// PERF: view.iter().count() could be views.iter().len() if we implemented ExactSizeIterator for archetype-only filters
light_meta
.view_gpu_lights
.reserve_and_clear(views.iter().count(), &render_resources);
.reserve_and_clear(views.iter().count(), &render_device);
// set up light data for each view
for entity in views.iter() {
let light_depth_texture = texture_cache.get(
&render_resources,
&render_device,
TextureDescriptor {
size: SHADOW_SIZE,
mip_level_count: 1,
@ -211,7 +222,7 @@ pub fn prepare_lights(
dimension: TextureDimension::D2,
format: SHADOW_FORMAT,
usage: TextureUsage::RENDER_ATTACHMENT | TextureUsage::SAMPLED,
..Default::default()
label: None,
},
);
let mut view_lights = Vec::new();
@ -223,18 +234,19 @@ pub fn prepare_lights(
// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
for (i, light) in lights.iter().enumerate().take(MAX_POINT_LIGHTS) {
let depth_texture_view = render_resources.create_texture_view(
light_depth_texture.texture,
TextureViewDescriptor {
format: None,
dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: i as u32,
array_layer_count: NonZeroU32::new(1),
},
);
let depth_texture_view =
light_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: None,
format: None,
dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: i as u32,
array_layer_count: NonZeroU32::new(1),
});
let view_transform = GlobalTransform::from_translation(light.transform.translation)
.looking_at(Vec3::default(), Vec3::Y);
@ -280,14 +292,7 @@ pub fn prepare_lights(
light_meta
.view_gpu_lights
.write_to_staging_buffer(&render_resources);
}
// TODO: we can remove this once we move to RAII
pub fn cleanup_view_lights(render_resources: Res<RenderResources>, query: Query<&ViewLight>) {
for view_light in query.iter() {
render_resources.remove_texture_view(view_light.depth_texture);
}
.write_to_staging_buffer(&render_device);
}
pub struct ShadowPhase;
@ -321,7 +326,7 @@ impl Node for ShadowPassNode {
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut dyn RenderContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
@ -331,39 +336,36 @@ impl Node for ShadowPassNode {
.view_light_query
.get_manual(world, view_light_entity)
.unwrap();
let pass_descriptor = PassDescriptor {
color_attachments: Vec::new(),
let pass_descriptor = RenderPassDescriptor {
label: Some("shadow_pass"),
color_attachments: &[],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
attachment: TextureAttachment::Id(view_light.depth_texture),
view: &view_light.depth_texture,
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
sample_count: 1,
};
let draw_functions = world.get_resource::<DrawFunctions>().unwrap();
render_context.begin_render_pass(
&pass_descriptor,
&mut |render_pass: &mut dyn RenderPass| {
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for drawable in shadow_phase.drawn_things.iter() {
let draw_function =
draw_functions.get_mut(drawable.draw_function).unwrap();
draw_function.draw(
world,
&mut tracked_pass,
view_light_entity,
drawable.draw_key,
drawable.sort_key,
);
}
},
);
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for drawable in shadow_phase.drawn_things.iter() {
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
draw_function.draw(
world,
&mut tracked_pass,
view_light_entity,
drawable.draw_key,
drawable.sort_key,
);
}
}
}
@ -371,13 +373,15 @@ impl Node for ShadowPassNode {
}
}
type DrawShadowMeshParams<'a> = (
Res<'a, ShadowShaders>,
Res<'a, ExtractedMeshes>,
Query<'a, (&'a ViewUniform, &'a MeshViewBindGroups)>,
type DrawShadowMeshParams<'s, 'w> = (
Res<'w, ShadowShaders>,
Res<'w, ExtractedMeshes>,
Res<'w, LightMeta>,
Res<'w, MeshMeta>,
Query<'w, 's, &'w ViewUniformOffset>,
);
pub struct DrawShadowMesh {
params: SystemState<DrawShadowMeshParams<'static>>,
params: SystemState<DrawShadowMeshParams<'static, 'static>>,
}
impl DrawShadowMesh {
@ -389,35 +393,41 @@ impl DrawShadowMesh {
}
impl Draw for DrawShadowMesh {
fn draw(
fn draw<'w>(
&mut self,
world: &World,
pass: &mut TrackedRenderPass,
world: &'w World,
pass: &mut TrackedRenderPass<'w>,
view: Entity,
draw_key: usize,
_sort_key: usize,
) {
let (shadow_shaders, extracted_meshes, views) = self.params.get(world);
let (view_uniforms, mesh_view_bind_groups) = views.get(view).unwrap();
let layout = &shadow_shaders.pipeline_descriptor.layout;
let extracted_mesh = &extracted_meshes.meshes[draw_key];
pass.set_pipeline(shadow_shaders.pipeline);
let (shadow_shaders, extracted_meshes, light_meta, mesh_meta, views) =
self.params.get(world);
let view_uniform_offset = views.get(view).unwrap();
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
pass.set_render_pipeline(&shadow_shaders.into_inner().pipeline);
pass.set_bind_group(
0,
layout.bind_group(0).id,
mesh_view_bind_groups.view_bind_group,
Some(&[view_uniforms.view_uniform_offset]),
light_meta
.into_inner()
.shadow_view_bind_group
.as_ref()
.unwrap(),
&[view_uniform_offset.offset],
);
pass.set_bind_group(
1,
layout.bind_group(1).id,
mesh_view_bind_groups.mesh_transform_bind_group,
Some(&[extracted_mesh.transform_binding_offset]),
mesh_meta
.into_inner()
.mesh_transform_bind_group
.as_ref()
.unwrap(),
&[extracted_mesh.transform_binding_offset],
);
pass.set_vertex_buffer(0, extracted_mesh.vertex_buffer, 0);
pass.set_vertex_buffer(0, extracted_mesh.vertex_buffer.slice(..));
if let Some(index_info) = &extracted_mesh.index_info {
pass.set_index_buffer(index_info.buffer, 0, IndexFormat::Uint32);
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
pass.draw_indexed(0..index_info.count, 0, 0..1);
} else {
panic!("non-indexed drawing not supported yet")

View file

@ -1,97 +1,197 @@
mod light;
use bevy_utils::HashMap;
pub use light::*;
use crate::{StandardMaterial, StandardMaterialUniformData};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{prelude::*, system::SystemState};
use bevy_math::Mat4;
use bevy_render2::{
core_pipeline::Transparent3dPhase,
mesh::Mesh,
pipeline::*,
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
render_resource::{
BindGroupBuilder, BindGroupId, BufferId, DynamicUniformVec, RenderResourceBinding,
},
renderer::{RenderContext, RenderResources},
shader::{Shader, ShaderStage, ShaderStages},
texture::{TextureFormat, TextureSampleType},
view::{ViewMeta, ViewUniform},
render_resource::*,
renderer::{RenderContext, RenderDevice},
shader::Shader,
texture::BevyDefault,
view::{ViewMeta, ViewUniform, ViewUniformOffset},
};
use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap;
use crevice::std140::AsStd140;
use std::borrow::Cow;
use crate::{StandardMaterial, StandardMaterialUniformData};
pub struct PbrShaders {
pipeline: PipelineId,
pipeline_descriptor: RenderPipelineDescriptor,
pipeline: RenderPipeline,
vertex_shader_module: ShaderModule,
view_layout: BindGroupLayout,
material_layout: BindGroupLayout,
mesh_layout: BindGroupLayout,
}
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
impl FromWorld for PbrShaders {
fn from_world(world: &mut World) -> Self {
let render_resources = world.get_resource::<RenderResources>().unwrap();
let vertex_shader = Shader::from_glsl(ShaderStage::Vertex, include_str!("pbr.vert"))
let render_device = world.get_resource::<RenderDevice>().unwrap();
let vertex_shader = Shader::from_glsl(ShaderStage::VERTEX, include_str!("pbr.vert"))
.get_spirv_shader(None)
.unwrap();
let fragment_shader = Shader::from_glsl(ShaderStage::Fragment, include_str!("pbr.frag"))
let fragment_shader = Shader::from_glsl(ShaderStage::FRAGMENT, include_str!("pbr.frag"))
.get_spirv_shader(None)
.unwrap();
let vertex_layout = vertex_shader.reflect_layout(true).unwrap();
let fragment_layout = fragment_shader.reflect_layout(true).unwrap();
let vertex_spirv = vertex_shader.get_spirv(None).unwrap();
let fragment_spirv = fragment_shader.get_spirv(None).unwrap();
let mut pipeline_layout =
PipelineLayout::from_shader_layouts(&mut [vertex_layout, fragment_layout]);
let vertex_shader_module = render_device.create_shader_module(&ShaderModuleDescriptor {
flags: ShaderFlags::default(),
label: None,
source: ShaderSource::SpirV(Cow::Borrowed(&vertex_spirv)),
});
let fragment_shader_module = render_device.create_shader_module(&ShaderModuleDescriptor {
flags: ShaderFlags::default(),
label: None,
source: ShaderSource::SpirV(Cow::Borrowed(&fragment_spirv)),
});
let vertex = render_resources.create_shader_module(&vertex_shader);
let fragment = render_resources.create_shader_module(&fragment_shader);
pipeline_layout.vertex_buffer_descriptors = vec![VertexBufferLayout {
stride: 32,
name: "Vertex".into(),
step_mode: InputStepMode::Vertex,
attributes: vec![
// GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically)
VertexAttribute {
name: "Vertex_Position".into(),
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
// TODO: move this into ViewMeta?
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
// View
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::VERTEX | ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
// TODO: verify this is correct
min_binding_size: BufferSize::new(ViewUniform::std140_size_static() as u64),
},
count: None,
},
VertexAttribute {
name: "Vertex_Normals".into(),
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
// Lights
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: BufferSize::new(GpuLights::std140_size_static() as u64),
},
count: None,
},
VertexAttribute {
name: "Vertex_Uv".into(),
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
// Shadow Texture Array
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Depth,
view_dimension: TextureViewDimension::D2Array,
},
count: None,
},
// Shadow Texture Array Sampler
BindGroupLayoutEntry {
binding: 3,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Sampler {
comparison: true,
filtering: true,
},
count: None,
},
],
}];
label: None,
});
pipeline_layout.bind_group_mut(0).bindings[0].set_dynamic(true);
pipeline_layout.bind_group_mut(0).bindings[1].set_dynamic(true);
if let BindType::Texture { sample_type, .. } =
&mut pipeline_layout.bind_group_mut(0).bindings[2].bind_type
{
*sample_type = TextureSampleType::Depth;
}
if let BindType::Sampler { comparison, .. } =
&mut pipeline_layout.bind_group_mut(0).bindings[3].bind_type
{
*comparison = true;
}
pipeline_layout.bind_group_mut(1).bindings[0].set_dynamic(true);
let mesh_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::VERTEX,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: BufferSize::new(Mat4::std140_size_static() as u64),
},
count: None,
}],
label: None,
});
pipeline_layout.update_bind_group_ids();
let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: BufferSize::new(
StandardMaterialUniformData::std140_size_static() as u64,
),
},
count: None,
}],
label: None,
});
let pipeline_descriptor = RenderPipelineDescriptor {
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
push_constant_ranges: &[],
bind_group_layouts: &[&view_layout, &mesh_layout, &material_layout],
});
let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
vertex: VertexState {
buffers: &[VertexBufferLayout {
array_stride: 32,
step_mode: InputStepMode::Vertex,
attributes: &[
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
],
}],
module: &&vertex_shader_module,
entry_point: "main",
},
fragment: Some(FragmentState {
module: &&fragment_shader_module,
entry_point: "main",
targets: &[ColorTargetState {
format: TextureFormat::bevy_default(),
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrite::ALL,
}],
}),
depth_stencil: Some(DepthStencilState {
format: TextureFormat::Depth32Float,
depth_write_enabled: true,
@ -108,50 +208,39 @@ impl FromWorld for PbrShaders {
clamp: 0.0,
},
}),
color_target_states: vec![ColorTargetState {
format: TextureFormat::default(),
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrite::ALL,
}],
..RenderPipelineDescriptor::new(
ShaderStages {
vertex,
fragment: Some(fragment),
},
pipeline_layout,
)
};
let pipeline = render_resources.create_render_pipeline(&pipeline_descriptor);
layout: Some(&pipeline_layout),
multisample: MultisampleState::default(),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: Some(Face::Back),
polygon_mode: PolygonMode::Fill,
clamp_depth: false,
conservative: false,
},
});
PbrShaders {
pipeline,
pipeline_descriptor,
view_layout,
material_layout,
mesh_layout,
vertex_shader_module,
}
}
}
struct ExtractedMesh {
transform: Mat4,
vertex_buffer: BufferId,
vertex_buffer: Buffer,
index_info: Option<IndexInfo>,
transform_binding_offset: u32,
material_buffer: BufferId,
material_buffer: Buffer,
}
struct IndexInfo {
buffer: BufferId,
buffer: Buffer,
count: u32,
}
@ -173,13 +262,13 @@ pub fn extract_meshes(
if let Some(material_gpu_data) = &material.gpu_data() {
extracted_meshes.push(ExtractedMesh {
transform: transform.compute_matrix(),
vertex_buffer: mesh_gpu_data.vertex_buffer,
index_info: mesh_gpu_data.index_buffer.map(|i| IndexInfo {
buffer: i,
vertex_buffer: mesh_gpu_data.vertex_buffer.clone(),
index_info: mesh_gpu_data.index_buffer.as_ref().map(|i| IndexInfo {
buffer: i.clone(),
count: mesh.indices().unwrap().len() as u32,
}),
transform_binding_offset: 0,
material_buffer: material_gpu_data.buffer,
material_buffer: material_gpu_data.buffer.clone(),
});
}
}
@ -195,16 +284,17 @@ pub fn extract_meshes(
#[derive(Default)]
pub struct MeshMeta {
transform_uniforms: DynamicUniformVec<Mat4>,
mesh_transform_bind_group: Option<BindGroup>,
}
pub fn prepare_meshes(
render_resources: Res<RenderResources>,
render_device: Res<RenderDevice>,
mut mesh_meta: ResMut<MeshMeta>,
mut extracted_meshes: ResMut<ExtractedMeshes>,
) {
mesh_meta
.transform_uniforms
.reserve_and_clear(extracted_meshes.meshes.len(), &render_resources);
.reserve_and_clear(extracted_meshes.meshes.len(), &render_device);
for extracted_mesh in extracted_meshes.meshes.iter_mut() {
extracted_mesh.transform_binding_offset =
mesh_meta.transform_uniforms.push(extracted_mesh.transform);
@ -212,83 +302,109 @@ pub fn prepare_meshes(
mesh_meta
.transform_uniforms
.write_to_staging_buffer(&render_resources);
.write_to_staging_buffer(&render_device);
}
// TODO: This is temporary. Once we expose BindGroupLayouts directly, we can create view bind groups without specific shader context
struct MeshViewBindGroups {
view_bind_group: BindGroupId,
mesh_transform_bind_group: BindGroupId,
}
#[derive(Default)]
pub struct MaterialMeta {
material_bind_groups: Vec<BindGroupId>,
material_bind_groups: Vec<BindGroup>,
material_bind_group_indices: HashMap<BufferId, usize>,
}
pub struct MeshViewBindGroups {
view: BindGroup,
}
pub fn queue_meshes(
mut commands: Commands,
draw_functions: Res<DrawFunctions>,
render_resources: Res<RenderResources>,
render_device: Res<RenderDevice>,
pbr_shaders: Res<PbrShaders>,
shadow_shaders: Res<ShadowShaders>,
mesh_meta: Res<MeshMeta>,
mut material_meta: ResMut<MaterialMeta>,
light_meta: Res<LightMeta>,
mesh_meta: ResMut<MeshMeta>,
material_meta: ResMut<MaterialMeta>,
mut light_meta: ResMut<LightMeta>,
view_meta: Res<ViewMeta>,
mut extracted_meshes: ResMut<ExtractedMeshes>,
mut views: Query<(Entity, &ViewLights, &mut RenderPhase<Transparent3dPhase>)>,
mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>,
) {
let mesh_meta = mesh_meta.into_inner();
let material_meta = material_meta.into_inner();
light_meta.shadow_view_bind_group.get_or_insert_with(|| {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: view_meta.uniforms.binding(),
}],
label: None,
layout: &shadow_shaders.view_layout,
})
});
if extracted_meshes.meshes.is_empty() {
return;
}
let transform_uniforms = &mesh_meta.transform_uniforms;
mesh_meta.mesh_transform_bind_group.get_or_insert_with(|| {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: transform_uniforms.binding(),
}],
label: None,
layout: &pbr_shaders.mesh_layout,
})
});
for (entity, view_lights, mut transparent_phase) in views.iter_mut() {
let layout = &pbr_shaders.pipeline_descriptor.layout;
let view_bind_group = BindGroupBuilder::default()
.add_binding(0, view_meta.uniforms.binding())
.add_binding(1, light_meta.view_gpu_lights.binding())
.add_binding(2, view_lights.light_depth_texture_view)
.add_binding(3, shadow_shaders.light_sampler)
.finish();
// TODO: this will only create the bind group if it isn't already created. this is a bit nasty
render_resources.create_bind_group(layout.bind_group(0).id, &view_bind_group);
let mesh_transform_bind_group = BindGroupBuilder::default()
.add_binding(0, mesh_meta.transform_uniforms.binding())
.finish();
render_resources.create_bind_group(layout.bind_group(1).id, &mesh_transform_bind_group);
commands.entity(entity).insert(MeshViewBindGroups {
view_bind_group: view_bind_group.id,
mesh_transform_bind_group: mesh_transform_bind_group.id,
// TODO: cache this?
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
BindGroupEntry {
binding: 0,
resource: view_meta.uniforms.binding(),
},
BindGroupEntry {
binding: 1,
resource: light_meta.view_gpu_lights.binding(),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(&view_lights.light_depth_texture_view),
},
BindGroupEntry {
binding: 3,
resource: BindingResource::Sampler(&shadow_shaders.light_sampler),
},
],
label: None,
layout: &pbr_shaders.view_layout,
});
// TODO: free old bind groups? clear_unused_bind_groups() currently does this for us? Moving to RAII would also do this for us?
material_meta.material_bind_groups.clear();
let mut material_bind_group_indices = HashMap::default();
commands.entity(entity).insert(MeshViewBindGroups {
view: view_bind_group,
});
// TODO: free old bind groups after a few frames without use?
let draw_pbr = draw_functions.read().get_id::<DrawPbr>().unwrap();
let material_bind_groups = &mut material_meta.material_bind_groups;
for (i, mesh) in extracted_meshes.meshes.iter_mut().enumerate() {
let material_bind_group_index = *material_bind_group_indices
.entry(mesh.material_buffer)
let material_bind_group_index = *material_meta
.material_bind_group_indices
.entry(mesh.material_buffer.id())
.or_insert_with(|| {
let index = material_meta.material_bind_groups.len();
let material_bind_group = BindGroupBuilder::default()
.add_binding(
0,
RenderResourceBinding::Buffer {
buffer: mesh.material_buffer,
range: 0..StandardMaterialUniformData::std140_size_static() as u64,
},
)
.finish();
render_resources
.create_bind_group(layout.bind_group(2).id, &material_bind_group);
material_meta
.material_bind_groups
.push(material_bind_group.id);
let index = material_bind_groups.len();
let material_bind_group =
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: mesh.material_buffer.as_entire_binding(),
}],
label: None,
layout: &pbr_shaders.material_layout,
});
material_bind_groups.push(material_bind_group);
index
});
@ -304,12 +420,6 @@ pub fn queue_meshes(
let draw_shadow_mesh = draw_functions.read().get_id::<DrawShadowMesh>().unwrap();
for view_light_entity in view_lights.lights.iter().copied() {
let mut shadow_phase = view_light_shadow_phases.get_mut(view_light_entity).unwrap();
let layout = &shadow_shaders.pipeline_descriptor.layout;
let shadow_view_bind_group = BindGroupBuilder::default()
.add_binding(0, view_meta.uniforms.binding())
.finish();
render_resources.create_bind_group(layout.bind_group(0).id, &shadow_view_bind_group);
// TODO: this should only queue up meshes that are actually visible by each "light view"
for i in 0..extracted_meshes.meshes.len() {
shadow_phase.add(Drawable {
@ -318,13 +428,6 @@ pub fn queue_meshes(
sort_key: 0, // TODO: sort back-to-front
})
}
commands
.entity(view_light_entity)
.insert(MeshViewBindGroups {
view_bind_group: shadow_view_bind_group.id,
mesh_transform_bind_group: mesh_transform_bind_group.id,
});
}
}
}
@ -336,29 +439,39 @@ impl Node for PbrNode {
fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut dyn RenderContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let mesh_meta = world.get_resource::<MeshMeta>().unwrap();
let light_meta = world.get_resource::<LightMeta>().unwrap();
mesh_meta
.transform_uniforms
.write_to_uniform_buffer(render_context);
.write_to_uniform_buffer(&mut render_context.command_encoder);
light_meta
.view_gpu_lights
.write_to_uniform_buffer(render_context);
.write_to_uniform_buffer(&mut render_context.command_encoder);
Ok(())
}
}
type DrawPbrParams<'a> = (
Res<'a, PbrShaders>,
Res<'a, MaterialMeta>,
Res<'a, ExtractedMeshes>,
Query<'a, (&'a ViewUniform, &'a MeshViewBindGroups, &'a ViewLights)>,
type DrawPbrParams<'s, 'w> = (
Res<'w, PbrShaders>,
Res<'w, MaterialMeta>,
Res<'w, MeshMeta>,
Res<'w, ExtractedMeshes>,
Query<
'w,
's,
(
&'w ViewUniformOffset,
&'w ViewLights,
&'w MeshViewBindGroups,
),
>,
);
pub struct DrawPbr {
params: SystemState<DrawPbrParams<'static>>,
params: SystemState<DrawPbrParams<'static, 'static>>,
}
impl DrawPbr {
@ -370,43 +483,41 @@ impl DrawPbr {
}
impl Draw for DrawPbr {
fn draw(
&mut self,
world: &World,
pass: &mut TrackedRenderPass,
fn draw<'w, 's>(
&'s mut self,
world: &'w World,
pass: &mut TrackedRenderPass<'w>,
view: Entity,
draw_key: usize,
sort_key: usize,
) {
let (pbr_shaders, material_meta, extracted_meshes, views) = self.params.get(world);
let (view_uniforms, mesh_view_bind_groups, view_lights) = views.get(view).unwrap();
let layout = &pbr_shaders.pipeline_descriptor.layout;
let extracted_mesh = &extracted_meshes.meshes[draw_key];
pass.set_pipeline(pbr_shaders.pipeline);
let (pbr_shaders, material_meta, mesh_meta, extracted_meshes, views) =
self.params.get(world);
let (view_uniforms, view_lights, mesh_view_bind_groups) = views.get(view).unwrap();
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
pass.set_render_pipeline(&pbr_shaders.into_inner().pipeline);
pass.set_bind_group(
0,
layout.bind_group(0).id,
mesh_view_bind_groups.view_bind_group,
Some(&[
view_uniforms.view_uniform_offset,
view_lights.gpu_light_binding_index,
]),
&mesh_view_bind_groups.view,
&[view_uniforms.offset, view_lights.gpu_light_binding_index],
);
pass.set_bind_group(
1,
layout.bind_group(1).id,
mesh_view_bind_groups.mesh_transform_bind_group,
Some(&[extracted_mesh.transform_binding_offset]),
mesh_meta
.into_inner()
.mesh_transform_bind_group
.as_ref()
.unwrap(),
&[extracted_mesh.transform_binding_offset],
);
pass.set_bind_group(
2,
layout.bind_group(2).id,
material_meta.material_bind_groups[sort_key],
None,
&material_meta.into_inner().material_bind_groups[sort_key],
&[],
);
pass.set_vertex_buffer(0, extracted_mesh.vertex_buffer, 0);
pass.set_vertex_buffer(0, extracted_mesh.vertex_buffer.slice(..));
if let Some(index_info) = &extracted_mesh.index_info {
pass.set_index_buffer(index_info.buffer, 0, IndexFormat::Uint32);
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
pass.draw_indexed(0..index_info.count, 0, 0..1);
} else {
panic!("non-indexed drawing not supported yet")

View file

@ -8,13 +8,11 @@ layout(location = 0) out vec4 v_WorldPosition;
layout(location = 1) out vec3 v_WorldNormal;
layout(location = 2) out vec2 v_Uv;
// View bindings - set 0
layout(set = 0, binding = 0) uniform View {
mat4 ViewProj;
vec3 ViewWorldPosition;
};
// Object bindings - set 1
layout(set = 1, binding = 0) uniform MeshTransform {
mat4 Model;
};

View file

@ -29,12 +29,14 @@ bevy_utils = { path = "../../crates/bevy_utils", version = "0.5.0" }
image = { version = "0.23.12", default-features = false }
# misc
wgpu = "0.8"
serde = { version = "1", features = ["derive"] }
bitflags = "1.2.1"
smallvec = { version = "1.6", features = ["union", "const_generics"] }
once_cell = "1.4.1" # TODO: replace once_cell with std equivalent if/when this lands: https://github.com/rust-lang/rfcs/pull/2788
downcast-rs = "1.2.0"
thiserror = "1.0"
futures-lite = "1.4.0"
anyhow = "1.0"
hex = "0.4.2"
hexasphere = "3.4"

View file

@ -1,6 +0,0 @@
* SubGraphs: Graphs can own other named graphs
* Graph Inputs: Graphs can now have "inputs". These are represented as a single input node, so inputs can be connected to other node slots using the existing apis.
* RenderGraph is now a static specification. No run state is stored
* Graph Nodes impls can only read their internal state when "running". This ensures that they can be used multiple times in parallel. State should be stored in World.
* RenderGraphContext now handles graph inputs and outputs
* Removed RenderGraphStager, RenderGraphExecutor, stager impls, and WgpuRenderGraphExecutor. It is now 100% the render backend's job to decide how to run the RenderGraph ... bevy_render doesn't provide any tools for this.

View file

@ -626,6 +626,18 @@ impl From<Vec4> for Color {
}
}
impl From<Color> for wgpu::Color {
fn from(color: Color) -> Self {
let color = color.as_rgba_linear();
wgpu::Color {
r: color.r() as f64,
g: color.g() as f64,
b: color.b() as f64,
a: color.a() as f64,
}
}
}
impl Mul<f32> for Color {
type Output = Color;

View file

@ -1,16 +1,13 @@
use crate::{
color::Color,
core_pipeline::Transparent2dPhase,
pass::{
LoadOp, Operations, PassDescriptor, RenderPass, RenderPassColorAttachment,
TextureAttachment,
},
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
renderer::RenderContext,
view::ExtractedView,
};
use bevy_ecs::prelude::*;
use wgpu::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor};
pub struct MainPass2dNode {
query: QueryState<&'static RenderPhase<Transparent2dPhase>, With<ExtractedView>>,
@ -42,21 +39,21 @@ impl Node for MainPass2dNode {
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut dyn RenderContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let color_attachment_texture = graph.get_input_texture(Self::IN_COLOR_ATTACHMENT)?;
let pass_descriptor = PassDescriptor {
color_attachments: vec![RenderPassColorAttachment {
attachment: TextureAttachment::Id(color_attachment_texture),
let pass_descriptor = RenderPassDescriptor {
label: Some("main_pass_2d"),
color_attachments: &[RenderPassColorAttachment {
view: color_attachment_texture,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::rgb(0.4, 0.4, 0.4)),
load: LoadOp::Clear(Color::rgb(0.4, 0.4, 0.4).into()),
store: true,
},
}],
depth_stencil_attachment: None,
sample_count: 1,
};
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
@ -67,23 +64,22 @@ impl Node for MainPass2dNode {
.get_manual(world, view_entity)
.expect("view entity should exist");
render_context.begin_render_pass(
&pass_descriptor,
&mut |render_pass: &mut dyn RenderPass| {
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for drawable in transparent_phase.drawn_things.iter() {
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
draw_function.draw(
world,
&mut tracked_pass,
view_entity,
drawable.draw_key,
drawable.sort_key,
);
}
},
);
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for drawable in transparent_phase.drawn_things.iter() {
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
draw_function.draw(
world,
&mut tracked_pass,
view_entity,
drawable.draw_key,
drawable.sort_key,
);
}
Ok(())
}
}

View file

@ -1,16 +1,16 @@
use crate::{
color::Color,
core_pipeline::Transparent3dPhase,
pass::{
LoadOp, Operations, PassDescriptor, RenderPass, RenderPassColorAttachment,
RenderPassDepthStencilAttachment, TextureAttachment,
},
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
renderer::RenderContext,
view::ExtractedView,
};
use bevy_ecs::prelude::*;
use wgpu::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
RenderPassDescriptor,
};
pub struct MainPass3dNode {
query: QueryState<&'static RenderPhase<Transparent3dPhase>, With<ExtractedView>>,
@ -44,29 +44,29 @@ impl Node for MainPass3dNode {
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut dyn RenderContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let color_attachment_texture = graph.get_input_texture(Self::IN_COLOR_ATTACHMENT)?;
let depth_texture = graph.get_input_texture(Self::IN_DEPTH)?;
let pass_descriptor = PassDescriptor {
color_attachments: vec![RenderPassColorAttachment {
attachment: TextureAttachment::Id(color_attachment_texture),
let pass_descriptor = RenderPassDescriptor {
label: Some("main_pass_3d"),
color_attachments: &[RenderPassColorAttachment {
view: color_attachment_texture,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::rgb(0.4, 0.4, 0.4)),
load: LoadOp::Clear(Color::rgb(0.4, 0.4, 0.4).into()),
store: true,
},
}],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
attachment: TextureAttachment::Id(depth_texture),
view: depth_texture,
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
sample_count: 1,
};
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
@ -77,23 +77,21 @@ impl Node for MainPass3dNode {
.get_manual(world, view_entity)
.expect("view entity should exist");
render_context.begin_render_pass(
&pass_descriptor,
&mut |render_pass: &mut dyn RenderPass| {
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for drawable in transparent_phase.drawn_things.iter() {
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
draw_function.draw(
world,
&mut tracked_pass,
view_entity,
drawable.draw_key,
drawable.sort_key,
);
}
},
);
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for drawable in transparent_phase.drawn_things.iter() {
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
draw_function.draw(
world,
&mut tracked_pass,
view_entity,
drawable.draw_key,
drawable.sort_key,
);
}
Ok(())
}
}

View file

@ -13,7 +13,7 @@ impl Node for MainPassDriverNode {
fn run(
&self,
graph: &mut RenderGraphContext,
_render_context: &mut dyn RenderContext,
_render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let extracted_cameras = world.get_resource::<ExtractedCameraNames>().unwrap();
@ -22,7 +22,7 @@ impl Node for MainPassDriverNode {
if let Some(camera_2d) = extracted_cameras.entities.get(CameraPlugin::CAMERA_2D) {
let extracted_camera = world.entity(*camera_2d).get::<ExtractedCamera>().unwrap();
let extracted_window = extracted_windows.get(&extracted_camera.window_id).unwrap();
let swap_chain_texture = extracted_window.swap_chain_texture.unwrap();
let swap_chain_texture = extracted_window.swap_chain_frame.as_ref().unwrap().clone();
graph.run_sub_graph(
core_pipeline::draw_2d_graph::NAME,
vec![
@ -36,13 +36,13 @@ impl Node for MainPassDriverNode {
let extracted_camera = world.entity(*camera_3d).get::<ExtractedCamera>().unwrap();
let depth_texture = world.entity(*camera_3d).get::<ViewDepthTexture>().unwrap();
let extracted_window = extracted_windows.get(&extracted_camera.window_id).unwrap();
let swap_chain_texture = extracted_window.swap_chain_texture.unwrap();
let swap_chain_texture = extracted_window.swap_chain_frame.as_ref().unwrap().clone();
graph.run_sub_graph(
core_pipeline::draw_3d_graph::NAME,
vec![
SlotValue::Entity(*camera_3d),
SlotValue::TextureView(swap_chain_texture),
SlotValue::TextureView(depth_texture.view),
SlotValue::TextureView(depth_texture.view.clone()),
],
)?;
}

View file

@ -5,17 +5,15 @@ mod main_pass_driver;
pub use main_pass_2d::*;
pub use main_pass_3d::*;
pub use main_pass_driver::*;
use wgpu::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage};
use crate::{
camera::{ActiveCameras, CameraPlugin},
render_command::RenderCommandPlugin,
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_phase::{sort_phase_system, RenderPhase},
render_resource::{TextureId, TextureViewId},
renderer::RenderResources,
texture::{
Extent3d, TextureCache, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage,
},
render_resource::{Texture, TextureId, TextureView, TextureViewId},
renderer::RenderDevice,
texture::TextureCache,
view::{ExtractedView, ViewPlugin},
RenderStage,
};
@ -143,12 +141,6 @@ impl Plugin for CorePipelinePlugin {
graph
.add_node_edge(ViewPlugin::VIEW_NODE, node::MAIN_PASS_DEPENDENCIES)
.unwrap();
graph
.add_node_edge(
RenderCommandPlugin::RENDER_COMMAND_QUEUE_NODE,
node::MAIN_PASS_DEPENDENCIES,
)
.unwrap();
graph
.add_node_edge(node::MAIN_PASS_DEPENDENCIES, node::MAIN_PASS_DRIVER)
.unwrap();
@ -159,8 +151,8 @@ pub struct Transparent3dPhase;
pub struct Transparent2dPhase;
pub struct ViewDepthTexture {
pub texture: TextureId,
pub view: TextureViewId,
pub texture: Texture,
pub view: TextureView,
}
pub fn extract_core_pipeline_camera_phases(
@ -186,13 +178,14 @@ pub fn extract_core_pipeline_camera_phases(
pub fn prepare_core_views_system(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
render_resources: Res<RenderResources>,
render_device: Res<RenderDevice>,
views: Query<(Entity, &ExtractedView), With<RenderPhase<Transparent3dPhase>>>,
) {
for (entity, view) in views.iter() {
let cached_texture = texture_cache.get(
&render_resources,
&render_device,
TextureDescriptor {
label: None,
size: Extent3d {
depth_or_array_layers: 1,
width: view.width as u32,

View file

@ -2,9 +2,6 @@ pub mod camera;
pub mod color;
pub mod core_pipeline;
pub mod mesh;
pub mod pass;
pub mod pipeline;
pub mod render_command;
pub mod render_graph;
pub mod render_phase;
pub mod render_resource;
@ -14,20 +11,19 @@ pub mod texture;
pub mod view;
pub use once_cell;
use wgpu::BackendBit;
use crate::{
camera::CameraPlugin,
mesh::MeshPlugin,
render_command::RenderCommandPlugin,
render_graph::RenderGraph,
render_phase::DrawFunctions,
renderer::RenderResources,
texture::TexturePlugin,
renderer::render_system,
texture::ImagePlugin,
view::{ViewPlugin, WindowRenderPlugin},
};
use bevy_app::{App, Plugin, StartupStage};
use bevy_ecs::prelude::*;
use bevy_utils::tracing::warn;
#[derive(Default)]
pub struct RenderPlugin;
@ -59,10 +55,18 @@ pub enum RenderStage {
impl Plugin for RenderPlugin {
fn build(&self, app: &mut App) {
app.add_startup_system_to_stage(
StartupStage::PreStartup,
check_for_render_resource_context.system(),
);
let (instance, device, queue) =
futures_lite::future::block_on(renderer::initialize_renderer(
BackendBit::PRIMARY,
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
..Default::default()
},
&wgpu::DeviceDescriptor::default(),
));
app.insert_resource(device.clone())
.insert_resource(queue.clone());
let mut render_app = App::empty();
let mut extract_stage = SystemStage::parallel();
// don't apply buffers when the stage finishes running
@ -73,8 +77,14 @@ impl Plugin for RenderPlugin {
.add_stage(RenderStage::Prepare, SystemStage::parallel())
.add_stage(RenderStage::Queue, SystemStage::parallel())
.add_stage(RenderStage::PhaseSort, SystemStage::parallel())
.add_stage(RenderStage::Render, SystemStage::parallel())
.add_stage(
RenderStage::Render,
SystemStage::parallel().with_system(render_system.exclusive_system()),
)
.add_stage(RenderStage::Cleanup, SystemStage::parallel())
.insert_resource(instance)
.insert_resource(device)
.insert_resource(queue)
.init_resource::<RenderGraph>()
.init_resource::<DrawFunctions>();
@ -132,12 +142,11 @@ impl Plugin for RenderPlugin {
render_app.world.clear_entities();
});
app.add_plugin(RenderCommandPlugin)
.add_plugin(WindowRenderPlugin)
app.add_plugin(WindowRenderPlugin)
.add_plugin(CameraPlugin)
.add_plugin(ViewPlugin)
.add_plugin(MeshPlugin)
.add_plugin(TexturePlugin);
.add_plugin(ImagePlugin);
}
}
@ -149,11 +158,3 @@ fn extract(app_world: &mut World, render_app: &mut App) {
extract.run(app_world);
extract.apply_buffers(&mut render_app.world);
}
fn check_for_render_resource_context(context: Option<Res<RenderResources>>) {
if context.is_none() {
warn!(
"bevy_render couldn't find a render backend. Perhaps try adding the bevy_wgpu feature/plugin!"
);
}
}

View file

@ -2,19 +2,16 @@ mod conversions;
mod mesh_resource_provider;
pub use mesh_resource_provider::*;
use wgpu::{IndexFormat, PrimitiveTopology, VertexFormat};
use crate::{
pipeline::{
IndexFormat, InputStepMode, PrimitiveTopology, VertexAttribute, VertexBufferLayout,
VertexFormat,
},
render_resource::BufferId,
render_resource::Buffer,
};
use bevy_core::cast_slice;
use bevy_math::*;
use bevy_reflect::TypeUuid;
use bevy_utils::EnumVariantMeta;
use std::{borrow::Cow, collections::BTreeMap};
use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
@ -223,8 +220,8 @@ impl From<&Indices> for IndexFormat {
// TODO: this shouldn't live in the Mesh type
#[derive(Debug, Clone)]
pub struct MeshGpuData {
pub vertex_buffer: BufferId,
pub index_buffer: Option<BufferId>,
pub vertex_buffer: Buffer,
pub index_buffer: Option<Buffer>,
}
// TODO: allow values to be unloaded after been submitting to the GPU to conserve memory
@ -342,27 +339,27 @@ impl Mesh {
})
}
pub fn get_vertex_buffer_layout(&self) -> VertexBufferLayout {
let mut attributes = Vec::new();
let mut accumulated_offset = 0;
for (attribute_name, attribute_values) in self.attributes.iter() {
let vertex_format = VertexFormat::from(attribute_values);
attributes.push(VertexAttribute {
name: attribute_name.clone(),
offset: accumulated_offset,
format: vertex_format,
shader_location: 0,
});
accumulated_offset += vertex_format.get_size();
}
// pub fn get_vertex_buffer_layout(&self) -> VertexBufferLayout {
// let mut attributes = Vec::new();
// let mut accumulated_offset = 0;
// for (attribute_name, attribute_values) in self.attributes.iter() {
// let vertex_format = VertexFormat::from(attribute_values);
// attributes.push(VertexAttribute {
// name: attribute_name.clone(),
// offset: accumulated_offset,
// format: vertex_format,
// shader_location: 0,
// });
// accumulated_offset += vertex_format.get_size();
// }
VertexBufferLayout {
name: Default::default(),
stride: accumulated_offset,
step_mode: InputStepMode::Vertex,
attributes,
}
}
// VertexBufferLayout {
// name: Default::default(),
// stride: accumulated_offset,
// step_mode: InputStepMode::Vertex,
// attributes,
// }
// }
pub fn count_vertices(&self) -> usize {
let mut vertex_count: Option<usize> = None;
@ -489,3 +486,48 @@ fn face_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] {
let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c));
(b - a).cross(c - a).normalize().into()
}
pub trait VertexFormatSize {
fn get_size(self) -> u64;
}
impl VertexFormatSize for wgpu::VertexFormat {
fn get_size(self) -> u64 {
match self {
VertexFormat::Uint8x2 => 2,
VertexFormat::Uint8x4 => 4,
VertexFormat::Sint8x2 => 2,
VertexFormat::Sint8x4 => 4,
VertexFormat::Unorm8x2 => 2,
VertexFormat::Unorm8x4 => 4,
VertexFormat::Snorm8x2 => 2,
VertexFormat::Snorm8x4 => 4,
VertexFormat::Uint16x2 => 2 * 2,
VertexFormat::Uint16x4 => 2 * 4,
VertexFormat::Sint16x2 => 2 * 2,
VertexFormat::Sint16x4 => 2 * 4,
VertexFormat::Unorm16x2 => 2 * 2,
VertexFormat::Unorm16x4 => 2 * 4,
VertexFormat::Snorm16x2 => 2 * 2,
VertexFormat::Snorm16x4 => 2 * 4,
VertexFormat::Float16x2 => 2 * 2,
VertexFormat::Float16x4 => 2 * 4,
VertexFormat::Float32 => 4,
VertexFormat::Float32x2 => 4 * 2,
VertexFormat::Float32x3 => 4 * 3,
VertexFormat::Float32x4 => 4 * 4,
VertexFormat::Uint32 => 4,
VertexFormat::Uint32x2 => 4 * 2,
VertexFormat::Uint32x3 => 4 * 3,
VertexFormat::Uint32x4 => 4 * 4,
VertexFormat::Sint32 => 4,
VertexFormat::Sint32x2 => 4 * 2,
VertexFormat::Sint32x3 => 4 * 3,
VertexFormat::Sint32x4 => 4 * 4,
VertexFormat::Float64 => 8,
VertexFormat::Float64x2 => 8 * 2,
VertexFormat::Float64x3 => 8 * 3,
VertexFormat::Float64x4 => 8 * 4,
}
}
}

View file

@ -1,32 +1,19 @@
use crate::{
mesh::{Mesh, MeshGpuData},
render_resource::{BufferInfo, BufferUsage},
renderer::{RenderResourceContext, RenderResources},
render_resource::Buffer,
renderer::RenderDevice,
};
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_asset::{AssetEvent, Assets};
use bevy_ecs::prelude::*;
use bevy_utils::HashSet;
fn remove_current_mesh_resources(
render_resource_context: &dyn RenderResourceContext,
handle: &Handle<Mesh>,
meshes: &mut Assets<Mesh>,
) {
if let Some(gpu_data) = meshes.get_mut(handle).and_then(|m| m.gpu_data.take()) {
render_resource_context.remove_buffer(gpu_data.vertex_buffer);
if let Some(index_buffer) = gpu_data.index_buffer {
render_resource_context.remove_buffer(index_buffer);
}
}
}
use wgpu::{util::BufferInitDescriptor, BufferUsage};
pub fn mesh_resource_provider_system(
render_resource_context: Res<RenderResources>,
render_device: Res<RenderDevice>,
mut meshes: ResMut<Assets<Mesh>>,
mut mesh_events: EventReader<AssetEvent<Mesh>>,
) {
let mut changed_meshes = HashSet::default();
let render_resource_context = &**render_resource_context;
for event in mesh_events.iter() {
match event {
AssetEvent::Created { ref handle } => {
@ -38,7 +25,6 @@ pub fn mesh_resource_provider_system(
// remove_current_mesh_resources(render_resource_context, handle, &mut meshes);
}
AssetEvent::Removed { ref handle } => {
remove_current_mesh_resources(render_resource_context, handle, &mut meshes);
// if mesh was modified and removed in the same update, ignore the modification
// events are ordered so future modification events are ok
changed_meshes.remove(handle);
@ -56,21 +42,21 @@ pub fn mesh_resource_provider_system(
}
let vertex_buffer_data = mesh.get_vertex_buffer_data();
let vertex_buffer = render_resource_context.create_buffer_with_data(
BufferInfo {
buffer_usage: BufferUsage::VERTEX,
..Default::default()
let vertex_buffer = Buffer::from(render_device.create_buffer_with_data(
&BufferInitDescriptor {
usage: BufferUsage::VERTEX,
label: None,
contents: &vertex_buffer_data,
},
&vertex_buffer_data,
);
));
let index_buffer = mesh.get_index_buffer_bytes().map(|data| {
render_resource_context.create_buffer_with_data(
BufferInfo {
buffer_usage: BufferUsage::INDEX,
..Default::default()
},
&data,
Buffer::from(
render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsage::INDEX,
contents: &data,
label: None,
}),
)
});

View file

@ -1,8 +1,8 @@
use crate::{
mesh::{Indices, Mesh},
pipeline::PrimitiveTopology,
};
use bevy_math::{Vec2, Vec3};
use wgpu::PrimitiveTopology;
/// A cylinder with hemispheres at the top and bottom
#[derive(Debug, Copy, Clone)]

View file

@ -1,9 +1,6 @@
use crate::mesh::{Indices, Mesh};
use hexasphere::shapes::IcoSphere;
use crate::{
mesh::{Indices, Mesh},
pipeline::PrimitiveTopology,
};
use wgpu::PrimitiveTopology;
/// A sphere made from a subdivided Icosahedron.
#[derive(Debug, Clone, Copy)]

View file

@ -1,5 +1,4 @@
use super::{Indices, Mesh};
use crate::pipeline::PrimitiveTopology;
use bevy_math::*;
#[derive(Debug, Copy, Clone)]
@ -274,3 +273,4 @@ pub use capsule::{Capsule, CapsuleUvProfile};
pub use icosphere::Icosphere;
pub use torus::Torus;
pub use uvsphere::UVSphere;
use wgpu::PrimitiveTopology;

View file

@ -1,8 +1,6 @@
use crate::{
mesh::{Indices, Mesh},
pipeline::PrimitiveTopology,
};
use crate::mesh::{Indices, Mesh};
use bevy_math::Vec3;
use wgpu::PrimitiveTopology;
/// A torus (donut) shape.
#[derive(Debug, Clone, Copy)]

View file

@ -1,7 +1,6 @@
use crate::{
mesh::{Indices, Mesh},
pipeline::PrimitiveTopology,
};
use wgpu::PrimitiveTopology;
use crate::mesh::{Indices, Mesh};
use std::f32::consts::PI;
/// A sphere made of sectors and stacks

View file

@ -1,18 +0,0 @@
use crate::{
pipeline::{BindGroupDescriptorId, PipelineId},
render_resource::BindGroupId,
renderer::RenderContext,
};
pub trait ComputePass {
fn get_render_context(&self) -> &dyn RenderContext;
fn set_pipeline(&mut self, pipeline: PipelineId);
fn dispatch(&mut self, x: u32, y: u32, z: u32);
fn set_bind_group(
&mut self,
index: u32,
bind_group_descriptor_id: BindGroupDescriptorId,
bind_group: BindGroupId,
dynamic_uniform_indices: Option<&[u32]>,
);
}

View file

@ -1,10 +0,0 @@
mod compute_pass;
mod ops;
#[allow(clippy::module_inception)]
mod pass;
mod render_pass;
pub use compute_pass::*;
pub use ops::*;
pub use pass::*;
pub use render_pass::*;

View file

@ -1,17 +0,0 @@
/// Operation to perform to the output attachment at the start of a renderpass.
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
pub enum LoadOp<V> {
/// Clear with a specified value.
Clear(V),
/// Load from memory.
Load,
}
/// Pair of load and store operations for an attachment aspect.
#[derive(Clone, Debug, Hash, PartialEq)]
pub struct Operations<V> {
/// How data should be read through this attachment.
pub load: LoadOp<V>,
/// Whether data will be written to through this attachment.
pub store: bool,
}

View file

@ -1,55 +0,0 @@
use crate::{color::Color, pass::Operations, render_resource::TextureViewId};
#[derive(Debug, Clone)]
pub enum TextureAttachment {
Id(TextureViewId),
Input(String),
}
impl TextureAttachment {
pub fn get_texture_view_id(&self) -> Option<TextureViewId> {
if let TextureAttachment::Id(texture_id) = self {
Some(*texture_id)
} else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct ClearColor(pub Color);
impl Default for ClearColor {
fn default() -> Self {
Self(Color::rgb(0.4, 0.4, 0.4))
}
}
#[derive(Debug, Clone)]
pub struct RenderPassColorAttachment {
/// The actual color attachment.
pub attachment: TextureAttachment,
/// The resolve target for this color attachment, if any.
pub resolve_target: Option<TextureAttachment>,
/// What operations will be performed on this color attachment.
pub ops: Operations<Color>,
}
#[derive(Debug, Clone)]
pub struct RenderPassDepthStencilAttachment {
pub attachment: TextureAttachment,
/// What operations will be performed on the depth part of the attachment.
pub depth_ops: Option<Operations<f32>>,
/// What operations will be performed on the stencil part of the attachment.
pub stencil_ops: Option<Operations<u32>>,
}
// A set of pipeline bindings and draw calls with color and depth outputs
#[derive(Debug, Clone)]
pub struct PassDescriptor {
pub color_attachments: Vec<RenderPassColorAttachment>,
pub depth_stencil_attachment: Option<RenderPassDepthStencilAttachment>,
pub sample_count: u32,
}

View file

@ -1,25 +0,0 @@
use crate::{
pipeline::{BindGroupDescriptorId, IndexFormat, PipelineId},
render_resource::{BindGroupId, BufferId},
renderer::RenderContext,
};
use std::ops::Range;
pub trait RenderPass {
fn get_render_context(&self) -> &dyn RenderContext;
fn set_index_buffer(&mut self, buffer: BufferId, offset: u64, index_format: IndexFormat);
fn set_vertex_buffer(&mut self, start_slot: u32, buffer: BufferId, offset: u64);
fn set_pipeline(&mut self, pipeline: PipelineId);
fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32);
fn set_scissor_rect(&mut self, x: u32, y: u32, w: u32, h: u32);
fn set_stencil_reference(&mut self, reference: u32);
fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>);
fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);
fn set_bind_group(
&mut self,
index: u32,
bind_group_descriptor_id: BindGroupDescriptorId,
bind_group: BindGroupId,
dynamic_uniform_indices: Option<&[u32]>,
);
}

View file

@ -1,49 +0,0 @@
use super::BindingDescriptor;
use bevy_utils::FixedState;
use std::hash::{BuildHasher, Hash, Hasher};
#[derive(Clone, Debug, Eq)]
pub struct BindGroupDescriptor {
pub index: u32,
pub bindings: Vec<BindingDescriptor>,
pub id: BindGroupDescriptorId,
}
#[derive(Hash, Copy, Clone, Eq, PartialEq, Debug)]
pub struct BindGroupDescriptorId(u64);
impl BindGroupDescriptor {
pub fn new(index: u32, bindings: Vec<BindingDescriptor>) -> Self {
let mut descriptor = BindGroupDescriptor {
index,
bindings,
id: BindGroupDescriptorId(0),
};
descriptor.update_id();
descriptor
}
pub fn update_id(&mut self) {
let mut hasher = FixedState::default().build_hasher();
self.hash(&mut hasher);
self.id = BindGroupDescriptorId(hasher.finish());
}
}
impl Hash for BindGroupDescriptor {
fn hash<H: Hasher>(&self, state: &mut H) {
// TODO: remove index from hash state (or at least id), and update the PartialEq implem.
// index is not considered a part of a bind group on the gpu.
// bind groups are bound to indices in pipelines.
self.index.hash(state);
self.bindings.hash(state);
}
}
impl PartialEq for BindGroupDescriptor {
fn eq(&self, other: &Self) -> bool {
// This MUST be kept in sync with the hash implementation above
self.index == other.index && self.bindings == other.bindings
}
}

View file

@ -1,82 +0,0 @@
use super::UniformProperty;
use crate::texture::{
StorageTextureAccess, TextureFormat, TextureSampleType, TextureViewDimension,
};
bitflags::bitflags! {
pub struct BindingShaderStage: u32 {
const VERTEX = 1;
const FRAGMENT = 2;
const COMPUTE = 4;
}
}
#[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct BindingDescriptor {
pub name: String,
pub index: u32,
pub bind_type: BindType,
pub shader_stage: BindingShaderStage,
}
impl BindingDescriptor {
pub fn set_dynamic(&mut self, is_dynamic: bool) -> bool {
self.bind_type.set_dynamic(is_dynamic)
}
}
#[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum BindType {
Uniform {
has_dynamic_offset: bool,
property: UniformProperty,
},
StorageBuffer {
has_dynamic_offset: bool,
readonly: bool,
},
Sampler {
/// The sampling result is produced based on more than a single color sample from a
/// texture, e.g. when bilinear interpolation is enabled.
///
/// A filtering sampler can only be used with a filterable texture.
filtering: bool,
/// Use as a comparison sampler instead of a normal sampler.
/// For more info take a look at the analogous functionality in OpenGL: <https://www.khronos.org/opengl/wiki/Sampler_Object#Comparison_mode>.
comparison: bool,
},
Texture {
multisampled: bool,
view_dimension: TextureViewDimension,
sample_type: TextureSampleType,
},
StorageTexture {
/// Allowed access to this texture.
access: StorageTextureAccess,
/// Format of the texture.
format: TextureFormat,
/// Dimension of the texture view that is going to be sampled.
view_dimension: TextureViewDimension,
},
}
impl BindType {
pub fn get_uniform_size(&self) -> Option<u64> {
match self {
BindType::Uniform { property, .. } => Some(property.get_size()),
_ => None,
}
}
pub fn set_dynamic(&mut self, is_dynamic: bool) -> bool {
match self {
BindType::Uniform {
has_dynamic_offset, ..
} => {
*has_dynamic_offset = is_dynamic;
true
}
_ => false,
}
}
}

View file

@ -1,29 +0,0 @@
use super::PipelineLayout;
use crate::shader::ComputeShaderStages;
use bevy_reflect::TypeUuid;
#[derive(Clone, Debug, TypeUuid)]
#[uuid = "359c1fff-0c86-4fbb-8b66-4e2af422a2f1"]
pub struct ComputePipelineDescriptor {
pub name: Option<String>,
pub layout: PipelineLayout,
pub shader_stages: ComputeShaderStages,
}
impl ComputePipelineDescriptor {
pub fn new(shader_stages: ComputeShaderStages, layout: PipelineLayout) -> Self {
ComputePipelineDescriptor {
name: None,
layout,
shader_stages,
}
}
pub fn default_config(shader_stages: ComputeShaderStages, layout: PipelineLayout) -> Self {
ComputePipelineDescriptor {
name: None,
layout,
shader_stages,
}
}
}

View file

@ -1,18 +0,0 @@
mod bind_group;
mod binding;
mod compute_pipeline;
mod pipeline_layout;
#[allow(clippy::module_inception)]
mod render_pipeline;
mod state_descriptors;
mod vertex_buffer_descriptor;
mod vertex_format;
pub use bind_group::*;
pub use binding::*;
pub use compute_pipeline::*;
pub use pipeline_layout::*;
pub use render_pipeline::*;
pub use state_descriptors::*;
pub use vertex_buffer_descriptor::*;
pub use vertex_format::*;

View file

@ -1,127 +0,0 @@
use super::{BindGroupDescriptor, VertexBufferLayout};
use crate::shader::ShaderLayout;
use bevy_utils::HashMap;
use std::hash::Hash;
#[derive(Clone, Debug, Default)]
pub struct PipelineLayout {
pub bind_groups: Vec<BindGroupDescriptor>,
// TODO: rename me
pub vertex_buffer_descriptors: Vec<VertexBufferLayout>,
}
impl PipelineLayout {
// TODO: make direct indexing work to avoid a full scan
pub fn get_bind_group(&self, index: u32) -> Option<&BindGroupDescriptor> {
self.bind_groups
.iter()
.find(|bind_group| bind_group.index == index)
}
pub fn get_bind_group_mut(&mut self, index: u32) -> Option<&mut BindGroupDescriptor> {
self.bind_groups
.iter_mut()
.find(|bind_group| bind_group.index == index)
}
pub fn bind_group(&self, index: u32) -> &BindGroupDescriptor {
self.get_bind_group(index).expect("bind group exists")
}
pub fn bind_group_mut(&mut self, index: u32) -> &mut BindGroupDescriptor {
self.get_bind_group_mut(index).expect("bind group exists")
}
pub fn from_shader_layouts(shader_layouts: &mut [ShaderLayout]) -> Self {
let mut bind_groups = HashMap::<u32, BindGroupDescriptor>::default();
let mut vertex_buffer_descriptors = Vec::new();
for shader_layout in shader_layouts.iter_mut() {
for shader_bind_group in shader_layout.bind_groups.iter_mut() {
match bind_groups.get_mut(&shader_bind_group.index) {
Some(bind_group) => {
for shader_binding in shader_bind_group.bindings.iter() {
if let Some(binding) = bind_group
.bindings
.iter_mut()
.find(|binding| binding.index == shader_binding.index)
{
binding.shader_stage |= shader_binding.shader_stage;
if binding.bind_type != shader_binding.bind_type
|| binding.name != shader_binding.name
|| binding.index != shader_binding.index
{
panic!("Binding {} in BindGroup {} does not match across all shader types: {:?} {:?}.", binding.index, bind_group.index, binding, shader_binding);
}
} else {
bind_group.bindings.push(shader_binding.clone());
}
}
bind_group.update_id();
}
None => {
bind_groups.insert(shader_bind_group.index, shader_bind_group.clone());
}
}
}
}
for vertex_buffer_descriptor in shader_layouts[0].vertex_buffer_layout.iter() {
vertex_buffer_descriptors.push(vertex_buffer_descriptor.clone());
}
let mut bind_groups_result = bind_groups
.drain()
.map(|(_, value)| value)
.collect::<Vec<BindGroupDescriptor>>();
// NOTE: for some reason bind groups need to be sorted by index. this is likely an issue
// with bevy and not with wgpu TODO: try removing this
bind_groups_result.sort_by(|a, b| a.index.partial_cmp(&b.index).unwrap());
PipelineLayout {
bind_groups: bind_groups_result,
vertex_buffer_descriptors,
}
}
pub fn update_bind_group_ids(&mut self) {
for bind_group in self.bind_groups.iter_mut() {
bind_group.update_id();
}
}
}
#[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum UniformProperty {
UInt,
Int,
IVec2,
Float,
UVec4,
Vec2,
Vec3,
Vec4,
Mat3,
Mat4,
Struct(Vec<UniformProperty>),
Array(Box<UniformProperty>, usize),
}
impl UniformProperty {
pub fn get_size(&self) -> u64 {
match self {
UniformProperty::UInt => 4,
UniformProperty::Int => 4,
UniformProperty::IVec2 => 4 * 2,
UniformProperty::Float => 4,
UniformProperty::UVec4 => 4 * 4,
UniformProperty::Vec2 => 4 * 2,
UniformProperty::Vec3 => 4 * 3,
UniformProperty::Vec4 => 4 * 4,
UniformProperty::Mat3 => 4 * 4 * 3,
UniformProperty::Mat4 => 4 * 4 * 4,
UniformProperty::Struct(properties) => properties.iter().map(|p| p.get_size()).sum(),
UniformProperty::Array(property, length) => property.get_size() * *length as u64,
}
}
}

View file

@ -1,120 +0,0 @@
use super::{
state_descriptors::{
BlendFactor, BlendOperation, ColorWrite, CompareFunction, Face, FrontFace,
PrimitiveTopology,
},
PipelineLayout,
};
use crate::{
pipeline::{
BlendComponent, BlendState, ColorTargetState, DepthBiasState, DepthStencilState,
MultisampleState, PolygonMode, PrimitiveState, StencilFaceState, StencilState,
},
shader::ShaderStages,
texture::TextureFormat,
};
use bevy_reflect::{TypeUuid, Uuid};
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct PipelineId(Uuid);
impl PipelineId {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
PipelineId(Uuid::new_v4())
}
}
#[derive(Clone, Debug, TypeUuid)]
#[uuid = "ebfc1d11-a2a4-44cb-8f12-c49cc631146c"]
pub struct RenderPipelineDescriptor {
pub name: Option<String>,
pub layout: PipelineLayout,
pub shader_stages: ShaderStages,
pub primitive: PrimitiveState,
pub depth_stencil: Option<DepthStencilState>,
pub multisample: MultisampleState,
/// The effect of draw calls on the color aspect of the output target.
pub color_target_states: Vec<ColorTargetState>,
}
impl RenderPipelineDescriptor {
pub fn new(shader_stages: ShaderStages, layout: PipelineLayout) -> Self {
RenderPipelineDescriptor {
name: None,
layout,
color_target_states: Vec::new(),
depth_stencil: None,
shader_stages,
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: Some(Face::Back),
polygon_mode: PolygonMode::Fill,
clamp_depth: false,
conservative: false,
},
multisample: MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
}
}
pub fn default_config(shader_stages: ShaderStages, layout: PipelineLayout) -> Self {
RenderPipelineDescriptor {
name: None,
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: Some(Face::Back),
polygon_mode: PolygonMode::Fill,
clamp_depth: false,
conservative: false,
},
layout,
depth_stencil: Some(DepthStencilState {
format: TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: CompareFunction::Less,
stencil: StencilState {
front: StencilFaceState::IGNORE,
back: StencilFaceState::IGNORE,
read_mask: 0,
write_mask: 0,
},
bias: DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
color_target_states: vec![ColorTargetState {
format: TextureFormat::default(),
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrite::ALL,
}],
multisample: MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
shader_stages,
}
}
}

View file

@ -1,232 +0,0 @@
use crate::texture::TextureFormat;
use bevy_reflect::{Reflect, ReflectDeserialize};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)]
pub struct DepthStencilState {
pub format: TextureFormat,
pub depth_write_enabled: bool,
pub depth_compare: CompareFunction,
pub stencil: StencilState,
pub bias: DepthBiasState,
}
#[derive(Clone, Debug)]
pub struct StencilState {
pub front: StencilFaceState,
pub back: StencilFaceState,
pub read_mask: u32,
pub write_mask: u32,
}
#[derive(Clone, Debug)]
pub struct MultisampleState {
/// The number of samples calculated per pixel (for MSAA). For non-multisampled textures,
/// this should be `1`
pub count: u32,
/// Bitmask that restricts the samples of a pixel modified by this pipeline. All samples
/// can be enabled using the value `!0`
pub mask: u64,
/// When enabled, produces another sample mask per pixel based on the alpha output value, that
/// is ANDed with the sample_mask and the primitive coverage to restrict the set of samples
/// affected by a primitive.
///
/// The implicit mask produced for alpha of zero is guaranteed to be zero, and for alpha of one
/// is guaranteed to be all 1-s.
pub alpha_to_coverage_enabled: bool,
}
#[derive(Clone, Debug)]
pub struct DepthBiasState {
/// Constant depth biasing factor, in basic units of the depth format.
pub constant: i32,
/// Slope depth biasing factor.
pub slope_scale: f32,
/// Depth bias clamp value (absolute).
pub clamp: f32,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum StencilOperation {
Keep = 0,
Zero = 1,
Replace = 2,
Invert = 3,
IncrementClamp = 4,
DecrementClamp = 5,
IncrementWrap = 6,
DecrementWrap = 7,
}
#[derive(Clone, Debug, PartialEq)]
pub struct StencilFaceState {
pub compare: CompareFunction,
pub fail_op: StencilOperation,
pub depth_fail_op: StencilOperation,
pub pass_op: StencilOperation,
}
impl StencilFaceState {
pub const IGNORE: Self = StencilFaceState {
compare: CompareFunction::Always,
fail_op: StencilOperation::Keep,
depth_fail_op: StencilOperation::Keep,
pass_op: StencilOperation::Keep,
};
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum CompareFunction {
Never = 0,
Less = 1,
Equal = 2,
LessEqual = 3,
Greater = 4,
NotEqual = 5,
GreaterEqual = 6,
Always = 7,
}
/// Describes how the VertexAttributes should be interpreted while rendering
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, Reflect)]
#[reflect_value(Serialize, Deserialize, PartialEq, Hash)]
pub enum PrimitiveTopology {
PointList = 0,
LineList = 1,
LineStrip = 2,
TriangleList = 3,
TriangleStrip = 4,
}
impl Default for PrimitiveTopology {
fn default() -> Self {
PrimitiveTopology::TriangleList
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum FrontFace {
Ccw = 0,
Cw = 1,
}
impl Default for FrontFace {
fn default() -> Self {
FrontFace::Ccw
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum Face {
Front = 0,
Back = 1,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum PolygonMode {
/// Polygons are filled
Fill = 0,
/// Polygons are draw as line segments
Line = 1,
/// Polygons are draw as points
Point = 2,
}
impl Default for PolygonMode {
fn default() -> Self {
PolygonMode::Fill
}
}
#[derive(Clone, Debug, Default)]
pub struct PrimitiveState {
pub topology: PrimitiveTopology,
pub strip_index_format: Option<IndexFormat>,
pub front_face: FrontFace,
pub cull_mode: Option<Face>,
pub polygon_mode: PolygonMode,
pub clamp_depth: bool,
pub conservative: bool,
}
#[derive(Clone, Debug)]
pub struct ColorTargetState {
pub format: TextureFormat,
pub blend: Option<BlendState>,
pub write_mask: ColorWrite,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BlendComponent {
pub src_factor: BlendFactor,
pub dst_factor: BlendFactor,
pub operation: BlendOperation,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BlendState {
pub alpha: BlendComponent,
pub color: BlendComponent,
}
bitflags::bitflags! {
#[repr(transparent)]
pub struct ColorWrite: u32 {
const RED = 1;
const GREEN = 2;
const BLUE = 4;
const ALPHA = 8;
const COLOR = 7;
const ALL = 15;
}
}
impl Default for ColorWrite {
fn default() -> Self {
ColorWrite::ALL
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum BlendFactor {
Zero = 0,
One = 1,
Src = 2,
OneMinusSrc = 3,
SrcAlpha = 4,
OneMinusSrcAlpha = 5,
Dst = 6,
OneMinusDst = 7,
DstAlpha = 8,
OneMinusDstAlpha = 9,
SrcAlphaSaturated = 10,
Constant = 11,
OneMinusConstant = 12,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum BlendOperation {
Add = 0,
Subtract = 1,
ReverseSubtract = 2,
Min = 3,
Max = 4,
}
impl Default for BlendOperation {
fn default() -> Self {
BlendOperation::Add
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, Reflect)]
#[reflect_value(Hash, PartialEq, Serialize, Deserialize)]
pub enum IndexFormat {
Uint16 = 0,
Uint32 = 1,
}
impl Default for IndexFormat {
fn default() -> Self {
IndexFormat::Uint32
}
}

View file

@ -1,56 +0,0 @@
use super::VertexFormat;
use bevy_reflect::{Reflect, ReflectDeserialize};
use serde::{Deserialize, Serialize};
use std::{
borrow::Cow,
hash::{Hash, Hasher},
};
#[derive(Clone, Debug, Eq, PartialEq, Default, Reflect, Serialize, Deserialize)]
#[reflect_value(Serialize, Deserialize, PartialEq)]
pub struct VertexBufferLayout {
pub name: Cow<'static, str>,
pub stride: u64,
pub step_mode: InputStepMode,
pub attributes: Vec<VertexAttribute>,
}
impl VertexBufferLayout {
pub fn new_from_attribute(
attribute: VertexAttribute,
step_mode: InputStepMode,
) -> VertexBufferLayout {
VertexBufferLayout {
name: attribute.name.clone(),
stride: attribute.format.get_size(),
step_mode,
attributes: vec![attribute],
}
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub enum InputStepMode {
Vertex = 0,
Instance = 1,
}
impl Default for InputStepMode {
fn default() -> Self {
InputStepMode::Vertex
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct VertexAttribute {
pub name: Cow<'static, str>,
pub format: VertexFormat,
pub offset: u64,
pub shader_location: u32,
}
/// Internally, `bevy_render` uses hashes to identify vertex attribute names.
pub fn get_vertex_attribute_name_id(name: &str) -> u64 {
let mut hasher = bevy_utils::AHasher::default();
hasher.write(&name.as_bytes());
hasher.finish()
}

View file

@ -1,136 +0,0 @@
use crate::color::Color;
use bevy_math::{Mat4, Vec2, Vec3, Vec4};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub enum VertexFormat {
Uint8x2 = 1,
Uint8x4 = 3,
Sint8x2 = 5,
Sint8x4 = 7,
Unorm8x2 = 9,
Unorm8x4 = 11,
Snorm8x2 = 14,
Snorm8x4 = 16,
Uint16x2 = 18,
Uint16x4 = 20,
Sint16x2 = 22,
Sint16x4 = 24,
Unorm16x2 = 26,
Unorm16x4 = 28,
Snorm16x2 = 30,
Snorm16x4 = 32,
Float16x2 = 34,
Float16x4 = 36,
Float32 = 37,
Float32x2 = 38,
Float32x3 = 39,
Float32x4 = 40,
Uint32 = 41,
Uint32x2 = 42,
Uint32x3 = 43,
Uint32x4 = 44,
Sint32 = 45,
Sint32x2 = 46,
Sint32x3 = 47,
Sint32x4 = 48,
}
impl VertexFormat {
pub fn get_size(&self) -> u64 {
match *self {
VertexFormat::Uint8x2 => 2,
VertexFormat::Uint8x4 => 4,
VertexFormat::Sint8x2 => 2,
VertexFormat::Sint8x4 => 4,
VertexFormat::Unorm8x2 => 2,
VertexFormat::Unorm8x4 => 4,
VertexFormat::Snorm8x2 => 2,
VertexFormat::Snorm8x4 => 4,
VertexFormat::Uint16x2 => 2 * 2,
VertexFormat::Uint16x4 => 2 * 4,
VertexFormat::Sint16x2 => 2 * 2,
VertexFormat::Sint16x4 => 2 * 4,
VertexFormat::Unorm16x2 => 2 * 2,
VertexFormat::Unorm16x4 => 2 * 4,
VertexFormat::Snorm16x2 => 2 * 2,
VertexFormat::Snorm16x4 => 2 * 4,
VertexFormat::Float16x2 => 2 * 2,
VertexFormat::Float16x4 => 2 * 4,
VertexFormat::Float32 => 4,
VertexFormat::Float32x2 => 4 * 2,
VertexFormat::Float32x3 => 4 * 3,
VertexFormat::Float32x4 => 4 * 4,
VertexFormat::Uint32 => 4,
VertexFormat::Uint32x2 => 4 * 2,
VertexFormat::Uint32x3 => 4 * 3,
VertexFormat::Uint32x4 => 4 * 4,
VertexFormat::Sint32 => 4,
VertexFormat::Sint32x2 => 4 * 2,
VertexFormat::Sint32x3 => 4 * 3,
VertexFormat::Sint32x4 => 4 * 4,
}
}
}
pub trait AsVertexFormats {
fn as_vertex_formats() -> &'static [VertexFormat];
}
impl AsVertexFormats for f32 {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[VertexFormat::Float32]
}
}
impl AsVertexFormats for Vec2 {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[VertexFormat::Float32x2]
}
}
impl AsVertexFormats for Vec3 {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[VertexFormat::Float32x3]
}
}
impl AsVertexFormats for Vec4 {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[VertexFormat::Float32x4]
}
}
impl AsVertexFormats for Mat4 {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[
VertexFormat::Float32x4,
VertexFormat::Float32x4,
VertexFormat::Float32x4,
VertexFormat::Float32x4,
]
}
}
impl AsVertexFormats for Color {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[VertexFormat::Float32x4]
}
}
impl AsVertexFormats for [f32; 2] {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[VertexFormat::Float32x2]
}
}
impl AsVertexFormats for [f32; 3] {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[VertexFormat::Float32x3]
}
}
impl AsVertexFormats for [f32; 4] {
fn as_vertex_formats() -> &'static [VertexFormat] {
&[VertexFormat::Float32x4]
}
}

View file

@ -1,51 +0,0 @@
mod render_command_queue;
pub use render_command_queue::*;
use crate::{
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
renderer::RenderContext,
RenderStage,
};
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
#[derive(Default)]
pub struct RenderCommandPlugin;
impl RenderCommandPlugin {
pub const RENDER_COMMAND_QUEUE_NODE: &'static str = "render_command_queue";
}
impl Plugin for RenderCommandPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<RenderCommandQueue>();
let render_app = app.sub_app_mut(0);
render_app.add_system_to_stage(RenderStage::Extract, extract_render_commands.system());
let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
graph.add_node(Self::RENDER_COMMAND_QUEUE_NODE, RenderCommandQueueNode);
}
}
fn extract_render_commands(
mut commands: Commands,
mut render_command_queue: ResMut<RenderCommandQueue>,
) {
let mut queue = RenderCommandQueue::default();
queue.extend(&mut render_command_queue);
commands.insert_resource(queue);
}
pub struct RenderCommandQueueNode;
impl Node for RenderCommandQueueNode {
fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut dyn RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let queue = world.get_resource::<RenderCommandQueue>().unwrap();
queue.execute(render_context);
Ok(())
}
}

View file

@ -1,227 +0,0 @@
use crate::{
render_resource::{BufferId, TextureId},
renderer::RenderContext,
texture::Extent3d,
};
#[derive(Clone, Debug)]
pub enum Command {
CopyBufferToBuffer {
source_buffer: BufferId,
source_offset: u64,
destination_buffer: BufferId,
destination_offset: u64,
size: u64,
},
CopyBufferToTexture {
source_buffer: BufferId,
source_offset: u64,
source_bytes_per_row: u32,
destination_texture: TextureId,
destination_origin: [u32; 3],
destination_mip_level: u32,
size: Extent3d,
},
CopyTextureToTexture {
source_texture: TextureId,
source_origin: [u32; 3],
source_mip_level: u32,
destination_texture: TextureId,
destination_origin: [u32; 3],
destination_mip_level: u32,
size: Extent3d,
},
CopyTextureToBuffer {
source_texture: TextureId,
source_origin: [u32; 3],
source_mip_level: u32,
destination_buffer: BufferId,
destination_offset: u64,
destination_bytes_per_row: u32,
size: Extent3d,
},
// TODO: Frees probably don't need to be queued?
FreeBuffer(BufferId),
}
#[derive(Debug, Default, Clone)]
pub struct RenderCommandQueue {
// TODO: this shouldn't really need a mutex. it just needs to be shared on whatever thread it's
// scheduled on
queue: Vec<Command>,
}
impl RenderCommandQueue {
fn push(&mut self, command: Command) {
self.queue.push(command);
}
pub fn copy_buffer_to_buffer(
&mut self,
source_buffer: BufferId,
source_offset: u64,
destination_buffer: BufferId,
destination_offset: u64,
size: u64,
) {
self.push(Command::CopyBufferToBuffer {
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
});
}
#[allow(clippy::too_many_arguments)]
pub fn copy_buffer_to_texture(
&mut self,
source_buffer: BufferId,
source_offset: u64,
source_bytes_per_row: u32,
destination_texture: TextureId,
destination_origin: [u32; 3],
destination_mip_level: u32,
size: Extent3d,
) {
self.push(Command::CopyBufferToTexture {
source_buffer,
source_offset,
source_bytes_per_row,
destination_texture,
destination_origin,
destination_mip_level,
size,
});
}
#[allow(clippy::too_many_arguments)]
pub fn copy_texture_to_buffer(
&mut self,
source_texture: TextureId,
source_origin: [u32; 3],
source_mip_level: u32,
destination_buffer: BufferId,
destination_offset: u64,
destination_bytes_per_row: u32,
size: Extent3d,
) {
self.push(Command::CopyTextureToBuffer {
source_texture,
source_origin,
source_mip_level,
destination_buffer,
destination_offset,
destination_bytes_per_row,
size,
})
}
#[allow(clippy::too_many_arguments)]
pub fn copy_texture_to_texture(
&mut self,
source_texture: TextureId,
source_origin: [u32; 3],
source_mip_level: u32,
destination_texture: TextureId,
destination_origin: [u32; 3],
destination_mip_level: u32,
size: Extent3d,
) {
self.push(Command::CopyTextureToTexture {
source_texture,
source_origin,
source_mip_level,
destination_texture,
destination_origin,
destination_mip_level,
size,
})
}
pub fn free_buffer(&mut self, buffer: BufferId) {
self.push(Command::FreeBuffer(buffer));
}
pub fn clear(&mut self) {
self.queue.clear();
}
pub fn extend(&mut self, other: &mut RenderCommandQueue) {
self.queue.extend(other.queue.drain(..));
}
// TODO: Ideally this consumes the queue, but RenderGraph Nodes currently don't have write access to World.
// This is currently ok because new queues are created every frame
pub fn execute(&self, render_context: &mut dyn RenderContext) {
for command in self.queue.iter().cloned() {
match command {
Command::CopyBufferToBuffer {
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
} => render_context.copy_buffer_to_buffer(
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
),
Command::CopyBufferToTexture {
source_buffer,
source_offset,
source_bytes_per_row,
destination_texture,
destination_origin,
destination_mip_level,
size,
} => render_context.copy_buffer_to_texture(
source_buffer,
source_offset,
source_bytes_per_row,
destination_texture,
destination_origin,
destination_mip_level,
size,
),
Command::CopyTextureToTexture {
source_texture,
source_origin,
source_mip_level,
destination_texture,
destination_origin,
destination_mip_level,
size,
} => render_context.copy_texture_to_texture(
source_texture,
source_origin,
source_mip_level,
destination_texture,
destination_origin,
destination_mip_level,
size,
),
Command::CopyTextureToBuffer {
source_texture,
source_origin,
source_mip_level,
destination_buffer,
destination_offset,
destination_bytes_per_row,
size,
} => render_context.copy_texture_to_buffer(
source_texture,
source_origin,
source_mip_level,
destination_buffer,
destination_offset,
destination_bytes_per_row,
size,
),
Command::FreeBuffer(buffer) => render_context.resources().remove_buffer(buffer),
}
}
}
}

View file

@ -1,10 +1,9 @@
use std::borrow::Cow;
use crate::{
render_graph::{NodeState, RenderGraph, SlotInfos, SlotLabel, SlotType, SlotValue},
render_resource::{BufferId, SamplerId, TextureViewId},
render_resource::{Buffer, Sampler, TextureView},
};
use bevy_ecs::entity::Entity;
use std::borrow::Cow;
use thiserror::Error;
pub struct RunSubGraph {
@ -49,19 +48,20 @@ impl<'a> RenderGraphContext<'a> {
&self.node.output_slots
}
pub fn get_input(&self, label: impl Into<SlotLabel>) -> Result<SlotValue, InputSlotError> {
pub fn get_input(&self, label: impl Into<SlotLabel>) -> Result<&SlotValue, InputSlotError> {
let label = label.into();
let index = self
.input_info()
.get_slot_index(label.clone())
.ok_or(InputSlotError::InvalidSlot(label))?;
Ok(self.inputs[index])
Ok(&self.inputs[index])
}
// TODO: should this return an Arc or a reference?
pub fn get_input_texture(
&self,
label: impl Into<SlotLabel>,
) -> Result<TextureViewId, InputSlotError> {
) -> Result<&TextureView, InputSlotError> {
let label = label.into();
match self.get_input(label.clone())? {
SlotValue::TextureView(value) => Ok(value),
@ -76,7 +76,7 @@ impl<'a> RenderGraphContext<'a> {
pub fn get_input_sampler(
&self,
label: impl Into<SlotLabel>,
) -> Result<SamplerId, InputSlotError> {
) -> Result<&Sampler, InputSlotError> {
let label = label.into();
match self.get_input(label.clone())? {
SlotValue::Sampler(value) => Ok(value),
@ -88,10 +88,7 @@ impl<'a> RenderGraphContext<'a> {
}
}
pub fn get_input_buffer(
&self,
label: impl Into<SlotLabel>,
) -> Result<BufferId, InputSlotError> {
pub fn get_input_buffer(&self, label: impl Into<SlotLabel>) -> Result<&Buffer, InputSlotError> {
let label = label.into();
match self.get_input(label.clone())? {
SlotValue::Buffer(value) => Ok(value),
@ -106,7 +103,7 @@ impl<'a> RenderGraphContext<'a> {
pub fn get_input_entity(&self, label: impl Into<SlotLabel>) -> Result<Entity, InputSlotError> {
let label = label.into();
match self.get_input(label.clone())? {
SlotValue::Entity(value) => Ok(value),
SlotValue::Entity(value) => Ok(*value),
value @ _ => Err(InputSlotError::MismatchedSlotType {
label,
actual: value.slot_type(),

View file

@ -333,11 +333,11 @@ impl Node for GraphInputNode {
fn run(
&self,
graph: &mut RenderGraphContext,
_render_context: &mut dyn RenderContext,
_render_context: &mut RenderContext,
_world: &World,
) -> Result<(), NodeRunError> {
for i in 0..graph.inputs().len() {
let input = graph.inputs()[i];
let input = graph.inputs()[i].clone();
graph.set_output(i, input)?;
}
Ok(())
@ -388,7 +388,7 @@ mod tests {
fn run(
&self,
_: &mut RenderGraphContext,
_: &mut dyn RenderContext,
_: &mut RenderContext,
_: &World,
) -> Result<(), NodeRunError> {
Ok(())
@ -461,7 +461,7 @@ mod tests {
fn run(
&self,
_: &mut RenderGraphContext,
_: &mut dyn RenderContext,
_: &mut RenderContext,
_: &World,
) -> Result<(), NodeRunError> {
Ok(())

View file

@ -41,7 +41,7 @@ pub trait Node: Downcast + Send + Sync + 'static {
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut dyn RenderContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError>;
}
@ -229,7 +229,7 @@ impl Node for EmptyNode {
fn run(
&self,
_graph: &mut RenderGraphContext,
_render_context: &mut dyn RenderContext,
_render_context: &mut RenderContext,
_world: &World,
) -> Result<(), NodeRunError> {
Ok(())

View file

@ -1,17 +1,18 @@
use crate::render_resource::{BufferId, SamplerId, TextureViewId};
use bevy_ecs::entity::Entity;
use std::borrow::Cow;
#[derive(Debug, Copy, Clone)]
use crate::render_resource::{Buffer, Sampler, TextureView};
#[derive(Debug, Clone)]
pub enum SlotValue {
Buffer(BufferId),
TextureView(TextureViewId),
Sampler(SamplerId),
Buffer(Buffer),
TextureView(TextureView),
Sampler(Sampler),
Entity(Entity),
}
impl SlotValue {
pub fn slot_type(self) -> SlotType {
pub fn slot_type(&self) -> SlotType {
match self {
SlotValue::Buffer(_) => SlotType::Buffer,
SlotValue::TextureView(_) => SlotType::TextureView,
@ -21,20 +22,20 @@ impl SlotValue {
}
}
impl From<BufferId> for SlotValue {
fn from(value: BufferId) -> Self {
impl From<Buffer> for SlotValue {
fn from(value: Buffer) -> Self {
SlotValue::Buffer(value)
}
}
impl From<TextureViewId> for SlotValue {
fn from(value: TextureViewId) -> Self {
impl From<TextureView> for SlotValue {
fn from(value: TextureView) -> Self {
SlotValue::TextureView(value)
}
}
impl From<SamplerId> for SlotValue {
fn from(value: SamplerId) -> Self {
impl From<Sampler> for SlotValue {
fn from(value: Sampler) -> Self {
SlotValue::Sampler(value)
}
}

View file

@ -7,10 +7,10 @@ use std::{any::TypeId, fmt::Debug, hash::Hash};
// TODO: should this be generic on "drawn thing"? would provide more flexibility and explicitness
// instead of hard coded draw key and sort key
pub trait Draw: Send + Sync + 'static {
fn draw(
&mut self,
world: &World,
pass: &mut TrackedRenderPass,
fn draw<'w, 's>(
&'s mut self,
world: &'w World,
pass: &mut TrackedRenderPass<'w>,
view: Entity,
draw_key: usize,
sort_key: usize,

View file

@ -1,16 +1,14 @@
use bevy_utils::tracing::debug;
use crate::{
pass::RenderPass,
pipeline::{BindGroupDescriptorId, IndexFormat, PipelineId},
render_resource::{BindGroupId, BufferId},
use crate::render_resource::{
BindGroup, BindGroupId, BufferId, BufferSlice, RenderPipeline, RenderPipelineId,
};
use bevy_utils::tracing::debug;
use std::ops::Range;
use wgpu::{IndexFormat, RenderPass};
/// Tracks the current pipeline state to ensure draw calls are valid.
#[derive(Debug, Default)]
pub struct DrawState {
pipeline: Option<PipelineId>,
pipeline: Option<RenderPipelineId>,
bind_groups: Vec<(Option<BindGroupId>, Vec<u32>)>,
vertex_buffers: Vec<Option<(BufferId, u64)>>,
index_buffer: Option<(BufferId, u64, IndexFormat)>,
@ -21,30 +19,24 @@ impl DrawState {
&mut self,
index: usize,
bind_group: BindGroupId,
dynamic_indices: Option<&[u32]>,
dynamic_indices: &[u32],
) {
if index >= self.bind_groups.len() {
self.bind_groups.resize(index + 1, (None, Vec::new()));
}
self.bind_groups[index].0 = Some(bind_group);
self.bind_groups[index].1.clear();
if let Some(indices) = dynamic_indices {
self.bind_groups[index].1.extend(indices);
}
self.bind_groups[index].1.extend(dynamic_indices);
}
pub fn is_bind_group_set(
&self,
index: usize,
bind_group: BindGroupId,
dynamic_indices: Option<&[u32]>,
dynamic_indices: &[u32],
) -> bool {
if let Some(current_bind_group) = self.bind_groups.get(index) {
if let Some(dynamic_indices) = dynamic_indices {
current_bind_group.0 == Some(bind_group) && dynamic_indices == current_bind_group.1
} else {
current_bind_group.0 == Some(bind_group)
}
current_bind_group.0 == Some(bind_group) && dynamic_indices == current_bind_group.1
} else {
false
}
@ -78,11 +70,11 @@ impl DrawState {
self.index_buffer == Some((buffer, offset, index_format))
}
pub fn is_pipeline_set(&self, pipeline: PipelineId) -> bool {
pub fn is_pipeline_set(&self, pipeline: RenderPipelineId) -> bool {
self.pipeline == Some(pipeline)
}
pub fn set_pipeline(&mut self, pipeline: PipelineId) {
pub fn set_pipeline(&mut self, pipeline: RenderPipelineId) {
// TODO: do these need to be cleared?
// self.bind_groups.clear();
// self.vertex_buffers.clear();
@ -92,36 +84,36 @@ impl DrawState {
}
pub struct TrackedRenderPass<'a> {
pass: &'a mut dyn RenderPass,
pass: RenderPass<'a>,
state: DrawState,
}
impl<'a> TrackedRenderPass<'a> {
pub fn new(pass: &'a mut dyn RenderPass) -> Self {
pub fn new(pass: RenderPass<'a>) -> Self {
Self {
state: DrawState::default(),
pass,
}
}
pub fn set_pipeline(&mut self, pipeline: PipelineId) {
pub fn set_render_pipeline(&mut self, pipeline: &'a RenderPipeline) {
debug!("set pipeline: {:?}", pipeline);
if self.state.is_pipeline_set(pipeline) {
if self.state.is_pipeline_set(pipeline.id()) {
return;
}
self.pass.set_pipeline(pipeline);
self.state.set_pipeline(pipeline);
self.state.set_pipeline(pipeline.id());
}
pub fn set_bind_group(
&mut self,
index: usize,
bind_group_descriptor: BindGroupDescriptorId,
bind_group: BindGroupId,
dynamic_uniform_indices: Option<&[u32]>,
bind_group: &'a BindGroup,
dynamic_uniform_indices: &[u32],
) {
if self
.state
.is_bind_group_set(index as usize, bind_group, dynamic_uniform_indices)
.is_bind_group_set(index as usize, bind_group.id(), dynamic_uniform_indices)
{
debug!(
"set bind_group {} (already set): {:?} ({:?})",
@ -134,39 +126,60 @@ impl<'a> TrackedRenderPass<'a> {
index, bind_group, dynamic_uniform_indices
);
}
self.pass.set_bind_group(
index as u32,
bind_group_descriptor,
bind_group,
dynamic_uniform_indices,
);
self.pass
.set_bind_group(index as u32, bind_group.value(), dynamic_uniform_indices);
self.state
.set_bind_group(index as usize, bind_group, dynamic_uniform_indices);
.set_bind_group(index as usize, bind_group.id(), dynamic_uniform_indices);
}
pub fn set_vertex_buffer(&mut self, index: usize, buffer: BufferId, offset: u64) {
if self.state.is_vertex_buffer_set(index, buffer, offset) {
pub fn set_vertex_buffer(&mut self, index: usize, buffer_slice: BufferSlice<'a>) {
let offset = buffer_slice.offset();
if self
.state
.is_vertex_buffer_set(index, buffer_slice.id(), offset)
{
debug!(
"set vertex buffer {} (already set): {:?} ({})",
index, buffer, offset
index,
buffer_slice.id(),
offset
);
return;
} else {
debug!("set vertex buffer {}: {:?} ({})", index, buffer, offset);
debug!(
"set vertex buffer {}: {:?} ({})",
index,
buffer_slice.id(),
offset
);
}
self.pass.set_vertex_buffer(index as u32, buffer, offset);
self.state.set_vertex_buffer(index, buffer, offset);
self.pass.set_vertex_buffer(index as u32, *buffer_slice);
self.state
.set_vertex_buffer(index, buffer_slice.id(), offset);
}
pub fn set_index_buffer(&mut self, buffer: BufferId, offset: u64, index_format: IndexFormat) {
if self.state.is_index_buffer_set(buffer, offset, index_format) {
debug!("set index buffer (already set): {:?} ({})", buffer, offset);
pub fn set_index_buffer(
&mut self,
buffer_slice: BufferSlice<'a>,
offset: u64,
index_format: IndexFormat,
) {
if self
.state
.is_index_buffer_set(buffer_slice.id(), offset, index_format)
{
debug!(
"set index buffer (already set): {:?} ({})",
buffer_slice.id(),
offset
);
return;
} else {
debug!("set index buffer: {:?} ({})", buffer, offset);
debug!("set index buffer: {:?} ({})", buffer_slice.id(), offset);
}
self.pass.set_index_buffer(buffer, offset, index_format);
self.state.set_index_buffer(buffer, offset, index_format);
self.pass.set_index_buffer(*buffer_slice, index_format);
self.state
.set_index_buffer(buffer_slice.id(), offset, index_format);
}
pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {

View file

@ -1,84 +1,32 @@
use crate::render_resource::{
BufferId, RenderResourceBinding, RenderResourceId, SamplerId, TextureViewId,
};
use bevy_utils::AHasher;
use std::{
hash::{Hash, Hasher},
ops::Range,
sync::Arc,
};
use bevy_reflect::Uuid;
use std::sync::Arc;
#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
pub struct BindGroupId(pub u64);
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct BindGroupId(Uuid);
#[derive(Eq, PartialEq, Debug)]
pub struct IndexedBindGroupEntry {
pub index: u32,
pub entry: RenderResourceBinding,
}
#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(Clone, Debug)]
pub struct BindGroup {
pub id: BindGroupId,
pub indexed_bindings: Arc<[IndexedBindGroupEntry]>,
id: BindGroupId,
value: Arc<wgpu::BindGroup>,
}
impl BindGroup {
pub fn build() -> BindGroupBuilder {
BindGroupBuilder::default()
#[inline]
pub fn id(&self) -> BindGroupId {
self.id
}
#[inline]
pub fn value(&self) -> &wgpu::BindGroup {
&self.value
}
}
#[derive(Debug, Default)]
pub struct BindGroupBuilder {
pub indexed_bindings: Vec<IndexedBindGroupEntry>,
pub hasher: AHasher,
}
impl BindGroupBuilder {
pub fn add_binding<T: Into<RenderResourceBinding>>(mut self, index: u32, binding: T) -> Self {
let binding = binding.into();
self.hash_binding(&binding);
self.indexed_bindings.push(IndexedBindGroupEntry {
index,
entry: binding,
});
self
}
pub fn add_texture_view(self, index: u32, texture: TextureViewId) -> Self {
self.add_binding(index, RenderResourceBinding::TextureView(texture))
}
pub fn add_sampler(self, index: u32, sampler: SamplerId) -> Self {
self.add_binding(index, RenderResourceBinding::Sampler(sampler))
}
pub fn add_buffer(self, index: u32, buffer: BufferId, range: Range<u64>) -> Self {
self.add_binding(index, RenderResourceBinding::Buffer { buffer, range })
}
pub fn finish(mut self) -> BindGroup {
// this sort ensures that RenderResourceSets are insertion-order independent
self.indexed_bindings.sort_by_key(|i| i.index);
impl From<wgpu::BindGroup> for BindGroup {
fn from(value: wgpu::BindGroup) -> Self {
BindGroup {
id: BindGroupId(self.hasher.finish()),
indexed_bindings: self.indexed_bindings.into(),
}
}
fn hash_binding(&mut self, binding: &RenderResourceBinding) {
match binding {
RenderResourceBinding::Buffer { buffer, range } => {
RenderResourceId::Buffer(*buffer).hash(&mut self.hasher);
range.hash(&mut self.hasher);
}
RenderResourceBinding::TextureView(texture) => {
RenderResourceId::TextureView(*texture).hash(&mut self.hasher);
}
RenderResourceBinding::Sampler(sampler) => {
RenderResourceId::Sampler(*sampler).hash(&mut self.hasher);
}
id: BindGroupId(Uuid::new_v4()),
value: Arc::new(value),
}
}
}

View file

@ -1,51 +1,82 @@
use bevy_utils::Uuid;
use std::{ops::{Bound, Deref, RangeBounds}, sync::Arc};
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct BufferId(Uuid);
impl BufferId {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
BufferId(Uuid::new_v4())
#[derive(Clone, Debug)]
pub struct Buffer {
id: BufferId,
value: Arc<wgpu::Buffer>,
}
impl Buffer {
#[inline]
pub fn id(&self) -> BufferId {
self.id
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct BufferInfo {
pub size: usize,
pub buffer_usage: BufferUsage,
pub mapped_at_creation: bool,
}
impl Default for BufferInfo {
fn default() -> Self {
BufferInfo {
size: 0,
buffer_usage: BufferUsage::empty(),
mapped_at_creation: false,
pub fn slice(&self, bounds: impl RangeBounds<wgpu::BufferAddress>) -> BufferSlice {
BufferSlice {
id: self.id,
// need to compute and store this manually because wgpu doesn't export offset on wgpu::BufferSlice
offset: match bounds.start_bound() {
Bound::Included(&bound) => bound,
Bound::Excluded(&bound) => bound + 1,
Bound::Unbounded => 0,
},
value: self.value.slice(bounds),
}
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BufferUsage: u32 {
const MAP_READ = 1;
const MAP_WRITE = 2;
const COPY_SRC = 4;
const COPY_DST = 8;
const INDEX = 16;
const VERTEX = 32;
const UNIFORM = 64;
const STORAGE = 128;
const INDIRECT = 256;
#[inline]
pub fn unmap(&self) {
self.value.unmap()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BufferMapMode {
Read,
Write,
impl From<wgpu::Buffer> for Buffer {
fn from(value: wgpu::Buffer) -> Self {
Buffer {
id: BufferId(Uuid::new_v4()),
value: Arc::new(value),
}
}
}
impl Deref for Buffer {
type Target = wgpu::Buffer;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Clone, Debug)]
pub struct BufferSlice<'a> {
id: BufferId,
offset: wgpu::BufferAddress,
value: wgpu::BufferSlice<'a>,
}
impl<'a> BufferSlice<'a> {
#[inline]
pub fn id(&self) -> BufferId {
self.id
}
#[inline]
pub fn offset(&self) -> wgpu::BufferAddress {
self.offset
}
}
impl<'a> Deref for BufferSlice<'a> {
type Target = wgpu::BufferSlice<'a>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}

View file

@ -1,13 +1,11 @@
use crate::{
render_resource::{BufferId, BufferInfo, BufferMapMode, BufferUsage},
renderer::{RenderContext, RenderResources},
};
use crate::{render_resource::Buffer, renderer::RenderDevice};
use bevy_core::{cast_slice, Pod};
use wgpu::BufferUsage;
pub struct BufferVec<T: Pod> {
values: Vec<T>,
staging_buffer: Option<BufferId>,
buffer: Option<BufferId>,
staging_buffer: Option<Buffer>,
buffer: Option<Buffer>,
capacity: usize,
item_size: usize,
buffer_usage: BufferUsage,
@ -34,13 +32,13 @@ impl<T: Pod> BufferVec<T> {
}
}
#[inline]
pub fn staging_buffer(&self) -> Option<BufferId> {
self.staging_buffer
pub fn staging_buffer(&self) -> Option<&Buffer> {
self.staging_buffer.as_ref()
}
#[inline]
pub fn buffer(&self) -> Option<BufferId> {
self.buffer
pub fn buffer(&self) -> Option<&Buffer> {
self.buffer.as_ref()
}
#[inline]
@ -61,54 +59,45 @@ impl<T: Pod> BufferVec<T> {
}
}
pub fn reserve(&mut self, capacity: usize, render_resources: &RenderResources) {
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
if capacity > self.capacity {
self.capacity = capacity;
if let Some(staging_buffer) = self.staging_buffer.take() {
render_resources.remove_buffer(staging_buffer);
}
if let Some(buffer) = self.buffer.take() {
render_resources.remove_buffer(buffer);
}
let size = self.item_size * capacity;
self.staging_buffer = Some(render_resources.create_buffer(BufferInfo {
let size = (self.item_size * capacity) as wgpu::BufferAddress;
self.staging_buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size,
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
mapped_at_creation: false,
}));
self.buffer = Some(render_resources.create_buffer(BufferInfo {
self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size,
buffer_usage: BufferUsage::COPY_DST | self.buffer_usage,
usage: BufferUsage::COPY_DST | self.buffer_usage,
mapped_at_creation: false,
}));
}
}
pub fn reserve_and_clear(&mut self, capacity: usize, render_resources: &RenderResources) {
pub fn reserve_and_clear(&mut self, capacity: usize, device: &RenderDevice) {
self.clear();
self.reserve(capacity, render_resources);
self.reserve(capacity, device);
}
pub fn write_to_staging_buffer(&self, render_resources: &RenderResources) {
if let Some(staging_buffer) = self.staging_buffer {
let size = self.values.len() * self.item_size;
render_resources.map_buffer(staging_buffer, BufferMapMode::Write);
render_resources.write_mapped_buffer(
staging_buffer,
0..size as u64,
&mut |data, _renderer| {
let bytes: &[u8] = cast_slice(&self.values);
data.copy_from_slice(bytes);
},
);
render_resources.unmap_buffer(staging_buffer);
pub fn write_to_staging_buffer(&self, render_device: &RenderDevice) {
if let Some(staging_buffer) = &self.staging_buffer {
let slice = staging_buffer.slice(..);
render_device.map_buffer(&slice, wgpu::MapMode::Write);
{
let mut data = slice.get_mapped_range_mut();
let bytes: &[u8] = cast_slice(&self.values);
data.copy_from_slice(bytes);
}
staging_buffer.unmap();
}
}
pub fn write_to_buffer(&self, render_context: &mut dyn RenderContext) {
if let (Some(staging_buffer), Some(uniform_buffer)) = (self.staging_buffer, self.buffer) {
render_context.copy_buffer_to_buffer(
pub fn write_to_buffer(&self, command_encoder: &mut wgpu::CommandEncoder) {
if let (Some(staging_buffer), Some(uniform_buffer)) = (&self.staging_buffer, &self.buffer) {
command_encoder.copy_buffer_to_buffer(
staging_buffer,
0,
uniform_buffer,

View file

@ -1,17 +1,31 @@
mod bind_group;
mod buffer;
mod buffer_vec;
mod render_resource_bindings;
mod pipeline;
mod render_resource_id;
mod swap_chain;
mod texture;
mod uniform_vec;
pub use bind_group::*;
pub use buffer::*;
pub use buffer_vec::*;
pub use render_resource_bindings::*;
pub use pipeline::*;
pub use render_resource_id::*;
pub use swap_chain::*;
pub use texture::*;
pub use uniform_vec::*;
// TODO: decide where re-exports should go
pub use wgpu::util::BufferInitDescriptor;
pub use wgpu::{
AddressMode, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BlendFactor,
BlendOperation, BlendState, BufferAddress, BufferBindingType, BufferSize, BufferUsage,
ColorTargetState, ColorWrite, CompareFunction, DepthBiasState, DepthStencilState, Extent3d,
Face, FilterMode, FragmentState, FrontFace, IndexFormat, InputStepMode, MultisampleState,
PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology,
RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, SamplerDescriptor,
ShaderFlags, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStage, StencilFaceState,
StencilState, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat,
TextureSampleType, TextureUsage, TextureViewDescriptor, TextureViewDimension, VertexAttribute,
VertexBufferLayout, VertexFormat, VertexState,RenderPassDepthStencilAttachment, Operations, LoadOp
};

View file

@ -0,0 +1,61 @@
use bevy_reflect::Uuid;
use std::{ops::Deref, sync::Arc};
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct RenderPipelineId(Uuid);
#[derive(Clone, Debug)]
pub struct RenderPipeline {
id: RenderPipelineId,
value: Arc<wgpu::RenderPipeline>,
}
impl RenderPipeline {
#[inline]
pub fn id(&self) -> RenderPipelineId {
self.id
}
}
impl From<wgpu::RenderPipeline> for RenderPipeline {
fn from(value: wgpu::RenderPipeline) -> Self {
RenderPipeline {
id: RenderPipelineId(Uuid::new_v4()),
value: Arc::new(value),
}
}
}
impl Deref for RenderPipeline {
type Target = wgpu::RenderPipeline;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct ComputePipelineId(Uuid);
#[derive(Clone, Debug)]
pub struct ComputePipeline {
id: ComputePipelineId,
value: Arc<wgpu::ComputePipeline>,
}
impl ComputePipeline {
#[inline]
pub fn id(&self) -> ComputePipelineId {
self.id
}
}
impl From<wgpu::ComputePipeline> for ComputePipeline {
fn from(value: wgpu::ComputePipeline) -> Self {
ComputePipeline {
id: ComputePipelineId(Uuid::new_v4()),
value: Arc::new(value),
}
}
}

View file

@ -1,47 +0,0 @@
use crate::render_resource::{BufferId, SamplerId, TextureViewId};
use std::ops::Range;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum RenderResourceBinding {
Buffer { buffer: BufferId, range: Range<u64> },
TextureView(TextureViewId),
Sampler(SamplerId),
}
impl RenderResourceBinding {
pub fn get_texture_view(&self) -> Option<TextureViewId> {
if let RenderResourceBinding::TextureView(texture) = self {
Some(*texture)
} else {
None
}
}
pub fn get_buffer(&self) -> Option<BufferId> {
if let RenderResourceBinding::Buffer { buffer, .. } = self {
Some(*buffer)
} else {
None
}
}
pub fn get_sampler(&self) -> Option<SamplerId> {
if let RenderResourceBinding::Sampler(sampler) = self {
Some(*sampler)
} else {
None
}
}
}
impl From<TextureViewId> for RenderResourceBinding {
fn from(id: TextureViewId) -> Self {
RenderResourceBinding::TextureView(id)
}
}
impl From<SamplerId> for RenderResourceBinding {
fn from(id: SamplerId) -> Self {
RenderResourceBinding::Sampler(id)
}
}

View file

@ -1,16 +0,0 @@
use bevy_window::WindowId;
use crate::texture::TextureFormat;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SwapChainDescriptor {
pub window_id: WindowId,
/// The texture format of the swap chain. The only formats that are guaranteed are
/// `Bgra8Unorm` and `Bgra8UnormSrgb`
pub format: TextureFormat,
/// Width of the swap chain. Must be the same size as the surface.
pub width: u32,
/// Height of the swap chain. Must be the same size as the surface.
pub height: u32,
pub vsync: bool,
}

View file

@ -1,31 +1,157 @@
use bevy_utils::Uuid;
use std::{ops::Deref, sync::Arc};
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TextureId(Uuid);
impl TextureId {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
TextureId(Uuid::new_v4())
#[derive(Clone, Debug)]
pub struct Texture {
id: TextureId,
value: Arc<wgpu::Texture>,
}
impl Texture {
#[inline]
pub fn id(&self) -> TextureId {
self.id
}
pub fn create_view(&self, desc: &wgpu::TextureViewDescriptor) -> TextureView {
TextureView::from(self.value.create_view(desc))
}
}
impl From<wgpu::Texture> for Texture {
fn from(value: wgpu::Texture) -> Self {
Texture {
id: TextureId(Uuid::new_v4()),
value: Arc::new(value),
}
}
}
impl Deref for Texture {
type Target = wgpu::Texture;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TextureViewId(Uuid);
impl TextureViewId {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
TextureViewId(Uuid::new_v4())
#[derive(Clone, Debug)]
pub enum TextureViewValue {
TextureView(Arc<wgpu::TextureView>),
SwapChainFrame(Arc<wgpu::SwapChainFrame>),
}
#[derive(Clone, Debug)]
pub struct TextureView {
id: TextureViewId,
value: TextureViewValue,
}
impl TextureView {
#[inline]
pub fn id(&self) -> TextureViewId {
self.id
}
}
impl From<wgpu::TextureView> for TextureView {
fn from(value: wgpu::TextureView) -> Self {
TextureView {
id: TextureViewId(Uuid::new_v4()),
value: TextureViewValue::TextureView(Arc::new(value)),
}
}
}
impl From<wgpu::SwapChainFrame> for TextureView {
fn from(value: wgpu::SwapChainFrame) -> Self {
TextureView {
id: TextureViewId(Uuid::new_v4()),
value: TextureViewValue::SwapChainFrame(Arc::new(value)),
}
}
}
impl Deref for TextureView {
type Target = wgpu::TextureView;
#[inline]
fn deref(&self) -> &Self::Target {
match &self.value {
TextureViewValue::TextureView(value) => value,
TextureViewValue::SwapChainFrame(value) => &value.output.view,
}
}
}
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct SamplerId(Uuid);
impl SamplerId {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
SamplerId(Uuid::new_v4())
#[derive(Clone, Debug)]
pub struct Sampler {
id: SamplerId,
value: Arc<wgpu::Sampler>,
}
impl Sampler {
#[inline]
pub fn id(&self) -> SamplerId {
self.id
}
}
impl From<wgpu::Sampler> for Sampler {
fn from(value: wgpu::Sampler) -> Self {
Sampler {
id: SamplerId(Uuid::new_v4()),
value: Arc::new(value),
}
}
}
impl Deref for Sampler {
type Target = wgpu::Sampler;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Clone, Debug)]
pub struct SwapChainFrame {
id: TextureViewId,
value: Arc<wgpu::SwapChainFrame>,
}
impl SwapChainFrame {
#[inline]
pub fn id(&self) -> TextureViewId {
self.id
}
}
impl From<wgpu::SwapChainFrame> for SwapChainFrame {
fn from(value: wgpu::SwapChainFrame) -> Self {
Self {
id: TextureViewId(Uuid::new_v4()),
value: Arc::new(value),
}
}
}
impl Deref for SwapChainFrame {
type Target = wgpu::SwapChainFrame;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}

View file

@ -1,13 +1,12 @@
use crate::{
render_resource::{BufferId, BufferInfo, BufferMapMode, BufferUsage, RenderResourceBinding},
renderer::{RenderContext, RenderResources},
};
use crate::{render_resource::Buffer, renderer::RenderDevice};
use crevice::std140::{self, AsStd140, DynamicUniform, Std140};
use std::{num::NonZeroU64, ops::DerefMut};
use wgpu::{BindingResource, BufferBinding, BufferDescriptor, BufferUsage, CommandEncoder};
pub struct UniformVec<T: AsStd140> {
values: Vec<T>,
staging_buffer: Option<BufferId>,
uniform_buffer: Option<BufferId>,
staging_buffer: Option<Buffer>,
uniform_buffer: Option<Buffer>,
capacity: usize,
item_size: usize,
}
@ -27,13 +26,22 @@ impl<T: AsStd140> Default for UniformVec<T> {
impl<T: AsStd140> UniformVec<T> {
#[inline]
pub fn staging_buffer(&self) -> Option<BufferId> {
self.staging_buffer
pub fn staging_buffer(&self) -> Option<&Buffer> {
self.staging_buffer.as_ref()
}
#[inline]
pub fn uniform_buffer(&self) -> Option<BufferId> {
self.uniform_buffer
pub fn uniform_buffer(&self) -> Option<&Buffer> {
self.uniform_buffer.as_ref()
}
#[inline]
pub fn binding(&self) -> BindingResource {
BindingResource::Buffer(BufferBinding {
buffer: self.uniform_buffer().expect("uniform buffer should exist"),
offset: 0,
size: Some(NonZeroU64::new(self.item_size as u64).unwrap()),
})
}
#[inline]
@ -41,13 +49,6 @@ impl<T: AsStd140> UniformVec<T> {
self.capacity
}
pub fn binding(&self) -> RenderResourceBinding {
RenderResourceBinding::Buffer {
buffer: self.uniform_buffer.unwrap(),
range: 0..self.item_size as u64,
}
}
pub fn push(&mut self, value: T) -> usize {
let len = self.values.len();
if len < self.capacity {
@ -61,56 +62,47 @@ impl<T: AsStd140> UniformVec<T> {
}
}
pub fn reserve(&mut self, capacity: usize, render_resources: &RenderResources) {
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
if capacity > self.capacity {
self.capacity = capacity;
if let Some(staging_buffer) = self.staging_buffer.take() {
render_resources.remove_buffer(staging_buffer);
}
if let Some(uniform_buffer) = self.uniform_buffer.take() {
render_resources.remove_buffer(uniform_buffer);
}
let size = self.item_size * capacity;
self.staging_buffer = Some(render_resources.create_buffer(BufferInfo {
let size = (self.item_size * capacity) as wgpu::BufferAddress;
self.staging_buffer = Some(device.create_buffer(&BufferDescriptor {
label: None,
size,
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
mapped_at_creation: false,
}));
self.uniform_buffer = Some(render_resources.create_buffer(BufferInfo {
self.uniform_buffer = Some(device.create_buffer(&BufferDescriptor {
label: None,
size,
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
mapped_at_creation: false,
}));
}
}
pub fn reserve_and_clear(&mut self, capacity: usize, render_resources: &RenderResources) {
pub fn reserve_and_clear(&mut self, capacity: usize, device: &RenderDevice) {
self.clear();
self.reserve(capacity, render_resources);
self.reserve(capacity, device);
}
pub fn write_to_staging_buffer(&self, render_resources: &RenderResources) {
if let Some(staging_buffer) = self.staging_buffer {
let size = self.values.len() * self.item_size;
render_resources.map_buffer(staging_buffer, BufferMapMode::Write);
render_resources.write_mapped_buffer(
staging_buffer,
0..size as u64,
&mut |data, _renderer| {
let mut writer = std140::Writer::new(data);
writer.write(self.values.as_slice()).unwrap();
},
);
render_resources.unmap_buffer(staging_buffer);
pub fn write_to_staging_buffer(&self, device: &RenderDevice) {
if let Some(staging_buffer) = &self.staging_buffer {
let slice = staging_buffer.slice(..);
device.map_buffer(&slice, wgpu::MapMode::Write);
{
let mut data = slice.get_mapped_range_mut();
let mut writer = std140::Writer::new(data.deref_mut());
writer.write(self.values.as_slice()).unwrap();
}
staging_buffer.unmap()
}
}
pub fn write_to_uniform_buffer(&self, render_context: &mut dyn RenderContext) {
pub fn write_to_uniform_buffer(&self, command_encoder: &mut CommandEncoder) {
if let (Some(staging_buffer), Some(uniform_buffer)) =
(self.staging_buffer, self.uniform_buffer)
(&self.staging_buffer, &self.uniform_buffer)
{
render_context.copy_buffer_to_buffer(
command_encoder.copy_buffer_to_buffer(
staging_buffer,
0,
uniform_buffer,
@ -139,17 +131,17 @@ impl<T: AsStd140> Default for DynamicUniformVec<T> {
impl<T: AsStd140> DynamicUniformVec<T> {
#[inline]
pub fn staging_buffer(&self) -> Option<BufferId> {
pub fn staging_buffer(&self) -> Option<&Buffer> {
self.uniform_vec.staging_buffer()
}
#[inline]
pub fn uniform_buffer(&self) -> Option<BufferId> {
pub fn uniform_buffer(&self) -> Option<&Buffer> {
self.uniform_vec.uniform_buffer()
}
#[inline]
pub fn binding(&self) -> RenderResourceBinding {
pub fn binding(&self) -> BindingResource {
self.uniform_vec.binding()
}
@ -164,24 +156,23 @@ impl<T: AsStd140> DynamicUniformVec<T> {
}
#[inline]
pub fn reserve(&mut self, capacity: usize, render_resources: &RenderResources) {
self.uniform_vec.reserve(capacity, render_resources);
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) {
self.uniform_vec.reserve(capacity, device);
}
#[inline]
pub fn reserve_and_clear(&mut self, capacity: usize, render_resources: &RenderResources) {
self.uniform_vec
.reserve_and_clear(capacity, render_resources);
pub fn reserve_and_clear(&mut self, capacity: usize, device: &RenderDevice) {
self.uniform_vec.reserve_and_clear(capacity, device);
}
#[inline]
pub fn write_to_staging_buffer(&self, render_resources: &RenderResources) {
self.uniform_vec.write_to_staging_buffer(render_resources);
pub fn write_to_staging_buffer(&self, device: &RenderDevice) {
self.uniform_vec.write_to_staging_buffer(device);
}
#[inline]
pub fn write_to_uniform_buffer(&self, render_context: &mut dyn RenderContext) {
self.uniform_vec.write_to_uniform_buffer(render_context);
pub fn write_to_uniform_buffer(&self, command_encoder: &mut CommandEncoder) {
self.uniform_vec.write_to_uniform_buffer(command_encoder);
}
#[inline]

View file

@ -1,18 +1,21 @@
use crate::{WgpuRenderContext, WgpuRenderResourceContext};
use bevy_ecs::world::World;
use bevy_render2::render_graph::{
Edge, NodeId, NodeRunError, NodeState, RenderGraph, RenderGraphContext, SlotLabel, SlotType,
SlotValue,
};
use bevy_utils::{tracing::debug, HashMap};
use smallvec::{smallvec, SmallVec};
use std::{borrow::Cow, collections::VecDeque, sync::Arc};
use std::{borrow::Cow, collections::VecDeque};
use thiserror::Error;
pub(crate) struct WgpuRenderGraphRunner;
use crate::{
render_graph::{
Edge, NodeId, NodeRunError, NodeState, RenderGraph, RenderGraphContext, SlotLabel,
SlotType, SlotValue,
},
renderer::{RenderContext, RenderDevice},
};
pub(crate) struct RenderGraphRunner;
#[derive(Error, Debug)]
pub enum WgpuRenderGraphRunnerError {
pub enum RenderGraphRunnerError {
#[error(transparent)]
NodeRunError(#[from] NodeRunError),
#[error("node output slot not set (index {slot_index}, name {slot_name})")]
@ -36,29 +39,31 @@ pub enum WgpuRenderGraphRunnerError {
},
}
impl WgpuRenderGraphRunner {
impl RenderGraphRunner {
pub fn run(
graph: &RenderGraph,
device: Arc<wgpu::Device>,
queue: &mut wgpu::Queue,
render_device: RenderDevice,
queue: &wgpu::Queue,
world: &World,
resources: &WgpuRenderResourceContext,
) -> Result<(), WgpuRenderGraphRunnerError> {
let mut render_context = WgpuRenderContext::new(device, resources.clone());
) -> Result<(), RenderGraphRunnerError> {
let command_encoder =
render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let mut render_context = RenderContext {
render_device,
command_encoder,
};
Self::run_graph(graph, None, &mut render_context, world, &[])?;
if let Some(command_buffer) = render_context.finish() {
queue.submit(vec![command_buffer]);
}
queue.submit(vec![render_context.command_encoder.finish()]);
Ok(())
}
fn run_graph(
graph: &RenderGraph,
graph_name: Option<Cow<'static, str>>,
render_context: &mut WgpuRenderContext,
render_context: &mut RenderContext,
world: &World,
inputs: &[SlotValue],
) -> Result<(), WgpuRenderGraphRunnerError> {
) -> Result<(), RenderGraphRunnerError> {
let mut node_outputs: HashMap<NodeId, SmallVec<[SlotValue; 4]>> = HashMap::default();
debug!("-----------------");
debug!("Begin Graph Run: {:?}", graph_name);
@ -76,17 +81,17 @@ impl WgpuRenderGraphRunner {
for (i, input_slot) in input_node.input_slots.iter().enumerate() {
if let Some(input_value) = inputs.get(i) {
if input_slot.slot_type != input_value.slot_type() {
return Err(WgpuRenderGraphRunnerError::MismatchedInputSlotType {
return Err(RenderGraphRunnerError::MismatchedInputSlotType {
slot_index: i,
actual: input_value.slot_type(),
expected: input_slot.slot_type,
label: input_slot.name.clone().into(),
});
} else {
input_values.push(*input_value);
input_values.push(input_value.clone());
}
} else {
return Err(WgpuRenderGraphRunnerError::MissingInput {
return Err(RenderGraphRunnerError::MissingInput {
slot_index: i,
slot_name: input_slot.name.clone(),
graph_name: graph_name.clone(),
@ -120,7 +125,8 @@ impl WgpuRenderGraphRunner {
..
} => {
if let Some(outputs) = node_outputs.get(&input_node.id) {
slot_indices_and_inputs.push((*input_index, outputs[*output_index]));
slot_indices_and_inputs
.push((*input_index, outputs[*output_index].clone()));
} else {
node_queue.push_front(node_state);
continue 'handle_node;
@ -172,7 +178,7 @@ impl WgpuRenderGraphRunner {
values.push(value);
} else {
let empty_slot = node_state.output_slots.get_slot(i).unwrap();
return Err(WgpuRenderGraphRunnerError::EmptyNodeOutputSlot {
return Err(RenderGraphRunnerError::EmptyNodeOutputSlot {
type_name: node_state.type_name,
slot_index: i,
slot_name: empty_slot.name.clone(),

View file

@ -1,164 +0,0 @@
use crate::{
pipeline::{
BindGroupDescriptorId, ComputePipelineDescriptor, PipelineId, RenderPipelineDescriptor,
},
render_resource::{
BindGroup, BufferId, BufferInfo, BufferMapMode, SamplerId, SwapChainDescriptor, TextureId,
TextureViewId,
},
renderer::RenderResourceContext,
shader::{Shader, ShaderId},
texture::{SamplerDescriptor, TextureDescriptor, TextureViewDescriptor},
};
use bevy_utils::HashMap;
use parking_lot::RwLock;
use std::{ops::Range, sync::Arc};
#[derive(Debug, Default)]
pub struct HeadlessRenderResourceContext {
buffer_info: Arc<RwLock<HashMap<BufferId, BufferInfo>>>,
texture_descriptors: Arc<RwLock<HashMap<TextureId, TextureDescriptor>>>,
texture_view_descriptors: Arc<RwLock<HashMap<TextureViewId, TextureViewDescriptor>>>,
}
impl HeadlessRenderResourceContext {
pub fn add_buffer_info(&self, buffer: BufferId, info: BufferInfo) {
self.buffer_info.write().insert(buffer, info);
}
pub fn add_texture_descriptor(&self, texture: TextureId, descriptor: TextureDescriptor) {
self.texture_descriptors.write().insert(texture, descriptor);
}
}
impl RenderResourceContext for HeadlessRenderResourceContext {
fn drop_swap_chain_texture(&self, _texture_view: TextureViewId) {}
fn drop_all_swap_chain_textures(&self) {}
fn create_sampler(&self, _sampler_descriptor: &SamplerDescriptor) -> SamplerId {
SamplerId::new()
}
fn create_texture(&self, texture_descriptor: TextureDescriptor) -> TextureId {
let texture = TextureId::new();
self.add_texture_descriptor(texture, texture_descriptor);
texture
}
fn create_texture_view(
&self,
_texture_id: TextureId,
texture_view_descriptor: TextureViewDescriptor,
) -> TextureViewId {
let texture_view_id = TextureViewId::new();
self.texture_view_descriptors
.write()
.insert(texture_view_id, texture_view_descriptor);
texture_view_id
}
fn create_buffer(&self, buffer_info: BufferInfo) -> BufferId {
let buffer = BufferId::new();
self.add_buffer_info(buffer, buffer_info);
buffer
}
fn write_mapped_buffer(
&self,
id: BufferId,
_range: Range<u64>,
write: &mut dyn FnMut(&mut [u8], &dyn RenderResourceContext),
) {
let size = self.buffer_info.read().get(&id).unwrap().size;
let mut buffer = vec![0; size];
write(&mut buffer, self);
}
fn read_mapped_buffer(
&self,
id: BufferId,
_range: Range<u64>,
read: &dyn Fn(&[u8], &dyn RenderResourceContext),
) {
let size = self.buffer_info.read().get(&id).unwrap().size;
let buffer = vec![0; size];
read(&buffer, self);
}
fn map_buffer(&self, _id: BufferId, _mode: BufferMapMode) {}
fn unmap_buffer(&self, _id: BufferId) {}
fn create_buffer_with_data(&self, buffer_info: BufferInfo, _data: &[u8]) -> BufferId {
let buffer = BufferId::new();
self.add_buffer_info(buffer, buffer_info);
buffer
}
fn create_shader_module(&self, _shader: &Shader) -> ShaderId {
ShaderId::new()
}
fn remove_buffer(&self, buffer: BufferId) {
self.buffer_info.write().remove(&buffer);
}
fn remove_texture(&self, texture: TextureId) {
self.texture_descriptors.write().remove(&texture);
}
fn remove_sampler(&self, _sampler: SamplerId) {}
fn remove_texture_view(&self, texture_view: TextureViewId) {
self.texture_view_descriptors.write().remove(&texture_view);
}
fn create_render_pipeline(
&self,
_pipeline_descriptor: &RenderPipelineDescriptor,
) -> PipelineId {
PipelineId::new()
}
fn create_compute_pipeline(
&self,
_pipeline_descriptor: &ComputePipelineDescriptor,
) -> PipelineId {
PipelineId::new()
}
fn create_bind_group(
&self,
_bind_group_descriptor_id: BindGroupDescriptorId,
_bind_group: &BindGroup,
) {
}
fn clear_bind_groups(&self) {}
fn get_buffer_info(&self, buffer: BufferId) -> Option<BufferInfo> {
self.buffer_info.read().get(&buffer).cloned()
}
fn bind_group_descriptor_exists(
&self,
_bind_group_descriptor_id: BindGroupDescriptorId,
) -> bool {
false
}
fn get_aligned_uniform_size(&self, size: usize, _dynamic: bool) -> usize {
size
}
fn get_aligned_texture_size(&self, size: usize) -> usize {
size
}
fn remove_stale_bind_groups(&self) {}
fn next_swap_chain_texture(&self, _descriptor: &SwapChainDescriptor) -> TextureViewId {
TextureViewId::new()
}
}

View file

@ -1,7 +1,65 @@
mod headless_render_resource_context;
mod render_context;
mod render_resource_context;
mod graph_runner;
mod render_device;
pub use headless_render_resource_context::*;
pub use render_context::*;
pub use render_resource_context::*;
pub use graph_runner::*;
pub use render_device::*;
use crate::render_graph::RenderGraph;
use bevy_ecs::prelude::*;
use std::sync::Arc;
use wgpu::{BackendBit, CommandEncoder, DeviceDescriptor, Instance, Queue, RequestAdapterOptions};
pub fn render_system(world: &mut World) {
world.resource_scope(|world, mut graph: Mut<RenderGraph>| {
graph.update(world);
});
let graph = world.get_resource::<RenderGraph>().unwrap();
let render_device = world.get_resource::<RenderDevice>().unwrap();
let render_queue = world.get_resource::<RenderQueue>().unwrap();
RenderGraphRunner::run(
graph,
render_device.clone(), // TODO: is this clone really necessary?
render_queue,
world,
)
.unwrap();
}
pub type RenderQueue = Arc<Queue>;
pub type RenderInstance = Instance;
pub async fn initialize_renderer(
backends: BackendBit,
request_adapter_options: &RequestAdapterOptions<'_>,
device_descriptor: &DeviceDescriptor<'_>,
) -> (RenderInstance, RenderDevice, RenderQueue) {
let instance = wgpu::Instance::new(backends);
let adapter = instance
.request_adapter(&request_adapter_options)
.await
.expect("Unable to find a GPU! Make sure you have installed required drivers!");
#[cfg(feature = "trace")]
let trace_path = {
let path = std::path::Path::new("wgpu_trace");
// ignore potential error, wgpu will log it
let _ = std::fs::create_dir(path);
Some(path)
};
#[cfg(not(feature = "trace"))]
let trace_path = None;
let (device, queue) = adapter
.request_device(&device_descriptor, trace_path)
.await
.unwrap();
let device = Arc::new(device);
let queue = Arc::new(queue);
(instance, RenderDevice::from(device), queue)
}
pub struct RenderContext {
pub render_device: RenderDevice,
pub command_encoder: CommandEncoder,
}

View file

@ -1,59 +0,0 @@
use super::RenderResourceContext;
use crate::{
pass::{ComputePass, PassDescriptor, RenderPass},
render_resource::{BufferId, TextureId},
texture::Extent3d,
};
pub trait RenderContext {
fn resources(&self) -> &dyn RenderResourceContext;
fn resources_mut(&mut self) -> &mut dyn RenderResourceContext;
fn copy_buffer_to_buffer(
&mut self,
source_buffer: BufferId,
source_offset: u64,
destination_buffer: BufferId,
destination_offset: u64,
size: u64,
);
#[allow(clippy::too_many_arguments)]
fn copy_buffer_to_texture(
&mut self,
source_buffer: BufferId,
source_offset: u64,
source_bytes_per_row: u32,
destination_texture: TextureId,
destination_origin: [u32; 3],
destination_mip_level: u32,
size: Extent3d,
);
#[allow(clippy::too_many_arguments)]
fn copy_texture_to_buffer(
&mut self,
source_texture: TextureId,
source_origin: [u32; 3],
source_mip_level: u32,
destination_buffer: BufferId,
destination_offset: u64,
destination_bytes_per_row: u32,
size: Extent3d,
);
#[allow(clippy::too_many_arguments)]
fn copy_texture_to_texture(
&mut self,
source_texture: TextureId,
source_origin: [u32; 3],
source_mip_level: u32,
destination_texture: TextureId,
destination_origin: [u32; 3],
destination_mip_level: u32,
size: Extent3d,
);
fn begin_render_pass(
&mut self,
pass_descriptor: &PassDescriptor,
run_pass: &mut dyn FnMut(&mut dyn RenderPass),
);
fn begin_compute_pass(&mut self, run_pass: &mut dyn FnMut(&mut dyn ComputePass));
}

View file

@ -0,0 +1,170 @@
use futures_lite::future;
use wgpu::util::DeviceExt;
use crate::render_resource::{
BindGroup, Buffer, ComputePipeline, RenderPipeline, Sampler, Texture,
};
use std::sync::Arc;
#[derive(Clone)]
pub struct RenderDevice {
device: Arc<wgpu::Device>,
}
impl From<Arc<wgpu::Device>> for RenderDevice {
fn from(device: Arc<wgpu::Device>) -> Self {
Self { device }
}
}
impl RenderDevice {
/// List all features that may be used with this device.
///
/// Functions may panic if you use unsupported features.
#[inline]
pub fn features(&self) -> wgpu::Features {
self.device.features()
}
/// List all limits that were requested of this device.
///
/// If any of these limits are exceeded, functions may panic.
#[inline]
pub fn limits(&self) -> wgpu::Limits {
self.device.limits()
}
/// Creates a shader module from either SPIR-V or WGSL source code.
#[inline]
pub fn create_shader_module(&self, desc: &wgpu::ShaderModuleDescriptor) -> wgpu::ShaderModule {
self.device.create_shader_module(desc)
}
/// Check for resource cleanups and mapping callbacks.
///
/// no-op on the web, device is automatically polled.
#[inline]
pub fn poll(&self, maintain: wgpu::Maintain) {
self.device.poll(maintain)
}
/// Creates an empty [`CommandEncoder`].
#[inline]
pub fn create_command_encoder(
&self,
desc: &wgpu::CommandEncoderDescriptor,
) -> wgpu::CommandEncoder {
self.device.create_command_encoder(desc)
}
/// Creates an empty [`RenderBundleEncoder`].
#[inline]
pub fn create_render_bundle_encoder(
&self,
desc: &wgpu::RenderBundleEncoderDescriptor,
) -> wgpu::RenderBundleEncoder {
self.device.create_render_bundle_encoder(desc)
}
/// Creates a new [`BindGroup`].
#[inline]
pub fn create_bind_group(&self, desc: &wgpu::BindGroupDescriptor) -> BindGroup {
let wgpu_bind_group = self.device.create_bind_group(desc);
BindGroup::from(wgpu_bind_group)
}
/// Creates a [`BindGroupLayout`].
#[inline]
pub fn create_bind_group_layout(
&self,
desc: &wgpu::BindGroupLayoutDescriptor,
) -> wgpu::BindGroupLayout {
self.device.create_bind_group_layout(desc)
}
/// Creates a [`PipelineLayout`].
#[inline]
pub fn create_pipeline_layout(
&self,
desc: &wgpu::PipelineLayoutDescriptor,
) -> wgpu::PipelineLayout {
self.device.create_pipeline_layout(desc)
}
/// Creates a [`RenderPipeline`].
#[inline]
pub fn create_render_pipeline(&self, desc: &wgpu::RenderPipelineDescriptor) -> RenderPipeline {
let wgpu_render_pipeline = self.device.create_render_pipeline(desc);
RenderPipeline::from(wgpu_render_pipeline)
}
/// Creates a [`ComputePipeline`].
#[inline]
pub fn create_compute_pipeline(
&self,
desc: &wgpu::ComputePipelineDescriptor,
) -> ComputePipeline {
let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);
ComputePipeline::from(wgpu_compute_pipeline)
}
/// Creates a [`Buffer`].
pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {
let wgpu_buffer = self.device.create_buffer(desc);
Buffer::from(wgpu_buffer)
}
/// Creates a [`Buffer`].
pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {
let wgpu_buffer = self.device.create_buffer_init(desc);
Buffer::from(wgpu_buffer)
}
/// Creates a new [`Texture`].
///
/// `desc` specifies the general format of the texture.
pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {
let wgpu_texture = self.device.create_texture(desc);
Texture::from(wgpu_texture)
}
/// Creates a new [`Sampler`].
///
/// `desc` specifies the behavior of the sampler.
pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {
let wgpu_sampler = self.device.create_sampler(desc);
Sampler::from(wgpu_sampler)
}
/// Create a new [`SwapChain`] which targets `surface`.
///
/// # Panics
///
/// - A old [`SwapChainFrame`] is still alive referencing an old swapchain.
/// - Texture format requested is unsupported on the swap chain.
pub fn create_swap_chain(
&self,
surface: &wgpu::Surface,
desc: &wgpu::SwapChainDescriptor,
) -> wgpu::SwapChain {
self.device.create_swap_chain(surface, desc)
}
pub fn wgpu_device(&self) -> &wgpu::Device {
&self.device
}
pub fn map_buffer(&self, buffer: &wgpu::BufferSlice, map_mode: wgpu::MapMode) {
let data = buffer.map_async(map_mode);
self.poll(wgpu::Maintain::Wait);
if future::block_on(data).is_err() {
panic!("Failed to map buffer to host.");
}
}
pub fn align_copy_bytes_per_row(row_bytes: usize) -> usize {
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
let padded_bytes_per_row_padding = (align - row_bytes % align) % align;
row_bytes + padded_bytes_per_row_padding
}
}

View file

@ -1,89 +0,0 @@
use crate::{
pipeline::{
BindGroupDescriptorId, ComputePipelineDescriptor, PipelineId, RenderPipelineDescriptor,
},
render_resource::{
BindGroup, BufferId, BufferInfo, BufferMapMode, SamplerId, SwapChainDescriptor, TextureId,
TextureViewId,
},
shader::{Shader, ShaderId},
texture::{SamplerDescriptor, TextureDescriptor, TextureViewDescriptor},
};
use downcast_rs::{impl_downcast, Downcast};
use std::ops::{Deref, DerefMut, Range};
pub struct RenderResources(Box<dyn RenderResourceContext>);
impl RenderResources {
pub fn new(context: Box<dyn RenderResourceContext>) -> Self {
Self(context)
}
}
impl Deref for RenderResources {
type Target = dyn RenderResourceContext;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl DerefMut for RenderResources {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.0
}
}
pub trait RenderResourceContext: Downcast + Send + Sync + 'static {
fn next_swap_chain_texture(&self, descriptor: &SwapChainDescriptor) -> TextureViewId;
fn drop_swap_chain_texture(&self, resource: TextureViewId);
fn drop_all_swap_chain_textures(&self);
fn create_sampler(&self, sampler_descriptor: &SamplerDescriptor) -> SamplerId;
fn create_texture(&self, texture_descriptor: TextureDescriptor) -> TextureId;
fn create_texture_view(
&self,
texture_id: TextureId,
texture_view_descriptor: TextureViewDescriptor,
) -> TextureViewId;
fn create_buffer(&self, buffer_info: BufferInfo) -> BufferId;
// TODO: remove RenderResourceContext here
fn write_mapped_buffer(
&self,
id: BufferId,
range: Range<u64>,
write: &mut dyn FnMut(&mut [u8], &dyn RenderResourceContext),
);
fn read_mapped_buffer(
&self,
id: BufferId,
range: Range<u64>,
read: &dyn Fn(&[u8], &dyn RenderResourceContext),
);
fn map_buffer(&self, id: BufferId, mode: BufferMapMode);
fn unmap_buffer(&self, id: BufferId);
fn create_buffer_with_data(&self, buffer_info: BufferInfo, data: &[u8]) -> BufferId;
fn create_shader_module(&self, shader: &Shader) -> ShaderId;
fn remove_buffer(&self, buffer: BufferId);
fn remove_texture(&self, texture: TextureId);
fn remove_sampler(&self, sampler: SamplerId);
fn remove_texture_view(&self, texture_view: TextureViewId);
fn get_buffer_info(&self, buffer: BufferId) -> Option<BufferInfo>;
fn get_aligned_uniform_size(&self, size: usize, dynamic: bool) -> usize;
fn get_aligned_texture_size(&self, data_size: usize) -> usize;
fn create_render_pipeline(&self, pipeline_descriptor: &RenderPipelineDescriptor) -> PipelineId;
fn create_compute_pipeline(
&self,
_pipeline_descriptor: &ComputePipelineDescriptor,
) -> PipelineId;
fn bind_group_descriptor_exists(&self, bind_group_descriptor_id: BindGroupDescriptorId)
-> bool;
fn create_bind_group(
&self,
bind_group_descriptor_id: BindGroupDescriptorId,
bind_group: &BindGroup,
);
fn clear_bind_groups(&self);
fn remove_stale_bind_groups(&self);
}
impl_downcast!(RenderResourceContext);

View file

@ -1,24 +1,4 @@
#[allow(clippy::module_inception)]
mod shader;
#[cfg(not(target_arch = "wasm32"))]
mod shader_reflect;
pub use shader::*;
#[cfg(not(target_arch = "wasm32"))]
pub use shader_reflect::*;
use crate::pipeline::{BindGroupDescriptor, VertexBufferLayout};
/// Defines the memory layout of a shader
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShaderLayout {
pub bind_groups: Vec<BindGroupDescriptor>,
pub vertex_buffer_layout: Vec<VertexBufferLayout>,
pub entry_point: String,
}
pub const GL_VERTEX_INDEX: &str = "gl_VertexIndex";
pub const GL_INSTANCE_INDEX: &str = "gl_InstanceIndex";
pub const GL_FRONT_FACING: &str = "gl_FrontFacing";
pub use shader::*;

View file

@ -1,17 +1,10 @@
use super::ShaderLayout;
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
use bevy_reflect::{TypeUuid, Uuid};
use bevy_utils::{tracing::error, BoxedFuture};
use wgpu::ShaderStage;
use std::marker::Copy;
use thiserror::Error;
/// The stage of a shader
#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
pub enum ShaderStage {
Vertex,
Fragment,
Compute,
}
/// An error that occurs during shader handling.
#[derive(Error, Debug)]
@ -62,13 +55,12 @@ pub enum ShaderError {
all(target_arch = "armv7", target_os = "androidabi"),
all(target_arch = "x86_64", target_os = "windows", target_env = "msvc"),
))]
impl From<ShaderStage> for bevy_glsl_to_spirv::ShaderType {
fn from(s: ShaderStage) -> bevy_glsl_to_spirv::ShaderType {
match s {
ShaderStage::Vertex => bevy_glsl_to_spirv::ShaderType::Vertex,
ShaderStage::Fragment => bevy_glsl_to_spirv::ShaderType::Fragment,
ShaderStage::Compute => bevy_glsl_to_spirv::ShaderType::Compute,
}
fn convert_stage(s: ShaderStage) -> bevy_glsl_to_spirv::ShaderType {
match s {
ShaderStage::VERTEX => bevy_glsl_to_spirv::ShaderType::Vertex,
ShaderStage::FRAGMENT => bevy_glsl_to_spirv::ShaderType::Fragment,
ShaderStage::COMPUTE => bevy_glsl_to_spirv::ShaderType::Compute,
_ => panic!("unsupported stage type"),
}
}
@ -84,7 +76,7 @@ pub fn glsl_to_spirv(
stage: ShaderStage,
shader_defs: Option<&[String]>,
) -> Result<Vec<u32>, ShaderError> {
bevy_glsl_to_spirv::compile(glsl_source, stage.into(), shader_defs)
bevy_glsl_to_spirv::compile(glsl_source, convert_stage(stage), shader_defs)
.map_err(ShaderError::Compilation)
}
@ -194,8 +186,8 @@ impl Shader {
let module = ShaderModule::load_u8_data(spirv)
.map_err(|msg| ShaderError::Compilation(msg.to_string()))?;
let stage = match module.get_shader_stage() {
ReflectShaderStageFlags::VERTEX => ShaderStage::Vertex,
ReflectShaderStageFlags::FRAGMENT => ShaderStage::Fragment,
ReflectShaderStageFlags::VERTEX => ShaderStage::VERTEX,
ReflectShaderStageFlags::FRAGMENT => ShaderStage::FRAGMENT,
other => panic!("cannot load {:?} shader", other),
};
@ -227,23 +219,6 @@ impl Shader {
stage: self.stage,
})
}
#[cfg(not(target_arch = "wasm32"))]
pub fn reflect_layout(&self, enforce_bevy_conventions: bool) -> Option<ShaderLayout> {
if let ShaderSource::Spirv(ref spirv) = self.source {
Some(ShaderLayout::from_spirv(
spirv.as_slice(),
enforce_bevy_conventions,
))
} else {
panic!("Cannot reflect layout of non-SpirV shader. Try compiling this shader to SpirV first using self.get_spirv_shader().");
}
}
#[cfg(target_arch = "wasm32")]
pub fn reflect_layout(&self, _enforce_bevy_conventions: bool) -> Option<ShaderLayout> {
panic!("Cannot reflect layout on wasm32.");
}
}
/// All stages in a shader program
@ -272,8 +247,8 @@ impl AssetLoader for ShaderLoader {
let ext = load_context.path().extension().unwrap().to_str().unwrap();
let shader = match ext {
"vert" => Shader::from_glsl(ShaderStage::Vertex, std::str::from_utf8(bytes)?),
"frag" => Shader::from_glsl(ShaderStage::Fragment, std::str::from_utf8(bytes)?),
"vert" => Shader::from_glsl(ShaderStage::VERTEX, std::str::from_utf8(bytes)?),
"frag" => Shader::from_glsl(ShaderStage::FRAGMENT, std::str::from_utf8(bytes)?),
#[cfg(not(target_arch = "wasm32"))]
"spv" => Shader::from_spirv(bytes)?,
#[cfg(target_arch = "wasm32")]

View file

@ -1,417 +0,0 @@
use crate::{
pipeline::{
BindGroupDescriptor, BindType, BindingDescriptor, BindingShaderStage, InputStepMode,
UniformProperty, VertexAttribute, VertexBufferLayout, VertexFormat,
},
shader::{ShaderLayout, GL_FRONT_FACING, GL_INSTANCE_INDEX, GL_VERTEX_INDEX},
texture::{TextureSampleType, TextureViewDimension},
};
use bevy_core::cast_slice;
use spirv_reflect::{
types::{
ReflectDescriptorBinding, ReflectDescriptorSet, ReflectDescriptorType, ReflectDimension,
ReflectShaderStageFlags, ReflectTypeDescription, ReflectTypeFlags,
},
ShaderModule,
};
impl ShaderLayout {
pub fn from_spirv(spirv_data: &[u32], bevy_conventions: bool) -> ShaderLayout {
match ShaderModule::load_u8_data(cast_slice(spirv_data)) {
Ok(ref mut module) => {
// init
let entry_point_name = module.get_entry_point_name();
let shader_stage = module.get_shader_stage();
let mut bind_groups = Vec::new();
for descriptor_set in module.enumerate_descriptor_sets(None).unwrap() {
let bind_group = reflect_bind_group(&descriptor_set, shader_stage);
bind_groups.push(bind_group);
}
// obtain attribute descriptors from reflection
let mut vertex_attributes = Vec::new();
for input_variable in module.enumerate_input_variables(None).unwrap() {
if input_variable.name == GL_VERTEX_INDEX
|| input_variable.name == GL_INSTANCE_INDEX
|| input_variable.name == GL_FRONT_FACING
{
continue;
}
// reflect vertex attribute descriptor and record it
vertex_attributes.push(VertexAttribute {
name: input_variable.name.clone().into(),
format: reflect_vertex_format(
input_variable.type_description.as_ref().unwrap(),
),
offset: 0,
shader_location: input_variable.location,
});
}
vertex_attributes.sort_by(|a, b| a.shader_location.cmp(&b.shader_location));
let mut vertex_buffer_layout = Vec::new();
for vertex_attribute in vertex_attributes.drain(..) {
let mut instance = false;
// obtain buffer name and instancing flag
let current_buffer_name = {
if bevy_conventions {
if vertex_attribute.name == GL_VERTEX_INDEX {
GL_VERTEX_INDEX.to_string()
} else {
instance = vertex_attribute.name.starts_with("I_");
vertex_attribute.name.to_string()
}
} else {
"DefaultVertex".to_string()
}
};
// create a new buffer descriptor, per attribute!
vertex_buffer_layout.push(VertexBufferLayout {
attributes: vec![vertex_attribute],
name: current_buffer_name.into(),
step_mode: if instance {
InputStepMode::Instance
} else {
InputStepMode::Vertex
},
stride: 0,
});
}
ShaderLayout {
bind_groups,
vertex_buffer_layout,
entry_point: entry_point_name,
}
}
Err(err) => panic!("Failed to reflect shader layout: {:?}.", err),
}
}
}
fn reflect_bind_group(
descriptor_set: &ReflectDescriptorSet,
shader_stage: ReflectShaderStageFlags,
) -> BindGroupDescriptor {
let mut bindings = Vec::new();
for descriptor_binding in descriptor_set.bindings.iter() {
let binding = reflect_binding(descriptor_binding, shader_stage);
bindings.push(binding);
}
BindGroupDescriptor::new(descriptor_set.set, bindings)
}
fn reflect_dimension(type_description: &ReflectTypeDescription) -> TextureViewDimension {
let arrayed = type_description.traits.image.arrayed > 0;
match type_description.traits.image.dim {
ReflectDimension::Type1d => TextureViewDimension::D1,
ReflectDimension::Type2d => {
if arrayed {
TextureViewDimension::D2Array
} else {
TextureViewDimension::D2
}
}
ReflectDimension::Type3d => TextureViewDimension::D3,
ReflectDimension::Cube => {
if arrayed {
TextureViewDimension::CubeArray
} else {
TextureViewDimension::Cube
}
}
dimension => panic!("Unsupported image dimension: {:?}.", dimension),
}
}
fn reflect_binding(
binding: &ReflectDescriptorBinding,
shader_stage: ReflectShaderStageFlags,
) -> BindingDescriptor {
let type_description = binding.type_description.as_ref().unwrap();
let (name, bind_type) = match binding.descriptor_type {
ReflectDescriptorType::UniformBuffer => (
&type_description.type_name,
BindType::Uniform {
has_dynamic_offset: false,
property: reflect_uniform(type_description),
},
),
ReflectDescriptorType::SampledImage => (
&binding.name,
BindType::Texture {
view_dimension: reflect_dimension(type_description),
sample_type: TextureSampleType::Float { filterable: true },
multisampled: false,
},
),
ReflectDescriptorType::StorageBuffer => (
&type_description.type_name,
BindType::StorageBuffer {
has_dynamic_offset: false,
readonly: true,
},
),
// TODO: detect comparison "true" case: https://github.com/gpuweb/gpuweb/issues/552
// TODO: detect filtering "true" case
ReflectDescriptorType::Sampler => (
&binding.name,
BindType::Sampler {
comparison: false,
filtering: true,
},
),
_ => panic!("Unsupported bind type {:?}.", binding.descriptor_type),
};
let shader_stage = match shader_stage {
ReflectShaderStageFlags::COMPUTE => BindingShaderStage::COMPUTE,
ReflectShaderStageFlags::VERTEX => BindingShaderStage::VERTEX,
ReflectShaderStageFlags::FRAGMENT => BindingShaderStage::FRAGMENT,
_ => panic!("Only one specified shader stage is supported."),
};
BindingDescriptor {
index: binding.binding,
bind_type,
name: name.to_string(),
shader_stage,
}
}
#[derive(Debug)]
enum NumberType {
Int,
UInt,
Float,
}
fn reflect_uniform(type_description: &ReflectTypeDescription) -> UniformProperty {
if type_description
.type_flags
.contains(ReflectTypeFlags::STRUCT)
{
reflect_uniform_struct(type_description)
} else {
reflect_uniform_numeric(type_description)
}
}
fn reflect_uniform_struct(type_description: &ReflectTypeDescription) -> UniformProperty {
let mut properties = Vec::new();
for member in type_description.members.iter() {
properties.push(reflect_uniform(member));
}
UniformProperty::Struct(properties)
}
fn reflect_uniform_numeric(type_description: &ReflectTypeDescription) -> UniformProperty {
let traits = &type_description.traits;
let number_type = if type_description.type_flags.contains(ReflectTypeFlags::INT) {
match traits.numeric.scalar.signedness {
0 => NumberType::UInt,
1 => NumberType::Int,
signedness => panic!("Unexpected signedness {}.", signedness),
}
} else if type_description
.type_flags
.contains(ReflectTypeFlags::FLOAT)
{
NumberType::Float
} else {
panic!("Unexpected type flag {:?}.", type_description.type_flags);
};
// TODO: handle scalar width here
if type_description
.type_flags
.contains(ReflectTypeFlags::MATRIX)
{
match (
number_type,
traits.numeric.matrix.column_count,
traits.numeric.matrix.row_count,
) {
(NumberType::Float, 3, 3) => UniformProperty::Mat3,
(NumberType::Float, 4, 4) => UniformProperty::Mat4,
(number_type, column_count, row_count) => panic!(
"unexpected uniform property matrix format {:?} {}x{}",
number_type, column_count, row_count
),
}
} else {
match (number_type, traits.numeric.vector.component_count) {
(NumberType::UInt, 0) => UniformProperty::UInt,
(NumberType::Int, 0) => UniformProperty::Int,
(NumberType::Int, 2) => UniformProperty::IVec2,
(NumberType::Float, 0) => UniformProperty::Float,
(NumberType::Float, 2) => UniformProperty::Vec2,
(NumberType::Float, 3) => UniformProperty::Vec3,
(NumberType::Float, 4) => UniformProperty::Vec4,
(NumberType::UInt, 4) => UniformProperty::UVec4,
(number_type, component_count) => panic!(
"unexpected uniform property format {:?} {}",
number_type, component_count
),
}
}
}
fn reflect_vertex_format(type_description: &ReflectTypeDescription) -> VertexFormat {
let traits = &type_description.traits;
let number_type = if type_description.type_flags.contains(ReflectTypeFlags::INT) {
match traits.numeric.scalar.signedness {
0 => NumberType::UInt,
1 => NumberType::Int,
signedness => panic!("Unexpected signedness {}.", signedness),
}
} else if type_description
.type_flags
.contains(ReflectTypeFlags::FLOAT)
{
NumberType::Float
} else {
panic!("Unexpected type flag {:?}.", type_description.type_flags);
};
let width = traits.numeric.scalar.width;
match (number_type, traits.numeric.vector.component_count, width) {
(NumberType::UInt, 2, 8) => VertexFormat::Uint8x2,
(NumberType::UInt, 4, 8) => VertexFormat::Uint8x4,
(NumberType::Int, 2, 8) => VertexFormat::Sint8x2,
(NumberType::Int, 4, 8) => VertexFormat::Sint8x4,
(NumberType::UInt, 2, 16) => VertexFormat::Uint16x2,
(NumberType::UInt, 4, 16) => VertexFormat::Uint16x4,
(NumberType::Int, 2, 16) => VertexFormat::Sint16x2,
(NumberType::Int, 8, 16) => VertexFormat::Sint16x4,
(NumberType::Float, 2, 16) => VertexFormat::Float16x2,
(NumberType::Float, 4, 16) => VertexFormat::Float16x4,
(NumberType::Float, 0, 32) => VertexFormat::Float32,
(NumberType::Float, 2, 32) => VertexFormat::Float32x2,
(NumberType::Float, 3, 32) => VertexFormat::Float32x3,
(NumberType::Float, 4, 32) => VertexFormat::Float32x4,
(NumberType::UInt, 0, 32) => VertexFormat::Uint32,
(NumberType::UInt, 2, 32) => VertexFormat::Uint32x2,
(NumberType::UInt, 3, 32) => VertexFormat::Uint32x3,
(NumberType::UInt, 4, 32) => VertexFormat::Uint32x4,
(NumberType::Int, 0, 32) => VertexFormat::Sint32,
(NumberType::Int, 2, 32) => VertexFormat::Sint32x2,
(NumberType::Int, 3, 32) => VertexFormat::Sint32x3,
(NumberType::Int, 4, 32) => VertexFormat::Sint32x4,
(number_type, component_count, width) => panic!(
"unexpected uniform property format {:?} {} {}",
number_type, component_count, width
),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shader::{Shader, ShaderStage};
impl VertexBufferLayout {
pub fn test_zero_stride(mut self) -> VertexBufferLayout {
self.stride = 0;
self
}
}
#[test]
fn test_reflection() {
let vertex_shader = Shader::from_glsl(
ShaderStage::Vertex,
r#"
#version 450
layout(location = 0) in vec4 Vertex_Position;
layout(location = 1) in uvec4 Vertex_Normal;
layout(location = 2) in uvec4 I_TestInstancing_Property;
layout(location = 0) out vec4 v_Position;
layout(set = 0, binding = 0) uniform CameraViewProj {
mat4 ViewProj;
};
layout(set = 1, binding = 0) uniform texture2D Texture;
void main() {
v_Position = Vertex_Position;
gl_Position = ViewProj * v_Position;
}
"#,
)
.get_spirv_shader(None)
.unwrap();
let layout = vertex_shader.reflect_layout(true).unwrap();
assert_eq!(
layout,
ShaderLayout {
entry_point: "main".into(),
vertex_buffer_layout: vec![
VertexBufferLayout::new_from_attribute(
VertexAttribute {
name: "Vertex_Position".into(),
format: VertexFormat::Float32x4,
offset: 0,
shader_location: 0,
},
InputStepMode::Vertex
)
.test_zero_stride(),
VertexBufferLayout::new_from_attribute(
VertexAttribute {
name: "Vertex_Normal".into(),
format: VertexFormat::Uint32x4,
offset: 0,
shader_location: 1,
},
InputStepMode::Vertex
)
.test_zero_stride(),
VertexBufferLayout::new_from_attribute(
VertexAttribute {
name: "I_TestInstancing_Property".into(),
format: VertexFormat::Uint32x4,
offset: 0,
shader_location: 2,
},
InputStepMode::Instance
)
.test_zero_stride(),
],
bind_groups: vec![
BindGroupDescriptor::new(
0,
vec![BindingDescriptor {
index: 0,
name: "CameraViewProj".into(),
bind_type: BindType::Uniform {
has_dynamic_offset: false,
property: UniformProperty::Struct(vec![UniformProperty::Mat4]),
},
shader_stage: BindingShaderStage::VERTEX,
}]
),
BindGroupDescriptor::new(
1,
vec![BindingDescriptor {
index: 0,
name: "Texture".into(),
bind_type: BindType::Texture {
multisampled: false,
view_dimension: TextureViewDimension::D2,
sample_type: TextureSampleType::Float { filterable: true }
},
shader_stage: BindingShaderStage::VERTEX,
}]
),
]
}
);
}
}

View file

@ -1,7 +1,8 @@
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
use crate::texture::{Image, TextureFormatPixelInfo};
use anyhow::Result;
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
use bevy_utils::BoxedFuture;
use wgpu::{Extent3d, TextureDimension, TextureFormat};
/// Loads HDR textures as Texture assets
#[derive(Clone, Default)]
@ -35,8 +36,12 @@ impl AssetLoader for HdrTextureLoader {
rgba_data.extend_from_slice(&alpha.to_ne_bytes());
}
let texture = Texture::new(
Extent3d::new(info.width, info.height, 1),
let texture = Image::new(
Extent3d {
width: info.width,
height: info.height,
depth_or_array_layers: 1,
},
TextureDimension::D2,
rgba_data,
format,

View file

@ -1,7 +1,9 @@
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
use crate::texture::{Image, TextureFormatPixelInfo};
use wgpu::{Extent3d, TextureDimension, TextureFormat};
// TODO: fix name?
/// Helper method to convert a `DynamicImage` to a `Texture`
pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Image {
use bevy_core::cast_slice;
let width;
let height;
@ -110,8 +112,12 @@ pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
}
}
Texture::new(
Extent3d::new(width, height, 1),
Image::new(
Extent3d {
width,
height,
depth_or_array_layers: 1,
},
TextureDimension::D2,
data,
format,
@ -120,29 +126,29 @@ pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
/// Helper method to convert a `Texture` to a `DynamicImage`. Not all `Texture` formats are
/// covered, it will return `None` if the format is not supported
pub(crate) fn texture_to_image(texture: &Texture) -> Option<image::DynamicImage> {
match texture.format {
pub(crate) fn texture_to_image(texture: &Image) -> Option<image::DynamicImage> {
match texture.texture_descriptor.format {
TextureFormat::R8Unorm => image::ImageBuffer::from_raw(
texture.size.width,
texture.size.height,
texture.texture_descriptor.size.width,
texture.texture_descriptor.size.height,
texture.data.clone(),
)
.map(image::DynamicImage::ImageLuma8),
TextureFormat::Rg8Unorm => image::ImageBuffer::from_raw(
texture.size.width,
texture.size.height,
texture.texture_descriptor.size.width,
texture.texture_descriptor.size.height,
texture.data.clone(),
)
.map(image::DynamicImage::ImageLumaA8),
TextureFormat::Rgba8UnormSrgb => image::ImageBuffer::from_raw(
texture.size.width,
texture.size.height,
texture.texture_descriptor.size.width,
texture.texture_descriptor.size.height,
texture.data.clone(),
)
.map(image::DynamicImage::ImageRgba8),
TextureFormat::Bgra8UnormSrgb => image::ImageBuffer::from_raw(
texture.size.width,
texture.size.height,
texture.texture_descriptor.size.width,
texture.texture_descriptor.size.height,
texture.data.clone(),
)
.map(image::DynamicImage::ImageBgra8),

View file

@ -1,9 +1,10 @@
use super::texture::{ImageType, Texture, TextureError};
use anyhow::Result;
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
use bevy_utils::BoxedFuture;
use thiserror::Error;
use crate::texture::{Image, ImageType, TextureError};
/// Loader for images that can be read by the `image` crate.
#[derive(Clone, Default)]
pub struct ImageTextureLoader;
@ -20,13 +21,12 @@ impl AssetLoader for ImageTextureLoader {
// use the file extension for the image type
let ext = load_context.path().extension().unwrap().to_str().unwrap();
let dyn_img =
Texture::from_buffer(bytes, ImageType::Extension(ext)).map_err(|err| {
FileTextureError {
error: err,
path: format!("{}", load_context.path().display()),
}
})?;
let dyn_img = Image::from_buffer(bytes, ImageType::Extension(ext)).map_err(|err| {
FileTextureError {
error: err,
path: format!("{}", load_context.path().display()),
}
})?;
load_context.set_default_asset(LoadedAsset::new(dyn_img));
Ok(())

View file

@ -1,46 +1,40 @@
#[cfg(feature = "hdr")]
mod hdr_texture_loader;
mod image_texture_loader;
mod sampler_descriptor;
#[allow(clippy::module_inception)]
mod texture;
mod texture_cache;
mod texture_descriptor;
mod texture_dimension;
pub(crate) mod image_texture_conversion;
#[cfg(feature = "hdr")]
pub use hdr_texture_loader::*;
pub use image_texture_loader::*;
pub use sampler_descriptor::*;
pub use texture::*;
pub use texture_cache::*;
pub use texture_descriptor::*;
pub use texture_dimension::*;
use crate::{
render_command::RenderCommandQueue,
render_resource::{BufferInfo, BufferUsage},
renderer::{RenderResourceContext, RenderResources},
renderer::{RenderDevice, RenderQueue},
RenderStage,
};
use bevy_app::{App, CoreStage, Plugin};
use bevy_asset::{AddAsset, AssetEvent, Assets, Handle};
use bevy_asset::{AddAsset, AssetEvent, Assets};
use bevy_ecs::prelude::*;
use bevy_utils::HashSet;
use wgpu::{ImageCopyTexture, ImageDataLayout, Origin3d, TextureViewDescriptor};
pub struct TexturePlugin;
// TODO: replace Texture names with Image names?
pub struct ImagePlugin;
impl Plugin for TexturePlugin {
impl Plugin for ImagePlugin {
fn build(&self, app: &mut App) {
#[cfg(feature = "png")]
{
app.init_asset_loader::<ImageTextureLoader>();
}
app.add_system_to_stage(CoreStage::PostUpdate, texture_resource_system.system())
.add_asset::<Texture>();
app.add_system_to_stage(CoreStage::PostUpdate, image_resource_system.system())
.add_asset::<Image>();
let render_app = app.sub_app_mut(0);
render_app
@ -49,104 +43,104 @@ impl Plugin for TexturePlugin {
}
}
pub fn texture_resource_system(
render_resource_context: Res<RenderResources>,
mut render_command_queue: ResMut<RenderCommandQueue>,
mut textures: ResMut<Assets<Texture>>,
mut texture_events: EventReader<AssetEvent<Texture>>,
pub fn image_resource_system(
render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>,
mut images: ResMut<Assets<Image>>,
mut image_events: EventReader<AssetEvent<Image>>,
) {
let render_resource_context = &**render_resource_context;
let mut changed_textures = HashSet::default();
for event in texture_events.iter() {
let mut changed_images = HashSet::default();
for event in image_events.iter() {
match event {
AssetEvent::Created { handle } => {
changed_textures.insert(handle);
changed_images.insert(handle);
}
AssetEvent::Modified { handle } => {
changed_textures.insert(handle);
changed_images.insert(handle);
// TODO: uncomment this to support mutated textures
// remove_current_texture_resources(render_resource_context, handle, &mut textures);
}
AssetEvent::Removed { handle } => {
remove_current_texture_resources(render_resource_context, handle, &mut textures);
// if texture was modified and removed in the same update, ignore the
// modification events are ordered so future modification
// events are ok
changed_textures.remove(handle);
changed_images.remove(handle);
}
}
}
for texture_handle in changed_textures.iter() {
if let Some(texture) = textures.get_mut(*texture_handle) {
for image_handle in changed_images.iter() {
if let Some(image) = images.get_mut(*image_handle) {
// TODO: this avoids creating new textures each frame because storing gpu data in the texture flags it as
// modified. this prevents hot reloading and therefore can't be used in an actual impl.
if texture.gpu_data.is_some() {
if image.gpu_data.is_some() {
continue;
}
// TODO: free old buffers / textures / samplers
// TODO: using Into for TextureDescriptor is weird
let texture_descriptor: TextureDescriptor = (&*texture).into();
let texture_id = render_resource_context.create_texture(texture_descriptor);
let texture = render_device.create_texture(&image.texture_descriptor);
let sampler = render_device.create_sampler(&image.sampler_descriptor);
let sampler_id = render_resource_context.create_sampler(&texture.sampler);
let width = image.texture_descriptor.size.width as usize;
let format_size = image.texture_descriptor.format.pixel_size();
// let mut aligned_data = vec![
// 0;
// format_size
// * aligned_width
// * image.texture_descriptor.size.height as usize
// * image.texture_descriptor.size.depth_or_array_layers
// as usize
// ];
// image
// .data
// .chunks_exact(format_size * width)
// .enumerate()
// .for_each(|(index, row)| {
// let offset = index * aligned_width * format_size;
// aligned_data[offset..(offset + width * format_size)].copy_from_slice(row);
// });
let width = texture.size.width as usize;
let aligned_width = render_resource_context.get_aligned_texture_size(width);
let format_size = texture.format.pixel_size();
let mut aligned_data = vec![
0;
format_size
* aligned_width
* texture.size.height as usize
* texture.size.depth_or_array_layers as usize
];
texture
.data
.chunks_exact(format_size * width)
.enumerate()
.for_each(|(index, row)| {
let offset = index * aligned_width * format_size;
aligned_data[offset..(offset + width * format_size)].copy_from_slice(row);
});
let staging_buffer_id = render_resource_context.create_buffer_with_data(
BufferInfo {
buffer_usage: BufferUsage::COPY_SRC,
..Default::default()
// TODO: this might require different alignment. docs seem to say that we don't need it though
render_queue.write_texture(
ImageCopyTexture {
texture: &texture,
mip_level: 0,
origin: Origin3d::ZERO,
},
&aligned_data,
&image.data,
ImageDataLayout {
offset: 0,
bytes_per_row: Some(
std::num::NonZeroU32::new(
image.texture_descriptor.size.width * format_size as u32,
)
.unwrap(),
),
rows_per_image: None,
},
image.texture_descriptor.size,
);
let texture_view_id = render_resource_context
.create_texture_view(texture_id, TextureViewDescriptor::default());
texture.gpu_data = Some(TextureGpuData {
texture: texture_id,
texture_view: texture_view_id,
sampler: sampler_id,
let texture_view = texture.create_view(&TextureViewDescriptor::default());
image.gpu_data = Some(ImageGpuData {
texture,
texture_view,
sampler,
});
render_command_queue.copy_buffer_to_texture(
staging_buffer_id,
0,
(format_size * aligned_width) as u32,
texture_id,
[0, 0, 0],
0,
texture_descriptor.size,
);
render_command_queue.free_buffer(staging_buffer_id);
}
}
}
fn remove_current_texture_resources(
render_resource_context: &dyn RenderResourceContext,
handle: &Handle<Texture>,
textures: &mut Assets<Texture>,
) {
if let Some(gpu_data) = textures.get_mut(handle).and_then(|t| t.gpu_data.take()) {
render_resource_context.remove_texture(gpu_data.texture);
render_resource_context.remove_sampler(gpu_data.sampler);
pub trait BevyDefault {
fn bevy_default() -> Self;
}
impl BevyDefault for wgpu::TextureFormat {
fn bevy_default() -> Self {
if cfg!(target_os = "android") {
// Bgra8UnormSrgb texture missing on some Android devices
wgpu::TextureFormat::Rgba8UnormSrgb
} else {
wgpu::TextureFormat::Bgra8UnormSrgb
}
}
}

View file

@ -1,79 +0,0 @@
use crate::pipeline::CompareFunction;
use std::num::NonZeroU8;
/// Describes a sampler
#[derive(Debug, Copy, Clone)]
pub struct SamplerDescriptor {
pub address_mode_u: AddressMode,
pub address_mode_v: AddressMode,
pub address_mode_w: AddressMode,
pub mag_filter: FilterMode,
pub min_filter: FilterMode,
pub mipmap_filter: FilterMode,
pub lod_min_clamp: f32,
pub lod_max_clamp: f32,
pub compare_function: Option<CompareFunction>,
pub anisotropy_clamp: Option<NonZeroU8>,
pub border_color: Option<SamplerBorderColor>,
}
impl SamplerDescriptor {
/// Sets the address mode for all dimensions of the sampler descriptor.
pub fn set_address_mode(&mut self, address_mode: AddressMode) {
self.address_mode_u = address_mode;
self.address_mode_v = address_mode;
self.address_mode_w = address_mode;
}
}
impl Default for SamplerDescriptor {
fn default() -> Self {
SamplerDescriptor {
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
mag_filter: FilterMode::Nearest,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Nearest,
lod_min_clamp: 0.0,
lod_max_clamp: std::f32::MAX,
compare_function: None,
anisotropy_clamp: None,
border_color: None,
}
}
}
/// How edges should be handled in texture addressing.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum AddressMode {
ClampToEdge = 0,
Repeat = 1,
MirrorRepeat = 2,
}
impl Default for AddressMode {
fn default() -> Self {
AddressMode::ClampToEdge
}
}
/// Texel mixing mode when sampling between texels.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum FilterMode {
Nearest = 0,
Linear = 1,
}
impl Default for FilterMode {
fn default() -> Self {
FilterMode::Nearest
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum SamplerBorderColor {
TransparentBlack,
OpaqueBlack,
OpaqueWhite,
}

View file

@ -1,51 +1,54 @@
use super::{
image_texture_conversion::image_to_texture, Extent3d, SamplerDescriptor, TextureDimension,
TextureFormat,
};
use crate::render_resource::{SamplerId, TextureId, TextureViewId};
use super::image_texture_conversion::image_to_texture;
use crate::render_resource::{Sampler, Texture, TextureView};
use bevy_reflect::TypeUuid;
use thiserror::Error;
use wgpu::{Extent3d, TextureDimension, TextureFormat};
pub const TEXTURE_ASSET_INDEX: u64 = 0;
pub const SAMPLER_ASSET_INDEX: u64 = 1;
// TODO: this shouldn't live in the Texture type
#[derive(Debug, Clone)]
pub struct TextureGpuData {
pub texture: TextureId,
pub texture_view: TextureViewId,
pub sampler: SamplerId,
pub struct ImageGpuData {
pub texture: Texture,
pub texture_view: TextureView,
pub sampler: Sampler,
}
#[derive(Debug, Clone, TypeUuid)]
#[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"]
pub struct Texture {
pub struct Image {
pub data: Vec<u8>,
pub gpu_data: Option<TextureGpuData>,
pub size: Extent3d,
pub format: TextureFormat,
pub dimension: TextureDimension,
pub sampler: SamplerDescriptor,
pub gpu_data: Option<ImageGpuData>,
// TODO: this nesting makes accessing Image metadata verbose. Either flatten out descriptor or add accessors
pub texture_descriptor: wgpu::TextureDescriptor<'static>,
pub sampler_descriptor: wgpu::SamplerDescriptor<'static>,
}
impl Default for Texture {
impl Default for Image {
fn default() -> Self {
Texture {
Image {
data: Default::default(),
gpu_data: None,
size: Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
texture_descriptor: wgpu::TextureDescriptor {
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
format: wgpu::TextureFormat::Rgba8UnormSrgb,
dimension: wgpu::TextureDimension::D2,
label: None,
mip_level_count: 1,
sample_count: 1,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
},
format: TextureFormat::Rgba8UnormSrgb,
dimension: TextureDimension::D2,
sampler: Default::default(),
sampler_descriptor: wgpu::SamplerDescriptor::default(),
}
}
}
impl Texture {
impl Image {
pub fn new(
size: Extent3d,
dimension: TextureDimension,
@ -57,13 +60,12 @@ impl Texture {
data.len(),
"Pixel data, size and format have to match",
);
Self {
data,
size,
format,
dimension,
..Default::default()
}
let mut image = Self::default();
image.data = data;
image.texture_descriptor.dimension = dimension;
image.texture_descriptor.size = size;
image.texture_descriptor.format = format;
image
}
pub fn new_fill(
@ -72,11 +74,9 @@ impl Texture {
pixel: &[u8],
format: TextureFormat,
) -> Self {
let mut value = Texture {
format,
dimension,
..Default::default()
};
let mut value = Image::default();
value.texture_descriptor.format = format;
value.texture_descriptor.dimension = dimension;
value.resize(size);
debug_assert_eq!(
@ -96,26 +96,28 @@ impl Texture {
}
pub fn aspect_2d(&self) -> f32 {
self.size.height as f32 / self.size.width as f32
self.texture_descriptor.size.height as f32 / self.texture_descriptor.size.width as f32
}
pub fn resize(&mut self, size: Extent3d) {
self.size = size;
self.data
.resize(size.volume() * self.format.pixel_size(), 0);
self.texture_descriptor.size = size;
self.data.resize(
size.volume() * self.texture_descriptor.format.pixel_size(),
0,
);
}
/// Changes the `size`, asserting that the total number of data elements (pixels) remains the
/// same.
pub fn reinterpret_size(&mut self, new_size: Extent3d) {
assert!(
new_size.volume() == self.size.volume(),
new_size.volume() == self.texture_descriptor.size.volume(),
"Incompatible sizes: old = {:?} new = {:?}",
self.size,
self.texture_descriptor.size,
new_size
);
self.size = new_size;
self.texture_descriptor.size = new_size;
}
/// Takes a 2D texture containing vertically stacked images of the same size, and reinterprets
@ -123,13 +125,13 @@ impl Texture {
/// array. This is primarily for use with the `texture2DArray` shader uniform type.
pub fn reinterpret_stacked_2d_as_array(&mut self, layers: u32) {
// Must be a stacked image, and the height must be divisible by layers.
assert!(self.dimension == TextureDimension::D2);
assert!(self.size.depth_or_array_layers == 1);
assert_eq!(self.size.height % layers, 0);
assert!(self.texture_descriptor.dimension == TextureDimension::D2);
assert!(self.texture_descriptor.size.depth_or_array_layers == 1);
assert_eq!(self.texture_descriptor.size.height % layers, 0);
self.reinterpret_size(Extent3d {
width: self.size.width,
height: self.size.height / layers,
width: self.texture_descriptor.size.width,
height: self.texture_descriptor.size.height / layers,
depth_or_array_layers: layers,
});
}
@ -160,7 +162,7 @@ impl Texture {
/// Load a bytes buffer in a [`Texture`], according to type `image_type`, using the `image`
/// crate`
pub fn from_buffer(buffer: &[u8], image_type: ImageType) -> Result<Texture, TextureError> {
pub fn from_buffer(buffer: &[u8], image_type: ImageType) -> Result<Image, TextureError> {
let format = match image_type {
ImageType::MimeType(mime_type) => match mime_type {
"image/png" => Ok(image::ImageFormat::Png),
@ -205,3 +207,132 @@ pub enum ImageType<'a> {
/// Extension of an image file, for example `"png"`
Extension(&'a str),
}
pub trait Volume {
fn volume(&self) -> usize;
}
impl Volume for Extent3d {
fn volume(&self) -> usize {
(self.width * self.height * self.depth_or_array_layers) as usize
}
}
pub struct PixelInfo {
pub type_size: usize,
pub num_components: usize,
}
pub trait TextureFormatPixelInfo {
fn pixel_info(&self) -> PixelInfo;
fn pixel_size(&self) -> usize {
let info = self.pixel_info();
info.type_size * info.num_components
}
}
impl TextureFormatPixelInfo for TextureFormat {
fn pixel_info(&self) -> PixelInfo {
let type_size = match self {
// 8bit
TextureFormat::R8Unorm
| TextureFormat::R8Snorm
| TextureFormat::R8Uint
| TextureFormat::R8Sint
| TextureFormat::Rg8Unorm
| TextureFormat::Rg8Snorm
| TextureFormat::Rg8Uint
| TextureFormat::Rg8Sint
| TextureFormat::Rgba8Unorm
| TextureFormat::Rgba8UnormSrgb
| TextureFormat::Rgba8Snorm
| TextureFormat::Rgba8Uint
| TextureFormat::Rgba8Sint
| TextureFormat::Bgra8Unorm
| TextureFormat::Bgra8UnormSrgb => 1,
// 16bit
TextureFormat::R16Uint
| TextureFormat::R16Sint
| TextureFormat::R16Float
| TextureFormat::Rg16Uint
| TextureFormat::Rg16Sint
| TextureFormat::Rg16Float
| TextureFormat::Rgba16Uint
| TextureFormat::Rgba16Sint
| TextureFormat::Rgba16Float => 2,
// 32bit
TextureFormat::R32Uint
| TextureFormat::R32Sint
| TextureFormat::R32Float
| TextureFormat::Rg32Uint
| TextureFormat::Rg32Sint
| TextureFormat::Rg32Float
| TextureFormat::Rgba32Uint
| TextureFormat::Rgba32Sint
| TextureFormat::Rgba32Float
| TextureFormat::Depth32Float => 4,
// special cases
TextureFormat::Rgb10a2Unorm => 4,
TextureFormat::Rg11b10Float => 4,
TextureFormat::Depth24Plus => 3, // FIXME is this correct?
TextureFormat::Depth24PlusStencil8 => 4,
// TODO: this is not good! this is a temporary step while porting bevy_render to direct wgpu usage
_ => panic!("cannot get pixel info for type"),
};
let components = match self {
TextureFormat::R8Unorm
| TextureFormat::R8Snorm
| TextureFormat::R8Uint
| TextureFormat::R8Sint
| TextureFormat::R16Uint
| TextureFormat::R16Sint
| TextureFormat::R16Float
| TextureFormat::R32Uint
| TextureFormat::R32Sint
| TextureFormat::R32Float => 1,
TextureFormat::Rg8Unorm
| TextureFormat::Rg8Snorm
| TextureFormat::Rg8Uint
| TextureFormat::Rg8Sint
| TextureFormat::Rg16Uint
| TextureFormat::Rg16Sint
| TextureFormat::Rg16Float
| TextureFormat::Rg32Uint
| TextureFormat::Rg32Sint
| TextureFormat::Rg32Float => 2,
TextureFormat::Rgba8Unorm
| TextureFormat::Rgba8UnormSrgb
| TextureFormat::Rgba8Snorm
| TextureFormat::Rgba8Uint
| TextureFormat::Rgba8Sint
| TextureFormat::Bgra8Unorm
| TextureFormat::Bgra8UnormSrgb
| TextureFormat::Rgba16Uint
| TextureFormat::Rgba16Sint
| TextureFormat::Rgba16Float
| TextureFormat::Rgba32Uint
| TextureFormat::Rgba32Sint
| TextureFormat::Rgba32Float => 4,
// special cases
TextureFormat::Rgb10a2Unorm
| TextureFormat::Rg11b10Float
| TextureFormat::Depth32Float
| TextureFormat::Depth24Plus
| TextureFormat::Depth24PlusStencil8 => 1,
// TODO: this is not good! this is a temporary step while porting bevy_render to direct wgpu usage
_ => panic!("cannot get pixel info for type"),
};
PixelInfo {
type_size,
num_components: components,
}
}
}

View file

@ -1,33 +1,33 @@
use crate::{
render_resource::{TextureId, TextureViewId},
renderer::RenderResources,
texture::{TextureDescriptor, TextureViewDescriptor},
render_resource::{Texture, TextureView, TextureViewId},
renderer::RenderDevice,
};
use bevy_ecs::prelude::{Res, ResMut};
use bevy_utils::HashMap;
use wgpu::{TextureDescriptor, TextureViewDescriptor};
struct CachedTextureMeta {
texture: TextureId,
default_view: TextureViewId,
texture: Texture,
default_view: TextureView,
taken: bool,
frames_since_last_use: usize,
}
pub struct CachedTexture {
pub texture: TextureId,
pub default_view: TextureViewId,
pub texture: Texture,
pub default_view: TextureView,
}
#[derive(Default)]
pub struct TextureCache {
textures: HashMap<TextureDescriptor, Vec<CachedTextureMeta>>,
textures: HashMap<wgpu::TextureDescriptor<'static>, Vec<CachedTextureMeta>>,
}
impl TextureCache {
pub fn get(
&mut self,
render_resources: &RenderResources,
descriptor: TextureDescriptor,
render_device: &RenderDevice,
descriptor: TextureDescriptor<'static>,
) -> CachedTexture {
match self.textures.entry(descriptor) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
@ -36,66 +36,57 @@ impl TextureCache {
texture.frames_since_last_use = 0;
texture.taken = true;
return CachedTexture {
texture: texture.texture,
default_view: texture.default_view,
texture: texture.texture.clone(),
default_view: texture.default_view.clone(),
};
}
}
let texture_id = render_resources.create_texture(entry.key().clone());
let view_id = render_resources
.create_texture_view(texture_id, TextureViewDescriptor::default());
let texture = render_device.create_texture(&entry.key().clone());
let default_view = texture.create_view(&TextureViewDescriptor::default());
entry.get_mut().push(CachedTextureMeta {
texture: texture_id,
default_view: view_id,
texture: texture.clone(),
default_view: default_view.clone(),
frames_since_last_use: 0,
taken: true,
});
CachedTexture {
texture: texture_id,
default_view: view_id,
texture,
default_view,
}
}
std::collections::hash_map::Entry::Vacant(entry) => {
let texture_id = render_resources.create_texture(entry.key().clone());
let view_id = render_resources
.create_texture_view(texture_id, TextureViewDescriptor::default());
let texture = render_device.create_texture(entry.key());
let default_view = texture.create_view(&TextureViewDescriptor::default());
entry.insert(vec![CachedTextureMeta {
texture: texture_id,
default_view: view_id,
texture: texture.clone(),
default_view: default_view.clone(),
taken: true,
frames_since_last_use: 0,
}]);
CachedTexture {
texture: texture_id,
default_view: view_id,
texture,
default_view,
}
}
}
}
pub fn update(&mut self, render_resources: &RenderResources) {
pub fn update(&mut self, device: &RenderDevice) {
for textures in self.textures.values_mut() {
for texture in textures.iter_mut() {
texture.frames_since_last_use += 1;
texture.taken = false;
}
textures.retain(|texture| {
let should_keep = texture.frames_since_last_use < 3;
if !should_keep {
render_resources.remove_texture_view(texture.default_view);
render_resources.remove_texture(texture.texture);
}
should_keep
});
textures.retain(|texture| texture.frames_since_last_use < 3);
}
}
}
pub fn update_texture_cache_system(
mut texture_cache: ResMut<TextureCache>,
render_resources: Res<RenderResources>,
render_device: Res<RenderDevice>,
) {
texture_cache.update(&render_resources);
texture_cache.update(&render_device);
}

View file

@ -1,112 +0,0 @@
use std::num::NonZeroU32;
use crate::texture::TextureViewDimension;
use super::{Extent3d, Texture, TextureDimension, TextureFormat, TextureUsage};
/// Describes a texture
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct TextureDescriptor {
pub size: Extent3d,
pub mip_level_count: u32,
pub sample_count: u32,
pub dimension: TextureDimension,
pub format: TextureFormat,
pub usage: TextureUsage,
}
impl From<&Texture> for TextureDescriptor {
fn from(texture: &Texture) -> Self {
TextureDescriptor {
size: texture.size,
mip_level_count: 1,
sample_count: 1,
dimension: texture.dimension,
format: texture.format,
usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST,
}
}
}
impl Default for TextureDescriptor {
fn default() -> Self {
TextureDescriptor {
size: Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8UnormSrgb,
usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST,
}
}
}
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum StorageTextureAccess {
/// The texture can only be read in the shader and it must be annotated with `readonly`.
///
/// Example GLSL syntax:
/// ```cpp,ignore
/// layout(set=0, binding=0, r32f) readonly uniform image2D myStorageImage;
/// ```
ReadOnly,
/// The texture can only be written in the shader and it must be annotated with `writeonly`.
///
/// Example GLSL syntax:
/// ```cpp,ignore
/// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage;
/// ```
WriteOnly,
/// The texture can be both read and written in the shader.
/// `wgpu::Features::STORAGE_TEXTURE_ACCESS_READ_WRITE` must be enabled to use this access
/// mode.
///
/// Example GLSL syntax:
/// ```cpp,ignore
/// layout(set=0, binding=0, r32f) uniform image2D myStorageImage;
/// ```
ReadWrite,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TextureAspect {
/// Depth, Stencil, and Color.
All,
/// Stencil.
StencilOnly,
/// Depth.
DepthOnly,
}
impl Default for TextureAspect {
fn default() -> Self {
Self::All
}
}
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct TextureViewDescriptor {
/// Format of the texture view. At this time, it must be the same as the underlying format of the texture.
pub format: Option<TextureFormat>,
/// The dimension of the texture view. For 1D textures, this must be `1D`. For 2D textures it must be one of
/// `D2`, `D2Array`, `Cube`, and `CubeArray`. For 3D textures it must be `3D`
pub dimension: Option<TextureViewDimension>,
/// Aspect of the texture. Color textures must be [`TextureAspect::All`].
pub aspect: TextureAspect,
/// Base mip level.
pub base_mip_level: u32,
/// Mip level count.
/// If `Some(count)`, `base_mip_level + count` must be less or equal to underlying texture mip count.
/// If `None`, considered to include the rest of the mipmap levels, but at least 1 in total.
pub level_count: Option<NonZeroU32>,
/// Base array layer.
pub base_array_layer: u32,
/// Layer count.
/// If `Some(count)`, `base_array_layer + count` must be less or equal to the underlying array count.
/// If `None`, considered to include the rest of the array layers, but at least 1 in total.
pub array_layer_count: Option<NonZeroU32>,
}

View file

@ -1,293 +0,0 @@
// NOTE: These are currently just copies of the wgpu types, but they might change in the future
use bevy_math::Vec3;
/// Dimensions of a particular texture view.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum TextureViewDimension {
D1,
D2,
D2Array,
Cube,
CubeArray,
D3,
}
/// Dimensionality of a texture.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum TextureDimension {
D1,
D2,
D3,
}
// TODO: use math type here
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Extent3d {
pub width: u32,
pub height: u32,
pub depth_or_array_layers: u32,
}
impl Extent3d {
pub fn new(width: u32, height: u32, depth_or_array_layers: u32) -> Self {
Self {
width,
height,
depth_or_array_layers,
}
}
pub fn volume(&self) -> usize {
(self.width * self.height * self.depth_or_array_layers) as usize
}
pub fn as_vec3(&self) -> Vec3 {
Vec3::new(
self.width as f32,
self.height as f32,
self.depth_or_array_layers as f32,
)
}
}
/// Type of data shaders will read from a texture.
#[derive(Copy, Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum TextureSampleType {
/// Sampling returns floats.
///
/// If `filterable` is false, the texture can't be sampled with
/// a filtering sampler.
///
/// Example GLSL syntax:
/// ```cpp,ignore
/// layout(binding = 0)
/// uniform texture2D t;
/// ```
Float { filterable: bool },
/// Sampling does the depth reference comparison.
///
/// Example GLSL syntax:
/// ```cpp,ignore
/// layout(binding = 0)
/// uniform texture2DShadow t;
/// ```
Depth,
/// Sampling returns signed integers.
///
/// Example GLSL syntax:
/// ```cpp,ignore
/// layout(binding = 0)
/// uniform itexture2D t;
/// ```
Sint,
/// Sampling returns unsigned integers.
///
/// Example GLSL syntax:
/// ```cpp,ignore
/// layout(binding = 0)
/// uniform utexture2D t;
/// ```
Uint,
}
pub struct PixelInfo {
pub type_size: usize,
pub num_components: usize,
}
/// Underlying texture data format.
///
/// If there is a conversion in the format (such as srgb -> linear), The conversion listed is for
/// loading from texture in a shader. When writing to the texture, the opposite conversion takes
/// place.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum TextureFormat {
// Normal 8 bit formats
R8Unorm = 0,
R8Snorm = 1,
R8Uint = 2,
R8Sint = 3,
// Normal 16 bit formats
R16Uint = 4,
R16Sint = 5,
R16Float = 6,
Rg8Unorm = 7,
Rg8Snorm = 8,
Rg8Uint = 9,
Rg8Sint = 10,
// Normal 32 bit formats
R32Uint = 11,
R32Sint = 12,
R32Float = 13,
Rg16Uint = 14,
Rg16Sint = 15,
Rg16Float = 16,
Rgba8Unorm = 17,
Rgba8UnormSrgb = 18,
Rgba8Snorm = 19,
Rgba8Uint = 20,
Rgba8Sint = 21,
Bgra8Unorm = 22,
Bgra8UnormSrgb = 23,
// Packed 32 bit formats
Rgb10a2Unorm = 24,
Rg11b10Float = 25,
// Normal 64 bit formats
Rg32Uint = 26,
Rg32Sint = 27,
Rg32Float = 28,
Rgba16Uint = 29,
Rgba16Sint = 30,
Rgba16Float = 31,
// Normal 128 bit formats
Rgba32Uint = 32,
Rgba32Sint = 33,
Rgba32Float = 34,
// Depth and stencil formats
Depth32Float = 35,
Depth24Plus = 36,
Depth24PlusStencil8 = 37,
}
impl TextureFormat {
pub fn pixel_info(&self) -> PixelInfo {
let type_size = match self {
// 8bit
TextureFormat::R8Unorm
| TextureFormat::R8Snorm
| TextureFormat::R8Uint
| TextureFormat::R8Sint
| TextureFormat::Rg8Unorm
| TextureFormat::Rg8Snorm
| TextureFormat::Rg8Uint
| TextureFormat::Rg8Sint
| TextureFormat::Rgba8Unorm
| TextureFormat::Rgba8UnormSrgb
| TextureFormat::Rgba8Snorm
| TextureFormat::Rgba8Uint
| TextureFormat::Rgba8Sint
| TextureFormat::Bgra8Unorm
| TextureFormat::Bgra8UnormSrgb => 1,
// 16bit
TextureFormat::R16Uint
| TextureFormat::R16Sint
| TextureFormat::R16Float
| TextureFormat::Rg16Uint
| TextureFormat::Rg16Sint
| TextureFormat::Rg16Float
| TextureFormat::Rgba16Uint
| TextureFormat::Rgba16Sint
| TextureFormat::Rgba16Float => 2,
// 32bit
TextureFormat::R32Uint
| TextureFormat::R32Sint
| TextureFormat::R32Float
| TextureFormat::Rg32Uint
| TextureFormat::Rg32Sint
| TextureFormat::Rg32Float
| TextureFormat::Rgba32Uint
| TextureFormat::Rgba32Sint
| TextureFormat::Rgba32Float
| TextureFormat::Depth32Float => 4,
// special cases
TextureFormat::Rgb10a2Unorm => 4,
TextureFormat::Rg11b10Float => 4,
TextureFormat::Depth24Plus => 3, // FIXME is this correct?
TextureFormat::Depth24PlusStencil8 => 4,
};
let components = match self {
TextureFormat::R8Unorm
| TextureFormat::R8Snorm
| TextureFormat::R8Uint
| TextureFormat::R8Sint
| TextureFormat::R16Uint
| TextureFormat::R16Sint
| TextureFormat::R16Float
| TextureFormat::R32Uint
| TextureFormat::R32Sint
| TextureFormat::R32Float => 1,
TextureFormat::Rg8Unorm
| TextureFormat::Rg8Snorm
| TextureFormat::Rg8Uint
| TextureFormat::Rg8Sint
| TextureFormat::Rg16Uint
| TextureFormat::Rg16Sint
| TextureFormat::Rg16Float
| TextureFormat::Rg32Uint
| TextureFormat::Rg32Sint
| TextureFormat::Rg32Float => 2,
TextureFormat::Rgba8Unorm
| TextureFormat::Rgba8UnormSrgb
| TextureFormat::Rgba8Snorm
| TextureFormat::Rgba8Uint
| TextureFormat::Rgba8Sint
| TextureFormat::Bgra8Unorm
| TextureFormat::Bgra8UnormSrgb
| TextureFormat::Rgba16Uint
| TextureFormat::Rgba16Sint
| TextureFormat::Rgba16Float
| TextureFormat::Rgba32Uint
| TextureFormat::Rgba32Sint
| TextureFormat::Rgba32Float => 4,
// special cases
TextureFormat::Rgb10a2Unorm
| TextureFormat::Rg11b10Float
| TextureFormat::Depth32Float
| TextureFormat::Depth24Plus
| TextureFormat::Depth24PlusStencil8 => 1,
};
PixelInfo {
type_size,
num_components: components,
}
}
pub fn pixel_size(&self) -> usize {
let info = self.pixel_info();
info.type_size * info.num_components
}
}
impl Default for TextureFormat {
fn default() -> Self {
if cfg!(target_os = "android") {
// Bgra8UnormSrgb texture missing on some Android devices
TextureFormat::Rgba8UnormSrgb
} else {
TextureFormat::Bgra8UnormSrgb
}
}
}
bitflags::bitflags! {
#[repr(transparent)]
pub struct TextureUsage: u32 {
/// Allows a texture to be the source in a [`CommandEncoder::copy_texture_to_buffer`] or
/// [`CommandEncoder::copy_texture_to_texture`] operation.
const COPY_SRC = 1;
/// Allows a texture to be the destination in a [`CommandEncoder::copy_texture_to_buffer`],
/// [`CommandEncoder::copy_texture_to_texture`], or [`Queue::write_texture`] operation.
const COPY_DST = 2;
/// Allows a texture to be a [`BindingType::Texture`] in a bind group.
const SAMPLED = 4;
/// Allows a texture to be a [`BindingType::StorageTexture`] in a bind group.
const STORAGE = 8;
/// Allows a texture to be an output attachment of a renderpass.
const RENDER_ATTACHMENT = 16;
}
}

View file

@ -6,7 +6,7 @@ pub use window::*;
use crate::{
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
render_resource::DynamicUniformVec,
renderer::{RenderContext, RenderResources},
renderer::{RenderContext, RenderDevice},
RenderStage,
};
use bevy_app::{App, Plugin};
@ -40,23 +40,23 @@ pub struct ExtractedView {
}
#[derive(Clone, AsStd140)]
pub struct ViewUniformData {
pub struct ViewUniform {
view_proj: Mat4,
world_position: Vec3,
}
#[derive(Default)]
pub struct ViewMeta {
pub uniforms: DynamicUniformVec<ViewUniformData>,
pub uniforms: DynamicUniformVec<ViewUniform>,
}
pub struct ViewUniform {
pub view_uniform_offset: u32,
pub struct ViewUniformOffset {
pub offset: u32,
}
fn prepare_views(
mut commands: Commands,
render_resources: Res<RenderResources>,
render_resources: Res<RenderDevice>,
mut view_meta: ResMut<ViewMeta>,
mut extracted_views: Query<(Entity, &ExtractedView)>,
) {
@ -64,8 +64,8 @@ fn prepare_views(
.uniforms
.reserve_and_clear(extracted_views.iter_mut().len(), &render_resources);
for (entity, camera) in extracted_views.iter() {
let view_uniforms = ViewUniform {
view_uniform_offset: view_meta.uniforms.push(ViewUniformData {
let view_uniforms = ViewUniformOffset {
offset: view_meta.uniforms.push(ViewUniform {
view_proj: camera.projection * camera.transform.compute_matrix().inverse(),
world_position: camera.transform.translation,
}),
@ -85,11 +85,13 @@ impl Node for ViewNode {
fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut dyn RenderContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_meta = world.get_resource::<ViewMeta>().unwrap();
view_meta.uniforms.write_to_uniform_buffer(render_context);
view_meta
.uniforms
.write_to_uniform_buffer(&mut render_context.command_encoder);
Ok(())
}
}

View file

@ -1,14 +1,16 @@
use std::ops::{Deref, DerefMut};
use crate::{
render_resource::{SwapChainDescriptor, TextureViewId},
renderer::RenderResources,
render_resource::TextureView,
renderer::{RenderDevice, RenderInstance},
texture::BevyDefault,
RenderStage,
};
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use bevy_utils::HashMap;
use bevy_window::{RawWindowHandleWrapper, WindowId, Windows};
use wgpu::{SwapChainFrame, TextureFormat};
pub struct WindowRenderPlugin;
@ -16,6 +18,7 @@ impl Plugin for WindowRenderPlugin {
fn build(&self, app: &mut App) {
let render_app = app.sub_app_mut(0);
render_app
.init_resource::<WindowSurfaces>()
.add_system_to_stage(RenderStage::Extract, extract_windows.system())
.add_system_to_stage(RenderStage::Prepare, prepare_windows.system());
}
@ -27,7 +30,7 @@ pub struct ExtractedWindow {
pub physical_width: u32,
pub physical_height: u32,
pub vsync: bool,
pub swap_chain_texture: Option<TextureViewId>,
pub swap_chain_frame: Option<TextureView>,
}
#[derive(Default)]
@ -60,7 +63,7 @@ fn extract_windows(mut commands: Commands, windows: Res<Windows>) {
physical_width: window.physical_width(),
physical_height: window.physical_height(),
vsync: window.vsync(),
swap_chain_texture: None,
swap_chain_frame: None,
},
);
}
@ -68,20 +71,63 @@ fn extract_windows(mut commands: Commands, windows: Res<Windows>) {
commands.insert_resource(extracted_windows);
}
#[derive(Default)]
pub struct WindowSurfaces {
surfaces: HashMap<WindowId, wgpu::Surface>,
swap_chains: HashMap<WindowId, wgpu::SwapChain>,
}
pub struct WindowSwapChain {
value: TextureView,
}
pub fn prepare_windows(
mut windows: ResMut<ExtractedWindows>,
render_resources: Res<RenderResources>,
mut window_surfaces: ResMut<WindowSurfaces>,
render_device: Res<RenderDevice>,
render_instance: Res<RenderInstance>,
) {
let window_surfaces = window_surfaces.deref_mut();
for window in windows.windows.values_mut() {
let swap_chain_descriptor = SwapChainDescriptor {
window_id: window.id,
format: crate::texture::TextureFormat::Bgra8UnormSrgb,
let surface = window_surfaces
.surfaces
.entry(window.id)
.or_insert_with(|| unsafe {
render_instance.create_surface(&window.handle.get_handle())
});
let swap_chain_descriptor = wgpu::SwapChainDescriptor {
format: TextureFormat::bevy_default(),
width: window.physical_width,
height: window.physical_height,
vsync: window.vsync,
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
present_mode: if window.vsync {
wgpu::PresentMode::Fifo
} else {
wgpu::PresentMode::Immediate
},
};
let swap_chain_texture = render_resources.next_swap_chain_texture(&swap_chain_descriptor);
window.swap_chain_texture = Some(swap_chain_texture);
let swap_chain = window_surfaces
.swap_chains
.entry(window.id)
.or_insert_with(|| render_device.create_swap_chain(surface, &swap_chain_descriptor));
let frame = if let Ok(swap_chain_frame) = swap_chain.get_current_frame() {
swap_chain_frame
} else {
let swap_chain = window_surfaces
.swap_chains
.entry(window.id)
.or_insert_with(|| {
render_device.create_swap_chain(surface, &swap_chain_descriptor)
});
swap_chain
.get_current_frame()
.expect("Failed to acquire next swap chain texture!")
};
window.swap_chain_frame = Some(TextureView::from(frame));
}
}

View file

@ -1,7 +1,7 @@
use crate::Sprite;
use bevy_asset::Handle;
use bevy_ecs::bundle::Bundle;
use bevy_render2::texture::Texture;
use bevy_render2::texture::Image;
use bevy_transform::components::{GlobalTransform, Transform};
#[derive(Bundle, Clone)]
@ -9,7 +9,7 @@ pub struct PipelinedSpriteBundle {
pub sprite: Sprite,
pub transform: Transform,
pub global_transform: GlobalTransform,
pub texture: Handle<Texture>,
pub texture: Handle<Image>,
}
impl Default for PipelinedSpriteBundle {

View file

@ -5,86 +5,143 @@ use bevy_math::{Mat4, Vec2, Vec3, Vec4Swizzles};
use bevy_render2::{
core_pipeline::Transparent2dPhase,
mesh::{shape::Quad, Indices, Mesh, VertexAttributeValues},
pipeline::*,
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
render_resource::{
BindGroupBuilder, BindGroupId, BufferUsage, BufferVec, SamplerId, TextureViewId,
},
renderer::{RenderContext, RenderResources},
shader::{Shader, ShaderStage, ShaderStages},
texture::{Texture, TextureFormat},
view::{ViewMeta, ViewUniform},
render_resource::*,
renderer::{RenderContext, RenderDevice},
shader::Shader,
texture::{BevyDefault, Image},
view::{ViewMeta, ViewUniformOffset, ViewUniform},
};
use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap;
use bytemuck::{Pod, Zeroable};
use std::borrow::Cow;
pub struct SpriteShaders {
pipeline: PipelineId,
pipeline_descriptor: RenderPipelineDescriptor,
pipeline: RenderPipeline,
view_layout: BindGroupLayout,
material_layout: BindGroupLayout,
}
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
impl FromWorld for SpriteShaders {
fn from_world(world: &mut World) -> Self {
let render_resources = world.get_resource::<RenderResources>().unwrap();
let vertex_shader = Shader::from_glsl(ShaderStage::Vertex, include_str!("sprite.vert"))
let render_device = world.get_resource::<RenderDevice>().unwrap();
let vertex_shader = Shader::from_glsl(ShaderStage::VERTEX, include_str!("sprite.vert"))
.get_spirv_shader(None)
.unwrap();
let fragment_shader = Shader::from_glsl(ShaderStage::Fragment, include_str!("sprite.frag"))
let fragment_shader = Shader::from_glsl(ShaderStage::FRAGMENT, include_str!("sprite.frag"))
.get_spirv_shader(None)
.unwrap();
let vertex_spirv = vertex_shader.get_spirv(None).unwrap();
let fragment_spirv = fragment_shader.get_spirv(None).unwrap();
let vertex_layout = vertex_shader.reflect_layout(true).unwrap();
let fragment_layout = fragment_shader.reflect_layout(true).unwrap();
let vertex = render_device.create_shader_module(&ShaderModuleDescriptor {
flags: ShaderFlags::default(),
label: None,
source: ShaderSource::SpirV(Cow::Borrowed(&vertex_spirv)),
});
let fragment = render_device.create_shader_module(&ShaderModuleDescriptor {
flags: ShaderFlags::default(),
label: None,
source: ShaderSource::SpirV(Cow::Borrowed(&fragment_spirv)),
});
let mut pipeline_layout =
PipelineLayout::from_shader_layouts(&mut [vertex_layout, fragment_layout]);
let view_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::VERTEX | ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
// TODO: verify this is correct
min_binding_size: BufferSize::new(
std::mem::size_of::<ViewUniform>() as u64
),
},
count: None,
}],
label: None,
});
let vertex = render_resources.create_shader_module(&vertex_shader);
let fragment = render_resources.create_shader_module(&fragment_shader);
pipeline_layout.vertex_buffer_descriptors = vec![VertexBufferLayout {
stride: 20,
name: "Vertex".into(),
step_mode: InputStepMode::Vertex,
attributes: vec![
VertexAttribute {
name: "Vertex_Position".into(),
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 0,
let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
},
count: None,
},
VertexAttribute {
name: "Vertex_Uv".into(),
format: VertexFormat::Float32x2,
offset: 12,
shader_location: 1,
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStage::FRAGMENT,
ty: BindingType::Sampler {
comparison: false,
filtering: true,
},
count: None,
},
],
}];
label: None,
});
pipeline_layout.bind_groups[0].bindings[0].set_dynamic(true);
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
push_constant_ranges: &[],
bind_group_layouts: &[&view_layout, &material_layout],
});
let pipeline_descriptor = RenderPipelineDescriptor {
let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
depth_stencil: None,
color_target_states: vec![ColorTargetState {
format: TextureFormat::default(),
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrite::ALL,
}],
vertex: VertexState {
buffers: &[VertexBufferLayout {
array_stride: 20,
step_mode: InputStepMode::Vertex,
attributes: &[
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 0,
},
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 12,
shader_location: 1,
},
],
}],
module: &vertex,
entry_point: "main",
},
fragment: Some(FragmentState {
module: &fragment,
entry_point: "main",
targets: &[ColorTargetState {
format: TextureFormat::bevy_default(),
blend: Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
}),
write_mask: ColorWrite::ALL,
}],
}),
layout: Some(&pipeline_layout),
multisample: MultisampleState::default(),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
@ -94,20 +151,12 @@ impl FromWorld for SpriteShaders {
clamp_depth: false,
conservative: false,
},
..RenderPipelineDescriptor::new(
ShaderStages {
vertex,
fragment: Some(fragment),
},
pipeline_layout,
)
};
let pipeline = render_resources.create_render_pipeline(&pipeline_descriptor);
});
SpriteShaders {
pipeline,
pipeline_descriptor,
view_layout,
material_layout,
}
}
}
@ -115,8 +164,9 @@ impl FromWorld for SpriteShaders {
struct ExtractedSprite {
transform: Mat4,
size: Vec2,
texture_view: TextureViewId,
sampler: SamplerId,
// TODO: use asset handle here instead of owned renderer handles (lots of arc cloning)
texture_view: TextureView,
sampler: Sampler,
}
pub struct ExtractedSprites {
@ -125,8 +175,8 @@ pub struct ExtractedSprites {
pub fn extract_sprites(
mut commands: Commands,
textures: Res<Assets<Texture>>,
query: Query<(&Sprite, &GlobalTransform, &Handle<Texture>)>,
textures: Res<Assets<Image>>,
query: Query<(&Sprite, &GlobalTransform, &Handle<Image>)>,
) {
let mut extracted_sprites = Vec::new();
for (sprite, transform, handle) in query.iter() {
@ -135,8 +185,8 @@ pub fn extract_sprites(
extracted_sprites.push(ExtractedSprite {
transform: transform.compute_matrix(),
size: sprite.size,
texture_view: gpu_data.texture_view,
sampler: gpu_data.sampler,
texture_view: gpu_data.texture_view.clone(),
sampler: gpu_data.sampler.clone(),
})
}
}
@ -158,7 +208,10 @@ pub struct SpriteMeta {
vertices: BufferVec<SpriteVertex>,
indices: BufferVec<u32>,
quad: Mesh,
texture_bind_groups: Vec<BindGroupId>,
view_bind_group: Option<BindGroup>,
// TODO: these should be garbage collected if unused across X frames
texture_bind_groups: Vec<BindGroup>,
texture_bind_group_indices: HashMap<TextureViewId, usize>,
}
impl Default for SpriteMeta {
@ -167,6 +220,8 @@ impl Default for SpriteMeta {
vertices: BufferVec::new(BufferUsage::VERTEX),
indices: BufferVec::new(BufferUsage::INDEX),
texture_bind_groups: Vec::new(),
texture_bind_group_indices: HashMap::default(),
view_bind_group: None,
quad: Quad {
size: Vec2::new(1.0, 1.0),
..Default::default()
@ -177,7 +232,7 @@ impl Default for SpriteMeta {
}
pub fn prepare_sprites(
render_resources: Res<RenderResources>,
render_device: Res<RenderDevice>,
mut sprite_meta: ResMut<SpriteMeta>,
extracted_sprites: Res<ExtractedSprites>,
) {
@ -217,11 +272,11 @@ pub fn prepare_sprites(
sprite_meta.vertices.reserve_and_clear(
extracted_sprites.sprites.len() * quad_vertex_positions.len(),
&render_resources,
&render_device,
);
sprite_meta.indices.reserve_and_clear(
extracted_sprites.sprites.len() * quad_indices.len(),
&render_resources,
&render_device,
);
for (i, extracted_sprite) in extracted_sprites.sprites.iter().enumerate() {
@ -243,60 +298,58 @@ pub fn prepare_sprites(
}
}
sprite_meta
.vertices
.write_to_staging_buffer(&render_resources);
sprite_meta
.indices
.write_to_staging_buffer(&render_resources);
}
// TODO: This is temporary. Once we expose BindGroupLayouts directly, we can create view bind groups without specific shader context
struct SpriteViewMeta {
bind_group: BindGroupId,
sprite_meta.vertices.write_to_staging_buffer(&render_device);
sprite_meta.indices.write_to_staging_buffer(&render_device);
}
pub fn queue_sprites(
mut commands: Commands,
draw_functions: Res<DrawFunctions>,
render_resources: Res<RenderResources>,
render_device: Res<RenderDevice>,
mut sprite_meta: ResMut<SpriteMeta>,
view_meta: Res<ViewMeta>,
sprite_shaders: Res<SpriteShaders>,
extracted_sprites: Res<ExtractedSprites>,
mut views: Query<(Entity, &mut RenderPhase<Transparent2dPhase>)>,
mut views: Query<&mut RenderPhase<Transparent2dPhase>>,
) {
for (view_entity, mut transparent_phase) in views.iter_mut() {
let layout = &sprite_shaders.pipeline_descriptor.layout;
let camera_bind_group = BindGroupBuilder::default()
.add_binding(0, view_meta.uniforms.binding())
.finish();
// TODO: this will only create the bind group if it isn't already created. this is a bit nasty
render_resources.create_bind_group(layout.bind_groups[0].id, &camera_bind_group);
commands.entity(view_entity).insert(SpriteViewMeta {
bind_group: camera_bind_group.id,
});
// TODO: define this without needing to check every frame
sprite_meta.view_bind_group.get_or_insert_with(|| {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: view_meta.uniforms.binding(),
}],
label: None,
layout: &sprite_shaders.view_layout,
})
});
let sprite_meta = &mut *sprite_meta;
for mut transparent_phase in views.iter_mut() {
// TODO: free old bind groups? clear_unused_bind_groups() currently does this for us? Moving to RAII would also do this for us?
sprite_meta.texture_bind_groups.clear();
let mut texture_bind_group_indices = HashMap::default();
let draw_sprite_function = draw_functions.read().get_id::<DrawSprite>().unwrap();
let texture_bind_groups = &mut sprite_meta.texture_bind_groups;
// let material_layout = ;
for (i, sprite) in extracted_sprites.sprites.iter().enumerate() {
let bind_group_index = *texture_bind_group_indices
.entry(sprite.texture_view)
let bind_group_index = *sprite_meta
.texture_bind_group_indices
.entry(sprite.texture_view.id())
.or_insert_with(|| {
let index = sprite_meta.texture_bind_groups.len();
let bind_group = BindGroupBuilder::default()
.add_binding(0, sprite.texture_view)
// NOTE: this currently reuses the same sampler across all sprites using the same texture
.add_binding(1, sprite.sampler)
.finish();
render_resources.create_bind_group(layout.bind_groups[1].id, &bind_group);
sprite_meta.texture_bind_groups.push(bind_group.id);
let index = texture_bind_groups.len();
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(&sprite.texture_view),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(&sprite.sampler),
},
],
label: None,
layout: &sprite_shaders.material_layout,
});
texture_bind_groups.push(bind_group);
index
});
transparent_phase.add(Drawable {
@ -315,23 +368,27 @@ impl Node for SpriteNode {
fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut dyn RenderContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let sprite_buffers = world.get_resource::<SpriteMeta>().unwrap();
sprite_buffers.vertices.write_to_buffer(render_context);
sprite_buffers.indices.write_to_buffer(render_context);
sprite_buffers
.vertices
.write_to_buffer(&mut render_context.command_encoder);
sprite_buffers
.indices
.write_to_buffer(&mut render_context.command_encoder);
Ok(())
}
}
type DrawSpriteQuery<'a> = (
Res<'a, SpriteShaders>,
Res<'a, SpriteMeta>,
Query<'a, (&'a ViewUniform, &'a SpriteViewMeta)>,
type DrawSpriteQuery<'s, 'w> = (
Res<'w, SpriteShaders>,
Res<'w, SpriteMeta>,
Query<'w, 's, &'w ViewUniformOffset>,
);
pub struct DrawSprite {
params: SystemState<DrawSpriteQuery<'static>>,
params: SystemState<DrawSpriteQuery<'static, 'static>>,
}
impl DrawSprite {
@ -343,37 +400,32 @@ impl DrawSprite {
}
impl Draw for DrawSprite {
fn draw(
fn draw<'w>(
&mut self,
world: &World,
pass: &mut TrackedRenderPass,
world: &'w World,
pass: &mut TrackedRenderPass<'w>,
view: Entity,
draw_key: usize,
sort_key: usize,
) {
const INDICES: usize = 6;
let (sprite_shaders, sprite_buffers, views) = self.params.get(world);
let layout = &sprite_shaders.pipeline_descriptor.layout;
let (view_uniforms, sprite_view_meta) = views.get(view).unwrap();
pass.set_pipeline(sprite_shaders.pipeline);
pass.set_vertex_buffer(0, sprite_buffers.vertices.buffer().unwrap(), 0);
let (sprite_shaders, sprite_meta, views) = self.params.get(world);
let (sprite_shaders, sprite_meta, views) =
(sprite_shaders.into_inner(), sprite_meta.into_inner(), views);
let view_uniform = views.get(view).unwrap();
pass.set_render_pipeline(&sprite_shaders.pipeline);
pass.set_vertex_buffer(0, sprite_meta.vertices.buffer().unwrap().slice(..));
pass.set_index_buffer(
sprite_buffers.indices.buffer().unwrap(),
sprite_meta.indices.buffer().unwrap().slice(..),
0,
IndexFormat::Uint32,
);
pass.set_bind_group(
0,
layout.bind_groups[0].id,
sprite_view_meta.bind_group,
Some(&[view_uniforms.view_uniform_offset]),
);
pass.set_bind_group(
1,
layout.bind_groups[1].id,
sprite_buffers.texture_bind_groups[sort_key],
None,
sprite_meta.view_bind_group.as_ref().unwrap(),
&[view_uniform.offset],
);
pass.set_bind_group(1, &sprite_meta.texture_bind_groups[sort_key], &[]);
pass.draw_indexed(
(draw_key * INDICES) as u32..(draw_key * INDICES + INDICES) as u32,

View file

@ -1,39 +0,0 @@
[package]
name = "bevy_wgpu2"
version = "0.5.0"
edition = "2018"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Carter Anderson <mcanders1@gmail.com>",
]
description = "A wgpu render backend for Bevy Engine"
homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT"
keywords = ["bevy"]
[features]
default = ["bevy_winit"]
trace = ["wgpu/trace"]
[dependencies]
# bevy
bevy_app = { path = "../../crates/bevy_app", version = "0.5.0" }
bevy_asset = { path = "../../crates/bevy_asset", version = "0.5.0" }
bevy_core = { path = "../../crates/bevy_core", version = "0.5.0" }
bevy_diagnostic = { path = "../../crates/bevy_diagnostic", version = "0.5.0" }
bevy_ecs = { path = "../../crates/bevy_ecs", version = "0.5.0" }
bevy_render2 = { path = "../bevy_render2", version = "0.5.0" }
bevy_window = { path = "../../crates/bevy_window", version = "0.5.0" }
bevy_winit = { path = "../../crates/bevy_winit", optional = true, version = "0.5.0" }
bevy_utils = { path = "../../crates/bevy_utils", version = "0.5.0" }
# other
wgpu = "0.8"
futures-lite = "1.4.0"
crossbeam-channel = "0.5.0"
crossbeam-utils = "0.8.1"
parking_lot = "0.11.0"
raw-window-handle = "0.3"
thiserror = "1.0"
smallvec = { version = "1.6", features = ["union", "const_generics"] }

View file

@ -1,74 +0,0 @@
use crate::{resources::WgpuResourceRefs, WgpuRenderContext};
use bevy_render2::{
pass::ComputePass,
pipeline::{BindGroupDescriptorId, ComputePipelineDescriptor, PipelineId},
render_resource::BindGroupId,
renderer::RenderContext,
};
use bevy_utils::tracing::trace;
#[derive(Debug)]
pub struct WgpuComputePass<'a> {
pub compute_pass: wgpu::ComputePass<'a>,
pub render_context: &'a WgpuRenderContext,
pub wgpu_resources: WgpuResourceRefs<'a>,
pub pipeline_descriptor: Option<&'a ComputePipelineDescriptor>,
}
impl<'a> ComputePass for WgpuComputePass<'a> {
fn get_render_context(&self) -> &dyn RenderContext {
self.render_context
}
fn set_bind_group(
&mut self,
index: u32,
bind_group_descriptor_id: BindGroupDescriptorId,
bind_group: BindGroupId,
dynamic_uniform_indices: Option<&[u32]>,
) {
if let Some(bind_group_info) = self
.wgpu_resources
.bind_groups
.get(&bind_group_descriptor_id)
{
if let Some(wgpu_bind_group) = bind_group_info.bind_groups.get(&bind_group) {
const EMPTY: &[u32] = &[];
let dynamic_uniform_indices =
if let Some(dynamic_uniform_indices) = dynamic_uniform_indices {
dynamic_uniform_indices
} else {
EMPTY
};
self.wgpu_resources
.used_bind_group_sender
.send(bind_group)
.unwrap();
trace!(
"set bind group {:?} {:?}: {:?}",
bind_group_descriptor_id,
dynamic_uniform_indices,
bind_group
);
self.compute_pass
.set_bind_group(index, wgpu_bind_group, dynamic_uniform_indices);
}
}
}
fn set_pipeline(&mut self, pipeline: PipelineId) {
let pipeline = self
.wgpu_resources
.compute_pipelines
.get(&pipeline)
.expect(
"Attempted to use a pipeline that does not exist in this `RenderPass`'s `RenderContext`.",
);
self.compute_pass.set_pipeline(pipeline);
}
fn dispatch(&mut self, x: u32, y: u32, z: u32) {
self.compute_pass.dispatch(x, y, z);
}
}

View file

@ -1,2 +0,0 @@
mod wgpu_resource_diagnostics_plugin;
pub use wgpu_resource_diagnostics_plugin::WgpuResourceDiagnosticsPlugin;

View file

@ -1,183 +0,0 @@
use crate::WgpuRenderResourceContext;
use bevy_app::prelude::*;
use bevy_diagnostic::{Diagnostic, DiagnosticId, Diagnostics};
use bevy_ecs::system::{IntoSystem, Res, ResMut};
use bevy_render2::renderer::RenderResources;
#[derive(Default)]
pub struct WgpuResourceDiagnosticsPlugin;
impl Plugin for WgpuResourceDiagnosticsPlugin {
fn build(&self, app: &mut App) {
app.add_startup_system(Self::setup_system.system())
.add_system(Self::diagnostic_system.system());
}
}
impl WgpuResourceDiagnosticsPlugin {
pub const BIND_GROUPS: DiagnosticId =
DiagnosticId::from_u128(21302464753369276741568507794995836890);
pub const BIND_GROUP_IDS: DiagnosticId =
DiagnosticId::from_u128(283571569334075937453357861280307923122);
pub const BIND_GROUP_LAYOUTS: DiagnosticId =
DiagnosticId::from_u128(96406067032931216377076410852598331304);
pub const BUFFERS: DiagnosticId =
DiagnosticId::from_u128(133146619577893994787249934474491530491);
pub const RENDER_PIPELINES: DiagnosticId =
DiagnosticId::from_u128(278527620040377353875091478462209885377);
pub const SAMPLERS: DiagnosticId =
DiagnosticId::from_u128(305855369913076220671125671543184691267);
pub const SHADER_MODULES: DiagnosticId =
DiagnosticId::from_u128(287681470908132753275843248383768232237);
pub const SWAP_CHAINS: DiagnosticId =
DiagnosticId::from_u128(199253035828743332241465305105689014605);
pub const SWAP_CHAIN_OUTPUTS: DiagnosticId =
DiagnosticId::from_u128(112048874168736161226721327099863374234);
pub const TEXTURES: DiagnosticId =
DiagnosticId::from_u128(305955424195390184883220102469231911115);
pub const TEXTURE_VIEWS: DiagnosticId =
DiagnosticId::from_u128(257307432866562594739240898780307437578);
pub const WINDOW_SURFACES: DiagnosticId =
DiagnosticId::from_u128(108237028251680341878766034324149135605);
pub fn setup_system(mut diagnostics: ResMut<Diagnostics>) {
diagnostics.add(Diagnostic::new(
Self::WINDOW_SURFACES,
"window_surfaces",
10,
));
diagnostics.add(Diagnostic::new(Self::SWAP_CHAINS, "swap_chains", 10));
diagnostics.add(Diagnostic::new(
Self::SWAP_CHAIN_OUTPUTS,
"swap_chain_outputs",
10,
));
diagnostics.add(Diagnostic::new(Self::BUFFERS, "buffers", 10));
diagnostics.add(Diagnostic::new(Self::TEXTURES, "textures", 10));
diagnostics.add(Diagnostic::new(Self::TEXTURE_VIEWS, "texture_views", 10));
diagnostics.add(Diagnostic::new(Self::SAMPLERS, "samplers", 10));
diagnostics.add(Diagnostic::new(Self::BIND_GROUP_IDS, "bind_group_ids", 10));
diagnostics.add(Diagnostic::new(Self::BIND_GROUPS, "bind_groups", 10));
diagnostics.add(Diagnostic::new(
Self::BIND_GROUP_LAYOUTS,
"bind_group_layouts",
10,
));
diagnostics.add(Diagnostic::new(Self::SHADER_MODULES, "shader_modules", 10));
diagnostics.add(Diagnostic::new(
Self::RENDER_PIPELINES,
"render_pipelines",
10,
));
}
pub fn diagnostic_system(
mut diagnostics: ResMut<Diagnostics>,
render_resources: Res<RenderResources>,
) {
let render_resource_context = render_resources
.downcast_ref::<WgpuRenderResourceContext>()
.unwrap();
diagnostics.add_measurement(
Self::WINDOW_SURFACES,
render_resource_context
.resources
.window_surfaces
.read()
.len() as f64,
);
diagnostics.add_measurement(
Self::SWAP_CHAINS,
render_resource_context
.resources
.window_swap_chains
.read()
.len() as f64,
);
diagnostics.add_measurement(
Self::SWAP_CHAIN_OUTPUTS,
render_resource_context
.resources
.swap_chain_frames
.read()
.len() as f64,
);
diagnostics.add_measurement(
Self::BUFFERS,
render_resource_context.resources.buffers.read().len() as f64,
);
diagnostics.add_measurement(
Self::TEXTURES,
render_resource_context.resources.textures.read().len() as f64,
);
diagnostics.add_measurement(
Self::TEXTURE_VIEWS,
render_resource_context.resources.texture_views.read().len() as f64,
);
diagnostics.add_measurement(
Self::SAMPLERS,
render_resource_context.resources.samplers.read().len() as f64,
);
diagnostics.add_measurement(
Self::BIND_GROUP_IDS,
render_resource_context.resources.bind_groups.read().len() as f64,
);
let mut bind_group_count = 0;
for bind_group in render_resource_context
.resources
.bind_groups
.read()
.values()
{
bind_group_count += bind_group.bind_groups.len();
}
diagnostics.add_measurement(Self::BIND_GROUPS, bind_group_count as f64);
diagnostics.add_measurement(
Self::BIND_GROUP_LAYOUTS,
render_resource_context
.resources
.bind_group_layouts
.read()
.len() as f64,
);
diagnostics.add_measurement(
Self::SHADER_MODULES,
render_resource_context
.resources
.shader_modules
.read()
.len() as f64,
);
diagnostics.add_measurement(
Self::RENDER_PIPELINES,
render_resource_context
.resources
.render_pipelines
.read()
.len() as f64,
);
}
}

View file

@ -1,193 +0,0 @@
pub mod diagnostic;
mod compute_pass;
mod render_context;
mod render_graph_runner;
mod render_pass;
mod render_resource_context;
mod renderer;
mod resources;
mod type_converter;
pub use compute_pass::*;
pub use render_context::*;
pub use render_graph_runner::*;
pub use render_pass::*;
pub use render_resource_context::*;
pub use renderer::*;
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_render2::{renderer::RenderResources, RenderStage};
use futures_lite::future;
use std::borrow::Cow;
#[derive(Clone, Copy)]
pub enum WgpuFeature {
DepthClamping,
TextureCompressionBc,
TimestampQuery,
PipelineStatisticsQuery,
MappablePrimaryBuffers,
SampledTextureBindingArray,
SampledTextureArrayDynamicIndexing,
SampledTextureArrayNonUniformIndexing,
UnsizedBindingArray,
MultiDrawIndirect,
MultiDrawIndirectCount,
PushConstants,
AddressModeClampToBorder,
NonFillPolygonMode,
TextureCompressionEtc2,
TextureCompressionAstcLdr,
TextureAdapterSpecificFormatFeatures,
ShaderFloat64,
VertexAttribute64Bit,
}
#[derive(Default, Clone)]
pub struct WgpuFeatures {
pub features: Vec<WgpuFeature>,
}
#[derive(Debug, Clone)]
pub struct WgpuLimits {
pub max_bind_groups: u32,
pub max_dynamic_uniform_buffers_per_pipeline_layout: u32,
pub max_dynamic_storage_buffers_per_pipeline_layout: u32,
pub max_sampled_textures_per_shader_stage: u32,
pub max_samplers_per_shader_stage: u32,
pub max_storage_buffers_per_shader_stage: u32,
pub max_storage_textures_per_shader_stage: u32,
pub max_uniform_buffers_per_shader_stage: u32,
pub max_uniform_buffer_binding_size: u32,
pub max_push_constant_size: u32,
pub max_texture_dimension_1d: u32,
pub max_texture_dimension_2d: u32,
pub max_texture_dimension_3d: u32,
pub max_texture_array_layers: u32,
pub max_storage_buffer_binding_size: u32,
pub max_vertex_buffers: u32,
pub max_vertex_attributes: u32,
pub max_vertex_buffer_array_stride: u32,
}
impl Default for WgpuLimits {
fn default() -> Self {
let default = wgpu::Limits::default();
WgpuLimits {
max_bind_groups: default.max_bind_groups,
max_dynamic_uniform_buffers_per_pipeline_layout: default
.max_dynamic_uniform_buffers_per_pipeline_layout,
max_dynamic_storage_buffers_per_pipeline_layout: default
.max_dynamic_storage_buffers_per_pipeline_layout,
max_sampled_textures_per_shader_stage: default.max_sampled_textures_per_shader_stage,
max_samplers_per_shader_stage: default.max_samplers_per_shader_stage,
max_storage_buffers_per_shader_stage: default.max_storage_buffers_per_shader_stage,
max_storage_textures_per_shader_stage: default.max_storage_textures_per_shader_stage,
max_uniform_buffers_per_shader_stage: default.max_uniform_buffers_per_shader_stage,
max_uniform_buffer_binding_size: default.max_uniform_buffer_binding_size,
max_push_constant_size: default.max_push_constant_size,
max_texture_dimension_1d: default.max_texture_dimension_1d,
max_texture_dimension_2d: default.max_texture_dimension_2d,
max_texture_dimension_3d: default.max_texture_dimension_3d,
max_texture_array_layers: default.max_texture_array_layers,
max_storage_buffer_binding_size: default.max_storage_buffer_binding_size,
max_vertex_buffers: default.max_vertex_buffers,
max_vertex_attributes: default.max_vertex_attributes,
max_vertex_buffer_array_stride: default.max_vertex_buffer_array_stride,
}
}
}
#[derive(Default)]
pub struct WgpuPlugin;
impl Plugin for WgpuPlugin {
fn build(&self, app: &mut App) {
let options = app
.world
.get_resource::<WgpuOptions>()
.cloned()
.unwrap_or_else(WgpuOptions::default);
let wgpu_renderer = future::block_on(WgpuRenderer::new(options));
let resource_context = WgpuRenderResourceContext::new(wgpu_renderer.device.clone());
app.world
.insert_resource(RenderResources::new(Box::new(resource_context.clone())));
let render_app = app.sub_app_mut(0);
render_app
.insert_resource(RenderResources::new(Box::new(resource_context)))
.insert_resource(wgpu_renderer)
.add_system_to_stage(RenderStage::Prepare, wgpu_window_system.exclusive_system())
.add_system_to_stage(RenderStage::Render, wgpu_render_system.exclusive_system());
}
}
pub fn wgpu_render_system(world: &mut World) {
world.resource_scope(|world, mut renderer: Mut<WgpuRenderer>| {
renderer.update(world);
})
}
pub fn wgpu_window_system(world: &mut World) {
world.resource_scope(|world, mut renderer: Mut<WgpuRenderer>| {
renderer.handle_new_windows(world);
})
}
#[derive(Default, Clone)]
pub struct WgpuOptions {
pub device_label: Option<Cow<'static, str>>,
pub backend: WgpuBackend,
pub power_pref: WgpuPowerOptions,
pub features: WgpuFeatures,
pub limits: WgpuLimits,
}
#[derive(Clone)]
pub enum WgpuBackend {
Auto,
Vulkan,
Metal,
Dx12,
Dx11,
Gl,
BrowserWgpu,
}
impl WgpuBackend {
fn from_env() -> Self {
if let Ok(backend) = std::env::var("BEVY_WGPU_BACKEND") {
match backend.to_lowercase().as_str() {
"vulkan" => WgpuBackend::Vulkan,
"metal" => WgpuBackend::Metal,
"dx12" => WgpuBackend::Dx12,
"dx11" => WgpuBackend::Dx11,
"gl" => WgpuBackend::Gl,
"webgpu" => WgpuBackend::BrowserWgpu,
other => panic!("Unknown backend: {}", other),
}
} else {
WgpuBackend::Auto
}
}
}
impl Default for WgpuBackend {
fn default() -> Self {
Self::from_env()
}
}
#[derive(Clone)]
pub enum WgpuPowerOptions {
HighPerformance,
Adaptive,
LowPower,
}
impl Default for WgpuPowerOptions {
fn default() -> Self {
WgpuPowerOptions::HighPerformance
}
}

View file

@ -1,285 +0,0 @@
use super::WgpuRenderResourceContext;
use crate::{
compute_pass::WgpuComputePass, resources::WgpuResourceRefs, type_converter::WgpuInto,
WgpuRenderPass,
};
use bevy_render2::{
pass::{
ComputePass, PassDescriptor, RenderPass, RenderPassColorAttachment,
RenderPassDepthStencilAttachment, TextureAttachment,
},
render_resource::{BufferId, TextureId},
renderer::{RenderContext, RenderResourceContext},
texture::Extent3d,
};
use std::sync::Arc;
#[derive(Debug, Default)]
pub struct LazyCommandEncoder {
command_encoder: Option<wgpu::CommandEncoder>,
}
impl LazyCommandEncoder {
pub fn get_or_create(&mut self, device: &wgpu::Device) -> &mut wgpu::CommandEncoder {
match self.command_encoder {
Some(ref mut command_encoder) => command_encoder,
None => {
self.create(device);
self.command_encoder.as_mut().unwrap()
}
}
}
pub fn is_some(&self) -> bool {
self.command_encoder.is_some()
}
pub fn create(&mut self, device: &wgpu::Device) {
let command_encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
self.command_encoder = Some(command_encoder);
}
pub fn take(&mut self) -> Option<wgpu::CommandEncoder> {
self.command_encoder.take()
}
pub fn set(&mut self, command_encoder: wgpu::CommandEncoder) {
self.command_encoder = Some(command_encoder);
}
}
#[derive(Debug)]
pub struct WgpuRenderContext {
pub device: Arc<wgpu::Device>,
pub command_encoder: LazyCommandEncoder,
pub render_resource_context: WgpuRenderResourceContext,
}
impl WgpuRenderContext {
pub fn new(device: Arc<wgpu::Device>, resources: WgpuRenderResourceContext) -> Self {
WgpuRenderContext {
device,
render_resource_context: resources,
command_encoder: LazyCommandEncoder::default(),
}
}
/// Consume this context, finalize the current CommandEncoder (if it exists), and take the
/// current WgpuResources. This is intended to be called from a worker thread right before
/// synchronizing with the main thread.
pub fn finish(&mut self) -> Option<wgpu::CommandBuffer> {
self.command_encoder.take().map(|encoder| encoder.finish())
}
}
impl RenderContext for WgpuRenderContext {
fn copy_buffer_to_buffer(
&mut self,
source_buffer: BufferId,
source_offset: u64,
destination_buffer: BufferId,
destination_offset: u64,
size: u64,
) {
self.render_resource_context.copy_buffer_to_buffer(
self.command_encoder.get_or_create(&self.device),
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
);
}
fn copy_buffer_to_texture(
&mut self,
source_buffer: BufferId,
source_offset: u64,
source_bytes_per_row: u32,
destination_texture: TextureId,
destination_origin: [u32; 3],
destination_mip_level: u32,
size: Extent3d,
) {
self.render_resource_context.copy_buffer_to_texture(
self.command_encoder.get_or_create(&self.device),
source_buffer,
source_offset,
source_bytes_per_row,
destination_texture,
destination_origin,
destination_mip_level,
size,
)
}
fn copy_texture_to_buffer(
&mut self,
source_texture: TextureId,
source_origin: [u32; 3],
source_mip_level: u32,
destination_buffer: BufferId,
destination_offset: u64,
destination_bytes_per_row: u32,
size: Extent3d,
) {
self.render_resource_context.copy_texture_to_buffer(
self.command_encoder.get_or_create(&self.device),
source_texture,
source_origin,
source_mip_level,
destination_buffer,
destination_offset,
destination_bytes_per_row,
size,
)
}
fn copy_texture_to_texture(
&mut self,
source_texture: TextureId,
source_origin: [u32; 3],
source_mip_level: u32,
destination_texture: TextureId,
destination_origin: [u32; 3],
destination_mip_level: u32,
size: Extent3d,
) {
self.render_resource_context.copy_texture_to_texture(
self.command_encoder.get_or_create(&self.device),
source_texture,
source_origin,
source_mip_level,
destination_texture,
destination_origin,
destination_mip_level,
size,
)
}
fn resources(&self) -> &dyn RenderResourceContext {
&self.render_resource_context
}
fn resources_mut(&mut self) -> &mut dyn RenderResourceContext {
&mut self.render_resource_context
}
fn begin_render_pass(
&mut self,
pass_descriptor: &PassDescriptor,
run_pass: &mut dyn FnMut(&mut dyn RenderPass),
) {
if !self.command_encoder.is_some() {
self.command_encoder.create(&self.device);
}
let resource_lock = self.render_resource_context.resources.read();
let refs = resource_lock.refs();
let mut encoder = self.command_encoder.take().unwrap();
{
let render_pass = create_render_pass(pass_descriptor, &refs, &mut encoder);
let mut wgpu_render_pass = WgpuRenderPass {
render_pass,
render_context: self,
wgpu_resources: refs,
pipeline_descriptor: None,
};
run_pass(&mut wgpu_render_pass);
}
self.command_encoder.set(encoder);
}
fn begin_compute_pass(&mut self, run_pass: &mut dyn FnMut(&mut dyn ComputePass)) {
if !self.command_encoder.is_some() {
self.command_encoder.create(&self.device);
}
let resource_lock = self.render_resource_context.resources.read();
let refs = resource_lock.refs();
let mut encoder = self.command_encoder.take().unwrap();
{
let compute_pass =
encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: None });
let mut wgpu_render_pass = WgpuComputePass {
compute_pass,
render_context: self,
wgpu_resources: refs,
pipeline_descriptor: None,
};
run_pass(&mut wgpu_render_pass);
}
self.command_encoder.set(encoder);
}
}
pub fn create_render_pass<'a, 'b>(
pass_descriptor: &PassDescriptor,
refs: &WgpuResourceRefs<'a>,
encoder: &'a mut wgpu::CommandEncoder,
) -> wgpu::RenderPass<'a> {
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &pass_descriptor
.color_attachments
.iter()
.map(|c| create_wgpu_color_attachment(refs, c))
.collect::<Vec<wgpu::RenderPassColorAttachment>>(),
depth_stencil_attachment: pass_descriptor
.depth_stencil_attachment
.as_ref()
.map(|d| create_wgpu_depth_stencil_attachment(refs, d)),
})
}
fn get_texture_view<'a>(
refs: &WgpuResourceRefs<'a>,
attachment: &TextureAttachment,
) -> &'a wgpu::TextureView {
match attachment {
TextureAttachment::Id(render_resource) => refs.texture_views.get(&render_resource).unwrap_or_else(|| &refs.swap_chain_frames.get(&render_resource).unwrap().output.view),
TextureAttachment::Input(_) => panic!("Encountered unset `TextureAttachment::Input`. The `RenderGraph` executor should always set `TextureAttachment::Inputs` to `TextureAttachment::RenderResource` before running. This is a bug, please report it!"),
}
}
fn create_wgpu_color_attachment<'a>(
refs: &WgpuResourceRefs<'a>,
color_attachment: &RenderPassColorAttachment,
) -> wgpu::RenderPassColorAttachment<'a> {
let view = get_texture_view(refs, &color_attachment.attachment);
let resolve_target = color_attachment
.resolve_target
.as_ref()
.map(|target| get_texture_view(refs, &target));
wgpu::RenderPassColorAttachment {
ops: (&color_attachment.ops).wgpu_into(),
view,
resolve_target,
}
}
fn create_wgpu_depth_stencil_attachment<'a>(
refs: &WgpuResourceRefs<'a>,
depth_stencil_attachment: &RenderPassDepthStencilAttachment,
) -> wgpu::RenderPassDepthStencilAttachment<'a> {
let view = get_texture_view(refs, &depth_stencil_attachment.attachment);
wgpu::RenderPassDepthStencilAttachment {
view,
depth_ops: depth_stencil_attachment
.depth_ops
.as_ref()
.map(|ops| ops.wgpu_into()),
stencil_ops: depth_stencil_attachment
.stencil_ops
.as_ref()
.map(|ops| ops.wgpu_into()),
}
}

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