mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 14:10:19 +00:00
bevy_render now uses wgpu directly
This commit is contained in:
parent
01116b1fdb
commit
13ca00178a
105 changed files with 2234 additions and 6710 deletions
|
@ -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"]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,)*);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]>,
|
||||
);
|
||||
}
|
|
@ -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::*;
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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]>,
|
||||
);
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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::*;
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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]
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
61
pipelined/bevy_render2/src/render_resource/pipeline.rs
Normal file
61
pipelined/bevy_render2/src/render_resource/pipeline.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
|
|
|
@ -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(),
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
170
pipelined/bevy_render2/src/renderer/render_device.rs
Normal file
170
pipelined/bevy_render2/src/renderer/render_device.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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::*;
|
|
@ -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")]
|
||||
|
|
|
@ -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,
|
||||
}]
|
||||
),
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"] }
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
mod wgpu_resource_diagnostics_plugin;
|
||||
pub use wgpu_resource_diagnostics_plugin::WgpuResourceDiagnosticsPlugin;
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in a new issue