summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine-ecs-macros/src/lib.rs44
-rw-r--r--engine-ecs/examples/component_events.rs2
-rw-r--r--engine-ecs/examples/component_removed_event.rs2
-rw-r--r--engine-ecs/src/actions.rs25
-rw-r--r--engine-ecs/src/component.rs70
-rw-r--r--engine-ecs/src/entity.rs11
-rw-r--r--engine-ecs/src/lib.rs39
-rw-r--r--engine-ecs/src/pair.rs61
-rw-r--r--engine-ecs/src/phase.rs2
-rw-r--r--engine-ecs/src/query/term.rs113
-rw-r--r--engine-ecs/src/sole.rs39
-rw-r--r--engine-ecs/src/system.rs4
-rw-r--r--engine-ecs/src/uid.rs45
-rw-r--r--engine/src/asset.rs82
-rw-r--r--engine/src/rendering.rs12
-rw-r--r--engine/src/rendering/backend/opengl.rs100
-rw-r--r--engine/src/rendering/main_pipeline.rs12
-rw-r--r--engine/src/rendering/shader.rs1015
-rw-r--r--engine/src/rendering/shader/cursor.rs2
-rw-r--r--engine/src/rendering/shader/reflection.rs713
-rw-r--r--engine/src/ui/dear_imgui.rs13
-rw-r--r--engine/src/ui/view/world.rs182
-rw-r--r--engine/src/windowing.rs11
23 files changed, 1489 insertions, 1110 deletions
diff --git a/engine-ecs-macros/src/lib.rs b/engine-ecs-macros/src/lib.rs
index 9bc829b..a95cfe4 100644
--- a/engine-ecs-macros/src/lib.rs
+++ b/engine-ecs-macros/src/lib.rs
@@ -45,8 +45,6 @@ pub fn component_derive(input: TokenStream) -> TokenStream
let item_ident = item.ident();
- let (impl_generics, type_generics, where_clause) = item.generics().split_for_impl();
-
let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs));
assert!(
@@ -80,14 +78,8 @@ pub fn component_derive(input: TokenStream) -> TokenStream
#id_var
- impl #impl_generics Component for #item_ident #type_generics
- #where_clause
+ impl Component for #item_ident
{
- fn id() -> Uid
- {
- *#id_var_ident
- }
-
fn type_reflection() -> Option<&'static #ecs_path::reflection::Type>
{
struct SpecializationTarget<T>(std::marker::PhantomData<T>);
@@ -134,8 +126,7 @@ pub fn component_derive(input: TokenStream) -> TokenStream
}
}
- impl #impl_generics SystemInput for #item_ident #type_generics
- #where_clause
+ impl SystemInput for #item_ident
{
}
@@ -146,7 +137,18 @@ pub fn component_derive(input: TokenStream) -> TokenStream
#ecs_path::component::Parts::builder()
.name(<Self as Component>::name(&self))
.type_reflection(<Self as Component>::type_reflection())
- .build(<Self as Component>::id(), self)
+ .build(
+ <Self as #ecs_path::component::WithAssociatedId>::id(),
+ self
+ )
+ }
+ }
+
+ impl #ecs_path::component::WithAssociatedId for #item_ident
+ {
+ fn id() -> Uid
+ {
+ *#id_var_ident
}
}
}
@@ -203,11 +205,6 @@ pub fn sole_derive(input: TokenStream) -> TokenStream
impl Sole for #item_ident
{
- fn id() -> Uid
- {
- *#id_var_ident
- }
-
fn type_reflection() -> Option<&'static #ecs_path::reflection::Type>
{
struct SpecializationTarget<T>(std::marker::PhantomData<T>);
@@ -262,7 +259,18 @@ pub fn sole_derive(input: TokenStream) -> TokenStream
.name(<Self as Sole>::name(&self))
.is_sole(true)
.type_reflection(<Self as Sole>::type_reflection())
- .build(<Self as Sole>::id(), self)
+ .build(
+ <Self as #ecs_path::component::WithAssociatedId>::id(),
+ self
+ )
+ }
+ }
+
+ impl #ecs_path::component::WithAssociatedId for #item_ident
+ {
+ fn id() -> Uid
+ {
+ *#id_var_ident
}
}
}
diff --git a/engine-ecs/examples/component_events.rs b/engine-ecs/examples/component_events.rs
index 82d9b6c..b32df8b 100644
--- a/engine-ecs/examples/component_events.rs
+++ b/engine-ecs/examples/component_events.rs
@@ -1,5 +1,5 @@
use engine_ecs::actions::Actions;
-use engine_ecs::component::Component;
+use engine_ecs::component::WithAssociatedId;
use engine_ecs::event::component::{Changed, EventMatchExt, Removed};
use engine_ecs::pair::Pair;
use engine_ecs::phase::UPDATE;
diff --git a/engine-ecs/examples/component_removed_event.rs b/engine-ecs/examples/component_removed_event.rs
index 23dfc92..40d9bc8 100644
--- a/engine-ecs/examples/component_removed_event.rs
+++ b/engine-ecs/examples/component_removed_event.rs
@@ -1,5 +1,5 @@
use engine_ecs::actions::Actions;
-use engine_ecs::component::Component;
+use engine_ecs::component::WithAssociatedId;
use engine_ecs::event::component::{EventMatchExt, Removed};
use engine_ecs::pair::Pair;
use engine_ecs::phase::UPDATE;
diff --git a/engine-ecs/src/actions.rs b/engine-ecs/src/actions.rs
index 69605b3..bbc19c6 100644
--- a/engine-ecs/src/actions.rs
+++ b/engine-ecs/src/actions.rs
@@ -1,12 +1,18 @@
use std::borrow::Cow;
use crate::bundle::Bundle;
-use crate::component::{Component, IntoParts, Parts as ComponentParts};
+use crate::component::{
+ Component,
+ IntoParts,
+ Parts as ComponentParts,
+ RemovableTuple as RemovableComponentTuple,
+};
use crate::entity::Name as EntityName;
use crate::event::component::Removed;
use crate::pair::Pair;
+use crate::sole::Sole;
use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
-use crate::uid::{Uid, WithUidTuple};
+use crate::uid::Uid;
use crate::{ActionQueue, World};
/// Used to to queue up actions for a [`World`] to perform.
@@ -174,9 +180,9 @@ impl Actions<'_>
}
/// Queues up removing component(s) from a entity at the end of the current tick.
- pub fn remove_comps<Ids: WithUidTuple>(&mut self, entity_uid: Uid)
+ pub fn remove_comps<Components: RemovableComponentTuple>(&mut self, entity_uid: Uid)
{
- self.remove_components(entity_uid, Ids::uids());
+ self.remove_components(entity_uid, Components::ids());
}
/// Queues up setting the target of a entity's pair at the end of the current tick.
@@ -299,6 +305,16 @@ impl Actions<'_>
});
}
+ /// Queues up creation of a sole at the end of the current tick, returning the [`Uid`]
+ /// of the sole.
+ pub fn create_sole<SoleT: Sole>(&mut self, sole: SoleT) -> Uid
+ {
+ self.action_queue
+ .push(Action::CreateSole(sole.into_parts()));
+
+ SoleT::id()
+ }
+
/// Stops the [`World`]. The world will finish the current tick and that tick will be
/// the last.
pub fn stop(&mut self)
@@ -334,5 +350,6 @@ pub(crate) enum Action
pair_id: Uid,
new_pair: ComponentParts,
},
+ CreateSole(ComponentParts),
Stop,
}
diff --git a/engine-ecs/src/component.rs b/engine-ecs/src/component.rs
index 400b12c..d424e16 100644
--- a/engine-ecs/src/component.rs
+++ b/engine-ecs/src/component.rs
@@ -3,6 +3,7 @@ use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use engine_ecs_macros::Component;
+use seq_macro::seq;
use crate::event::component::Changed;
use crate::event::Submitter as EventSubmitter;
@@ -23,13 +24,9 @@ pub mod local;
pub(crate) mod storage;
-pub trait Component: SystemInput + Any + IntoParts
+/// A component that is not a pair and is not a sole.
+pub trait Component: WithAssociatedId + SystemInput + Any + IntoParts
{
- /// Returns the ID of this component.
- fn id() -> Uid
- where
- Self: Sized;
-
fn type_reflection() -> Option<&'static TypeReflection>
where
Self: Sized;
@@ -38,32 +35,50 @@ pub trait Component: SystemInput + Any + IntoParts
fn name(&self) -> &'static str;
}
-impl dyn Component
+pub trait WithAssociatedId
{
- pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real>
- {
- (self as &mut dyn Any).downcast_mut()
- }
+ /// Returns the ID of this component.
+ fn id() -> Uid;
+}
- pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real>
- {
- (self as &dyn Any).downcast_ref()
- }
+/// A component that is removable from it's entity.
+///
+/// This trait is implemented for [`crate::pair::Pair`] so it does not have [`Component`]
+/// as supertrait.
+pub trait Removable: WithAssociatedId {}
- pub fn is<Other: 'static>(&self) -> bool
- {
- (self as &dyn Any).is::<Other>()
- }
-}
+impl<ComponentT: Component> Removable for ComponentT {}
-impl Debug for dyn Component
+/// Implemented by tuples of types implementing [`Removable`].
+///
+/// This trait is sealed so it cannot be implemented by types outside of this module.
+pub trait RemovableTuple: sealed::Sealed
{
- fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
- {
- formatter.debug_struct("Component").finish_non_exhaustive()
- }
+ fn ids() -> impl IntoIterator<Item = Uid>;
+}
+
+macro_rules! gen_removable_tuple_impl {
+ ($c: tt) => {
+ seq!(I in 0..=$c {
+ impl<#(RemovableT~I: Removable,)*> RemovableTuple for (#(RemovableT~I,)*)
+ {
+ fn ids() -> impl IntoIterator<Item = Uid>
+ {
+ [#(RemovableT~I::id(),)*]
+ }
+ }
+
+ impl<#(RemovableT~I: Removable,)*> sealed::Sealed for (#(RemovableT~I,)*)
+ {
+ }
+ });
+ };
}
+seq!(C in 0..=16 {
+ gen_removable_tuple_impl!(C);
+});
+
#[derive(Debug)]
pub struct Handle<'a, DataT: ?Sized + 'static>
{
@@ -394,3 +409,8 @@ pub struct Info
pub name: &'static str,
pub is_sole: bool,
}
+
+mod sealed
+{
+ pub trait Sealed {}
+}
diff --git a/engine-ecs/src/entity.rs b/engine-ecs/src/entity.rs
index 7515f73..e85c49c 100644
--- a/engine-ecs/src/entity.rs
+++ b/engine-ecs/src/entity.rs
@@ -232,9 +232,16 @@ impl<'a> Handle<'a>
}
}
+ /// Returns whether or not this entity contains the specified component.
+ #[must_use]
+ pub fn has_component<ComponentT: Component>(&self) -> bool
+ {
+ self.has_component_by_id(ComponentT::id())
+ }
+
/// Returns whether or not this entity contains a component with the specified `Uid`.
#[must_use]
- pub fn has_component(&self, component_uid: Uid) -> bool
+ pub fn has_component_by_id(&self, component_uid: Uid) -> bool
{
self.archetype
.contains_component_with_exact_id(component_uid)
@@ -327,7 +334,7 @@ macro_rules! declare_entity {
world.spawn_with_uid(
*$ident,
($components).with_elem($crate::entity::Name {
- name: concat!(module_path!(), stringify!($ident)).into()
+ name: concat!(module_path!(), "::", stringify!($ident)).into()
})
);
});
diff --git a/engine-ecs/src/lib.rs b/engine-ecs/src/lib.rs
index a752f6a..f9e8143 100644
--- a/engine-ecs/src/lib.rs
+++ b/engine-ecs/src/lib.rs
@@ -22,6 +22,7 @@ use crate::component::{
Info as ComponentInfo,
IntoParts as IntoComponentParts,
Parts as ComponentParts,
+ WithAssociatedId,
};
use crate::entity::{
Declaration as EntityDeclaration,
@@ -59,10 +60,10 @@ use crate::sole::{Single, Sole};
use crate::system::observer::Observer;
use crate::system::{
Callbacks,
+ Function as SystemFunction,
Metadata as SystemMetadata,
Param as SystemParam,
System,
- SystemComponent,
};
use crate::uid::Uid;
@@ -252,6 +253,7 @@ impl World
[
sole.into_parts(),
EntityName { name: name.into() }.into_parts(),
+ crate::sole::Is.into_parts(),
],
&ComponentAddingFlags { allow_soles: true },
);
@@ -271,7 +273,7 @@ impl World
self.create_ent(
ent_id,
- [SystemComponent { system }.into_parts()].into_iter().chain(
+ [SystemFunction { system }.into_parts()].into_iter().chain(
ObserverT::observed_events()
.into_iter()
.map(IntoComponentParts::into_parts),
@@ -290,7 +292,7 @@ impl World
{
let (type_erased_system, mut system_callbacks) = system.finish();
- let system_ent_id = self.spawn((SystemComponent { system: type_erased_system },));
+ let system_ent_id = self.spawn((SystemFunction { system: type_erased_system },));
system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id });
@@ -490,7 +492,7 @@ impl World
.into_iter()
.filter_map(|phase_has_system| phase_has_system.get_target_ent())
{
- let Some(system) = system_entity.get::<SystemComponent>() else {
+ let Some(system) = system_entity.get::<SystemFunction>() else {
cold_path();
continue;
};
@@ -666,6 +668,31 @@ impl World
&ComponentAddingFlags { allow_soles: false },
);
}
+ Action::CreateSole(sole_parts) => {
+ if let Err(EntityAlreadyExistsError) =
+ self.data.component_storage.create_entity(sole_parts.id)
+ {
+ tracing::error!(
+ name = sole_parts.name,
+ "Can not create sole that already exists"
+ );
+ continue;
+ }
+
+ let name = sole_parts.name;
+
+ Self::add_entity_components(
+ sole_parts.id,
+ [
+ sole_parts,
+ EntityName { name: name.into() }.into_parts(),
+ crate::sole::Is.into_parts(),
+ ],
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ &ComponentAddingFlags { allow_soles: true },
+ );
+ }
Action::Stop => {
self.stop.store(true, Ordering::Relaxed);
}
@@ -873,10 +900,10 @@ impl World
{
assert!(event_id.is_pair());
- let query = Query::<(&SystemComponent,)>::from_flexible_query(
+ let query = Query::<(&SystemFunction,)>::from_flexible_query(
self.flexible_query(
QueryTerms::<QUERY_MAX_TERM_CNT>::builder()
- .present([SystemComponent::id(), event_id])
+ .present([SystemFunction::id(), event_id])
.build(),
),
);
diff --git a/engine-ecs/src/pair.rs b/engine-ecs/src/pair.rs
index 834077c..efeead9 100644
--- a/engine-ecs/src/pair.rs
+++ b/engine-ecs/src/pair.rs
@@ -8,6 +8,8 @@ use crate::component::{
HandleMut as ComponentHandleMut,
IntoParts as IntoComponentParts,
Parts as ComponentParts,
+ Removable as RemovableComponent,
+ WithAssociatedId as ComponentWithAssociatedId,
};
use crate::entity::{
Handle as EntityHandle,
@@ -21,7 +23,7 @@ use crate::query::{
TermsBuilderInterface,
};
use crate::reflection::Type as TypeReflection;
-use crate::uid::{PairParams as UidPairParams, Uid, With as WithUid};
+use crate::uid::{PairParams as UidPairParams, Uid};
use crate::util::impl_multiple;
use crate::{Component, EntityComponentRef, World};
@@ -265,7 +267,7 @@ where
_term_metadata: QueryTermMetadata,
)
{
- terms_builder.present([Pair::<Relation, Target>::uid()]);
+ terms_builder.present([Pair::<Relation, Target>::id()]);
}
fn fields(
@@ -276,7 +278,7 @@ where
{
let target_component = search_result
.entity_handle
- .get_matching_components(Pair::<Relation, Target>::uid())
+ .get_matching_components(Pair::<Relation, Target>::id())
.next()
.expect("Not possible");
@@ -308,7 +310,7 @@ where
_term_metadata: QueryTermMetadata,
)
{
- terms_builder.present([Pair::<Relation, Target>::uid()]);
+ terms_builder.present([Pair::<Relation, Target>::id()]);
}
fn fields(
@@ -319,7 +321,7 @@ where
{
let target_component = search_result
.entity_handle
- .get_matching_components(Pair::<Relation, Target>::uid())
+ .get_matching_components(Pair::<Relation, Target>::id())
.next()
.expect("Not possible");
@@ -355,7 +357,7 @@ where
_term_metadata: QueryTermMetadata,
)
{
- terms_builder.present([Self::uid()]);
+ terms_builder.present([Self::id()]);
}
fn fields(
@@ -366,7 +368,7 @@ where
{
let first_matching_comp = search_result
.entity_handle
- .get_matching_components(Self::uid())
+ .get_matching_components(Self::id())
.next()
.expect("Not possible");
@@ -378,12 +380,12 @@ where
}
}
-impl<Relation, Target> WithUid for Pair<Relation, Target>
+impl<Relation, Target> ComponentWithAssociatedId for Pair<Relation, Target>
where
Relation: Component,
Target: Component,
{
- fn uid() -> Uid
+ fn id() -> Uid
{
Uid::new_pair(&UidPairParams {
relation: Relation::id(),
@@ -392,15 +394,28 @@ where
}
}
-impl<Relation> WithUid for Pair<Relation, Wildcard>
+impl<Relation, Target> RemovableComponent for Pair<Relation, Target>
where
Relation: Component,
+ Target: Component,
{
- fn uid() -> Uid
+}
+
+// TODO: Implement RemovableComponent for Pair<Relation, Wildcard>
+// and Pair<Wildcard, Target>.
+//
+// This should only be done after removal of wildcard pairs is added in lib.rs
+
+impl<Relation> Pair<Relation, Wildcard>
+where
+ Relation: Component,
+{
+ #[must_use]
+ pub fn id() -> Uid
{
Uid::new_pair(&UidPairParams {
relation: Relation::id(),
- target: Wildcard::uid(),
+ target: Wildcard::id(),
})
}
}
@@ -464,19 +479,17 @@ impl<'query, Relation, Target> WithWildcard<'query, Relation, Target>
assert!(component_id.is_pair());
assert!(
- Relation::uid() == Wildcard::uid()
+ Relation::uid() == Wildcard::id()
|| component_id.relation() == Relation::uid()
);
assert!(
- Target::uid() == Wildcard::uid() || component_id.target() == Target::uid()
+ Target::uid() == Wildcard::id() || component_id.target() == Target::uid()
);
- assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid());
+ assert!(Relation::uid() == Wildcard::id() || Target::uid() == Wildcard::id());
- assert!(
- !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid())
- );
+ assert!(!(Relation::uid() == Wildcard::id() && Target::uid() == Wildcard::id()));
WithWildcard {
world,
@@ -641,11 +654,9 @@ impl<'query, Relation, Target> MultipleWithWildcard<'query, Relation, Target>
Relation: ComponentOrWildcard,
Target: ComponentOrWildcard,
{
- assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid());
+ assert!(Relation::uid() == Wildcard::id() || Target::uid() == Wildcard::id());
- assert!(
- !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid())
- );
+ assert!(!(Relation::uid() == Wildcard::id() && Target::uid() == Wildcard::id()));
MultipleWithWildcard {
entity_handle,
@@ -691,7 +702,7 @@ impl<'a, Relation: Component> IntoIterator
WithWildcardIter {
inner: self
.entity_handle
- .get_matching_components(Pair::<Relation, Wildcard>::uid()),
+ .get_matching_components(Pair::<Relation, Wildcard>::id()),
world: self.world,
_pd: PhantomData,
}
@@ -736,7 +747,7 @@ pub struct Wildcard(Infallible);
impl Wildcard
{
#[must_use]
- pub fn uid() -> Uid
+ pub fn id() -> Uid
{
Uid::wildcard()
}
@@ -761,7 +772,7 @@ impl ComponentOrWildcard for Wildcard
{
fn uid() -> Uid
{
- Wildcard::uid()
+ Wildcard::id()
}
}
diff --git a/engine-ecs/src/phase.rs b/engine-ecs/src/phase.rs
index 64c1593..8d09ab4 100644
--- a/engine-ecs/src/phase.rs
+++ b/engine-ecs/src/phase.rs
@@ -56,7 +56,7 @@ pub(crate) fn check_parents(
continue;
};
- if !parent_ent.has_component(Phase::id()) {
+ if !parent_ent.has_component::<Phase>() {
let parent_ent_name = parent_ent.get::<EntityName>();
tracing::warn!(
diff --git a/engine-ecs/src/query/term.rs b/engine-ecs/src/query/term.rs
index 980ee87..f5fd531 100644
--- a/engine-ecs/src/query/term.rs
+++ b/engine-ecs/src/query/term.rs
@@ -5,7 +5,9 @@ use crate::component::{
Component,
Handle as ComponentHandle,
HandleMut as ComponentHandleMut,
+ WithAssociatedId as _,
};
+use crate::pair::{Pair, Wildcard};
use crate::query::{
FoundByTraversal,
SearchResult,
@@ -17,19 +19,61 @@ use crate::query::{
Traversal,
TraversalKind,
};
-use crate::uid::With as WithUid;
use crate::World;
-pub struct With<WithUidT>
+pub struct With<T>
+{
+ _pd: PhantomData<T>,
+}
+
+impl<'query, ComponentT: Component> Term<'query> for With<ComponentT>
+{
+ type Fields = ();
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.present([ComponentT::id()]);
+ }
+
+ fn fields(
+ _world: &'query World,
+ _search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ }
+}
+
+impl<'query, Relation, Target> Term<'query> for With<Pair<Relation, Target>>
where
- WithUidT: WithUid,
+ Relation: Component,
+ Target: Component,
{
- _pd: PhantomData<WithUidT>,
+ type Fields = ();
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.present([Pair::<Relation, Target>::id()]);
+ }
+
+ fn fields(
+ _world: &'query World,
+ _search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ }
}
-impl<'query, WithUidT> Term<'query> for With<WithUidT>
+impl<'query, Relation> Term<'query> for With<Pair<Relation, Wildcard>>
where
- WithUidT: WithUid,
+ Relation: Component,
{
type Fields = ();
@@ -38,7 +82,7 @@ where
_term_metadata: TermMetadata,
)
{
- terms_builder.present([WithUidT::uid()]);
+ terms_builder.present([Pair::<Relation, Wildcard>::id()]);
}
fn fields(
@@ -50,16 +94,59 @@ where
}
}
-pub struct Without<WithUidT>
+pub struct Without<T>
+{
+ _pd: PhantomData<T>,
+}
+
+impl<'query, ComponentT: Component> Term<'query> for Without<ComponentT>
+{
+ type Fields = ();
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.absent([ComponentT::id()]);
+ }
+
+ fn fields(
+ _world: &'query World,
+ _search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ }
+}
+
+impl<'query, Relation, Target> Term<'query> for Without<Pair<Relation, Target>>
where
- WithUidT: WithUid,
+ Relation: Component,
+ Target: Component,
{
- _pd: PhantomData<WithUidT>,
+ type Fields = ();
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.absent([Pair::<Relation, Target>::id()]);
+ }
+
+ fn fields(
+ _world: &'query World,
+ _search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ }
}
-impl<'query, WithUidT> Term<'query> for Without<WithUidT>
+impl<'query, Relation> Term<'query> for Without<Pair<Relation, Wildcard>>
where
- WithUidT: WithUid,
+ Relation: Component,
{
type Fields = ();
@@ -68,7 +155,7 @@ where
_term_metadata: TermMetadata,
)
{
- terms_builder.absent([WithUidT::uid()]);
+ terms_builder.absent([Pair::<Relation, Wildcard>::id()]);
}
fn fields(
diff --git a/engine-ecs/src/sole.rs b/engine-ecs/src/sole.rs
index 4047b48..cf5283b 100644
--- a/engine-ecs/src/sole.rs
+++ b/engine-ecs/src/sole.rs
@@ -4,18 +4,14 @@ use std::fmt::Debug;
use crate::component::{
HandleMut as ComponentHandleMut,
IntoParts as IntoComponentParts,
+ WithAssociatedId as ComponentWithAssociatedId,
};
use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
-use crate::uid::Uid;
-use crate::World;
+use crate::{Component, World};
/// A component which has a single instance in the [`World`].
-pub trait Sole: Any + IntoComponentParts
+pub trait Sole: Any + IntoComponentParts + ComponentWithAssociatedId
{
- fn id() -> Uid
- where
- Self: Sized;
-
fn type_reflection() -> Option<&'static crate::reflection::Type>
where
Self: Sized;
@@ -24,31 +20,10 @@ pub trait Sole: Any + IntoComponentParts
fn name(&self) -> &'static str;
}
-impl dyn Sole
-{
- pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real>
- {
- (self as &mut dyn Any).downcast_mut()
- }
-
- pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real>
- {
- (self as &dyn Any).downcast_ref()
- }
-
- pub fn is<Other: 'static>(&self) -> bool
- {
- (self as &dyn Any).is::<Other>()
- }
-}
-
-impl Debug for dyn Sole
-{
- fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
- {
- formatter.debug_struct("Sole").finish_non_exhaustive()
- }
-}
+/// Marker component for the entities of soles.
+#[derive(Debug, Component)]
+#[non_exhaustive]
+pub struct Is;
/// Holds a reference to a singleton component.
#[derive(Debug)]
diff --git a/engine-ecs/src/system.rs b/engine-ecs/src/system.rs
index e0eac57..b79f20c 100644
--- a/engine-ecs/src/system.rs
+++ b/engine-ecs/src/system.rs
@@ -163,8 +163,10 @@ impl Callbacks for NoCallbacks
fn on_created(&mut self, _world: &mut World, _metadata: Metadata) {}
}
+/// System & observer function component. Intentionally not runnable outside of this
+/// crate.
#[derive(Debug, Component)]
-pub(crate) struct SystemComponent
+pub struct Function
{
pub(crate) system: TypeErased,
}
diff --git a/engine-ecs/src/uid.rs b/engine-ecs/src/uid.rs
index 78ca2ad..6b1a4b5 100644
--- a/engine-ecs/src/uid.rs
+++ b/engine-ecs/src/uid.rs
@@ -1,10 +1,7 @@
use std::fmt::{Debug, Display, Formatter};
use std::sync::atomic::{AtomicU32, Ordering};
-use seq_macro::seq;
-
-use crate::component::Component;
-use crate::util::{gen_mask_64, Array, BitMask, NumberExt};
+use crate::util::{gen_mask_64, BitMask, NumberExt};
static NEXT: AtomicU32 = AtomicU32::new(Uid::FIRST_UNIQUE_ID);
@@ -165,43 +162,3 @@ pub struct PairParams
pub relation: Uid,
pub target: Uid,
}
-
-pub trait With: 'static
-{
- fn uid() -> Uid;
-}
-
-impl<ComponentT: Component> With for ComponentT
-{
- fn uid() -> Uid
- {
- Self::id()
- }
-}
-
-pub trait WithUidTuple
-{
- type UidsArray: Array<Uid>;
-
- fn uids() -> Self::UidsArray;
-}
-
-macro_rules! impl_with_uid_tuple {
- ($c: tt) => {
- seq!(I in 0..=$c {
- impl<#(WithUid~I: With,)*> WithUidTuple for (#(WithUid~I,)*)
- {
- type UidsArray = [Uid; $c + 1];
-
- fn uids() -> Self::UidsArray
- {
- [#(WithUid~I::uid(),)*]
- }
- }
- });
- };
-}
-
-seq!(C in 0..=16 {
- impl_with_uid_tuple!(C);
-});
diff --git a/engine/src/asset.rs b/engine/src/asset.rs
index 31d6e06..e8753bc 100644
--- a/engine/src/asset.rs
+++ b/engine/src/asset.rs
@@ -332,6 +332,88 @@ impl Assets
Some(metadata_lut.get(&id)?.label.clone().into_label())
}
+ pub fn get_status<AssetT: Asset>(&self, handle: &Handle<AssetT>) -> Status
+ {
+ self.get_status_by_id(handle.id())
+ }
+
+ pub fn get_status_by_id(&self, asset_id: Id) -> Status
+ {
+ let Some(asset_id) = asset_id.into_valid() else {
+ return Status::NotFound;
+ };
+
+ let metadata_lut = self.metadata_lut.borrow();
+
+ let Some(asset_metadata) = metadata_lut.get(&asset_id) else {
+ return Status::NotFound;
+ };
+
+ let mut is_asset_loading = false;
+
+ match &asset_metadata.state {
+ AssetState::DependenciesPending { .. } => {
+ return Status::Pending;
+ }
+ AssetState::Failed { reason, .. } => {
+ return Status::Failed(*reason);
+ }
+ AssetState::Ready => {
+ return Status::Ready;
+ }
+ AssetState::Loading => {
+ // TODO: It might be possible to just return Status::Pending here
+ // since asset states are propagated to their related assets
+ is_asset_loading = true;
+ }
+ AssetState::NotStored => {}
+ }
+
+ if matches!(asset_metadata.relation, AssetRelation::Parent { .. }) {
+ return if is_asset_loading {
+ Status::Pending
+ } else {
+ Status::NotFound
+ };
+ }
+
+ let parent_asset_state = (|| {
+ let parent_asset_id = match &asset_metadata.relation {
+ AssetRelation::Child { parent } => *parent,
+ AssetRelation::Parent { .. } => {
+ // label.name is Some so the asset can not be a parent
+ unreachable!()
+ }
+ };
+
+ let parent_asset_metadata = metadata_lut.get(&parent_asset_id)?;
+
+ match &parent_asset_metadata.state {
+ AssetState::Failed { reason, .. } => Some(Status::Failed(*reason)),
+ AssetState::Ready
+ | AssetState::NotStored
+ | AssetState::Loading
+ | AssetState::DependenciesPending { .. } => None,
+ }
+ })();
+
+ match parent_asset_state {
+ Some(Status::Failed(FailureReason::Dependency)) => {
+ // It would make no sense for a asset's status to be Status::Failed if the
+ // parent asset has a missing dependency
+
+ if is_asset_loading {
+ Status::Pending
+ } else {
+ Status::NotFound
+ }
+ }
+ Some(parent_asset_state) => parent_asset_state,
+ None if is_asset_loading => Status::Pending,
+ None => Status::NotFound,
+ }
+ }
+
pub fn get_status_by_label(&self, label: &Label<'_>) -> Status
{
let id_lut = self.id_lut.borrow();
diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs
index bbfee72..1f1be64 100644
--- a/engine/src/rendering.rs
+++ b/engine/src/rendering.rs
@@ -24,7 +24,10 @@ use crate::mesh::Mesh;
use crate::rendering::blending::Config as BlendingConfig;
use crate::rendering::object::{Id as ObjectId, Store as ObjectStore};
use crate::rendering::shader::cursor::Binding as ShaderBinding;
-use crate::rendering::shader::Program as ShaderProgram;
+use crate::rendering::shader::{
+ CompileTargetId as ShaderCompileTargetId,
+ Program as ShaderProgram,
+};
use crate::texture::{CubeMapFace, Properties as TextureProperties};
use crate::vector::Vec2;
use crate::windowing::dpi::PhysicalSize;
@@ -199,6 +202,13 @@ impl SurfaceId
}
}
+/// Properties of the current rendering backend.
+#[derive(Debug, Sole)]
+pub struct BackendProperties
+{
+ pub shader_target_id: ShaderCompileTargetId,
+}
+
#[derive(Debug, Default, Sole)]
pub struct RenderPasses
{
diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs
index 0a04d6e..bdaa555 100644
--- a/engine/src/rendering/backend/opengl.rs
+++ b/engine/src/rendering/backend/opengl.rs
@@ -6,6 +6,7 @@ use std::convert::Infallible;
use std::hint::cold_path;
use std::num::NonZero;
+use ecs::phase::INIT as INIT_PHASE;
use ecs::query::term::With;
use glutin::config::Config as GlutinConfig;
use glutin::display::GetGlDisplay;
@@ -116,14 +117,17 @@ use crate::rendering::shader::cursor::{
BindingLocation as ShaderBindingLocation,
BindingValue as ShaderBindingValue,
};
+use crate::rendering::shader::reflection::Stage as ShaderStage;
use crate::rendering::shader::{
+ EntrypointFlags as ShaderEntrypointFlags,
Error as ShaderError,
Program as ShaderProgram,
- ProgramMetadata as ShaderProgramMetadata,
- Stage as ShaderStage,
+ VertexDescCreationError as ShaderVertexDescCreationError,
+ VertexDescription as ShaderVertexDescription,
};
use crate::rendering::{
AssetOrValue,
+ BackendProperties,
BufferClearMask,
Command,
CommandQueue,
@@ -436,7 +440,7 @@ enum BackendResource
Shader
{
program: GlShaderProgram,
- program_metadata: ShaderProgramMetadata,
+ info: ShaderInfo,
bindings: IntMap<ShaderBindingIndex, BackendShaderBinding>,
},
Texture
@@ -489,6 +493,14 @@ enum TextureKind
CubeMap,
}
+#[derive(Debug, Clone)]
+struct ShaderInfo
+{
+ /// If the shader program has a entry point in the vertex stage, this field will
+ /// contain a description of the vertex type passed to the entry point.
+ vertex_desc: Option<ShaderVertexDescription>,
+}
+
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct Extension {}
@@ -497,6 +509,8 @@ impl crate::ecs::extension::Extension for Extension
{
fn collect(self, mut collector: crate::ecs::extension::Collector<'_>)
{
+ collector.add_system(*INIT_PHASE, init);
+
collector.add_system(*RENDER_PHASE, handle_commands);
collector.add_system(*POST_RENDER_PHASE, prepare_windows);
@@ -506,6 +520,24 @@ impl crate::ecs::extension::Extension for Extension
}
}
+fn init(
+ mut shader_context: Single<crate::rendering::shader::Context>,
+ mut actions: Actions,
+) -> Result<(), crate::Error>
+{
+ let shader_context = shader_context.get_mut()?;
+
+ let shader_target_ids = shader_context
+ .init_session(&[&crate::rendering::shader::CompileTarget::default()
+ .format(crate::rendering::shader::CompileTargetFormat::Glsl)])?;
+
+ actions.create_sole(BackendProperties {
+ shader_target_id: shader_target_ids[0],
+ });
+
+ Ok(())
+}
+
fn prepare_windows(
window_query: Query<(
Option<&Window>,
@@ -756,6 +788,7 @@ fn handle_commands(
mut object_store: Single<ObjectStore>,
mut command_queue: Single<CommandQueue>,
assets: Single<Assets>,
+ backend_properties: Single<BackendProperties>,
) -> Result<(), crate::Error>
{
let Ok(graphics_ctx) = graphics_ctx.get_mut() else {
@@ -766,6 +799,10 @@ fn handle_commands(
let command_queue = command_queue.get_mut()?;
let assets = assets.get()?;
+ let Ok(backend_properties) = backend_properties.get() else {
+ return Ok(());
+ };
+
let GraphicsContext {
ref gl_context,
ref mut surfaces,
@@ -776,11 +813,8 @@ fn handle_commands(
return Ok(());
};
- let mut activated_gl_shader_program: Option<(
- ObjectId,
- GlShaderProgram,
- ShaderProgramMetadata,
- )> = None;
+ let mut activated_gl_shader_program: Option<(ObjectId, GlShaderProgram, ShaderInfo)> =
+ None;
for command in command_queue.drain() {
let tracing_span = tracing::info_span!(
@@ -895,8 +929,21 @@ fn handle_commands(
program: create_shader_program(
gl_context,
&shader_program,
+ backend_properties,
)?,
- program_metadata: shader_program.metadata().clone(),
+ info: ShaderInfo {
+ vertex_desc: if shader_program
+ .metadata()
+ .link_entrypoints
+ .contains(ShaderEntrypointFlags::VERTEX)
+ {
+ Some(shader_program.create_vertex_desc(
+ backend_properties.shader_target_id,
+ )?)
+ } else {
+ None
+ },
+ },
bindings: IntMap::with_capacity(4),
})
},
@@ -914,7 +961,7 @@ fn handle_commands(
continue;
};
- let BackendResource::Shader { program, program_metadata, bindings } =
+ let BackendResource::Shader { program, info: shader_info, bindings } =
resource
else {
unreachable!();
@@ -927,7 +974,7 @@ fn handle_commands(
}
activated_gl_shader_program =
- Some((shader_object_id, program.clone(), program_metadata.clone()));
+ Some((shader_object_id, program.clone(), shader_info.clone()));
}
Command::SetShaderBinding(
shader_object_id,
@@ -1162,7 +1209,7 @@ fn handle_commands(
unreachable!();
};
- let Some((shader_program_obj_id, _, shader_program_metadata)) =
+ let Some((shader_program_obj_id, _, shader_info)) =
&activated_gl_shader_program
else {
tracing::error!("No shader program is activated");
@@ -1174,8 +1221,7 @@ fn handle_commands(
vertex_attrs_updated_for_shader != *shader_program_obj_id
},
) {
- let Some(shader_program_vertex_desc) =
- &shader_program_metadata.vertex_desc
+ let Some(shader_program_vertex_desc) = &shader_info.vertex_desc
else {
tracing::error!(
"Activated shader program does not have a vertex description"
@@ -1799,9 +1845,12 @@ fn cube_map_texture_face_to_gl(face: CubeMapTextureFace) -> GlCubeMapTextureFace
fn create_shader_program(
current_context: &MaybeCurrentContextWithFns,
shader_program: &ShaderProgram,
+ backend_properties: &BackendProperties,
) -> Result<GlShaderProgram, CreateShaderError>
{
- let shader_program_reflection = shader_program.reflection(0).expect("Not possible");
+ let shader_program_reflection = shader_program
+ .reflection(backend_properties.shader_target_id)
+ .expect("Not possible");
let (vs_entry_point_index, vs_entry_point_reflection) = shader_program_reflection
.entry_points()
@@ -1812,9 +1861,12 @@ fn create_shader_program(
})?;
let vertex_shader_entry_point_code = shader_program
- .get_entry_point_code(vs_entry_point_index.try_into().expect(
- "Vertex shader entry point index does not fit in 32-bit unsigned int",
- ))
+ .get_entry_point_code(
+ vs_entry_point_index.try_into().expect(
+ "Vertex shader entry point index does not fit in 32-bit unsigned int",
+ ),
+ backend_properties.shader_target_id,
+ )
.map_err(|err| CreateShaderError::GetShaderEntryPointCodeFailed {
err,
stage: ShaderStage::Vertex,
@@ -1832,9 +1884,12 @@ fn create_shader_program(
})?;
let fragment_shader_entry_point_code = shader_program
- .get_entry_point_code(fs_entry_point_index.try_into().expect(
- "Fragment shader entry point index does not fit in 32-bit unsigned int",
- ))
+ .get_entry_point_code(
+ fs_entry_point_index.try_into().expect(
+ "Fragment shader entry point index does not fit in 32-bit unsigned int",
+ ),
+ backend_properties.shader_target_id,
+ )
.map_err(|err| CreateShaderError::GetShaderEntryPointCodeFailed {
err,
stage: ShaderStage::Fragment,
@@ -1888,6 +1943,9 @@ enum CreateShaderError
#[error("No entrypoint was found for shader stage {0:?}")]
NoShaderStageEntrypointFound(ShaderStage),
+ #[error("Failed to create shader vertex description")]
+ VertexDescCreationFailed(#[from] ShaderVertexDescCreationError),
+
#[error(transparent)]
ShaderError(#[from] GlShaderError),
}
diff --git a/engine/src/rendering/main_pipeline.rs b/engine/src/rendering/main_pipeline.rs
index 66030ab..754b507 100644
--- a/engine/src/rendering/main_pipeline.rs
+++ b/engine/src/rendering/main_pipeline.rs
@@ -42,6 +42,7 @@ use crate::rendering::shader::{
};
use crate::rendering::{
AssetOrValue,
+ BackendProperties,
BufferClearMask,
Command,
DepthFunction,
@@ -130,6 +131,7 @@ pub fn add_passes(
Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
)>,
shader_context: Single<ShaderContext>,
+ backend_properties: Single<BackendProperties>,
mut assets: Single<Assets>,
mut render_passes: Single<RenderPasses>,
mut object_store: Single<ObjectStore>,
@@ -142,6 +144,10 @@ pub fn add_passes(
let render_passes = render_passes.get_mut()?;
let object_store = object_store.get_mut()?;
+ let Ok(backend_properties) = backend_properties.get() else {
+ return Ok(());
+ };
+
let Some((scene_ent_id, (scene_env_lighting, scene_skybox, scene_skybox_state))) =
scene_query.iter_with_euids().next()
else {
@@ -186,7 +192,7 @@ pub fn add_passes(
let main_3d_shader_cursor = ShaderCursor::new(
shaders
.main_3d_shader_program
- .reflection(0)
+ .reflection(backend_properties.shader_target_id)
.context("Unable to get reflection for main 3D shader")?
.global_params_var_layout()
.ok_or_else(|| {
@@ -327,6 +333,7 @@ pub fn add_passes(
&window,
shaders.sky_box_shader_obj_id,
shaders.sky_box_shader_program,
+ &backend_properties,
)?;
}
}
@@ -820,6 +827,7 @@ fn add_sky_box_commands(
window: &Window,
sky_box_shader_obj_id: ObjectId,
sky_box_shader_program: &ShaderProgram,
+ backend_properties: &BackendProperties,
) -> Result<(), crate::Error>
{
render_pass
@@ -828,7 +836,7 @@ fn add_sky_box_commands(
let sky_box_shader_cursor = ShaderCursor::new(
sky_box_shader_program
- .reflection(0)
+ .reflection(backend_properties.shader_target_id)
.unwrap()
.global_params_var_layout()
.unwrap(),
diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs
index 3ba3a41..926302a 100644
--- a/engine/src/rendering/shader.rs
+++ b/engine/src/rendering/shader.rs
@@ -1,6 +1,5 @@
use std::any::type_name;
use std::borrow::Cow;
-use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Display, Write};
use std::path::Path;
use std::str::Utf8Error;
@@ -15,10 +14,7 @@ use shader_slang::{
EntryPoint as SlangEntryPoint,
GlobalSession as SlangGlobalSession,
Module as SlangModule,
- ParameterCategory as SlangParameterCategory,
- ScalarType as SlangScalarType,
Session as SlangSession,
- TypeKind as SlangTypeKind,
};
use crate::asset::{
@@ -39,6 +35,7 @@ use crate::ecs::{declare_entity, pair, Sole};
use crate::mesh::vertex_buffer::VertexLabel;
pub mod cursor;
+pub mod reflection;
pub const STD_VERT_IN_SEM_NAME_POSITION: &str = "STD_POSITION";
pub const STD_VERT_IN_SEM_NAME_NORMAL: &str = "STD_NORMAL";
@@ -113,68 +110,14 @@ pub struct EntryPoint
impl EntryPoint
{
#[must_use]
- pub fn function(&self) -> FunctionReflection<'_>
+ pub fn function_reflection(&self) -> self::reflection::Function<'_>
{
- FunctionReflection {
+ self::reflection::Function {
inner: self.inner.function_reflection(),
}
}
}
-pub struct FunctionReflection<'a>
-{
- inner: &'a shader_slang::reflection::Function,
-}
-
-impl FunctionReflection<'_>
-{
- #[must_use]
- pub fn name(&self) -> Option<&str>
- {
- self.inner.name()
- }
-}
-
-pub struct EntryPointReflection<'a>
-{
- inner: &'a shader_slang::reflection::EntryPoint,
-}
-
-impl<'a> EntryPointReflection<'a>
-{
- #[must_use]
- pub fn name(&self) -> Option<&str>
- {
- self.inner.name()
- }
-
- #[must_use]
- pub fn name_override(&self) -> Option<&str>
- {
- self.inner.name_override()
- }
-
- #[must_use]
- pub fn stage(&self) -> Stage
- {
- Stage::from_slang_stage(self.inner.stage())
- }
-
- #[must_use]
- pub fn parameters(&self) -> impl ExactSizeIterator<Item = VariableLayout<'a>>
- {
- self.inner
- .parameters()
- .map(|param| VariableLayout { inner: param })
- }
-
- #[must_use]
- pub fn var_layout(&self) -> Option<VariableLayout<'a>>
- {
- Some(VariableLayout { inner: self.inner.var_layout()? })
- }
-}
-
#[derive(Clone)]
pub struct Program
{
@@ -200,641 +143,61 @@ impl Program
&self.metadata
}
- pub fn get_entry_point_code(&self, entry_point_index: u32) -> Result<Blob, Error>
- {
- let blob = self.inner.entry_point_code(entry_point_index.into(), 0)?;
-
- Ok(Blob { inner: blob })
- }
-
- pub fn reflection(&self, target: u32) -> Result<ProgramReflection<'_>, Error>
- {
- let reflection = self.inner.layout(i64::from(target))?;
-
- Ok(ProgramReflection { inner: reflection })
- }
-}
-
-impl Debug for Program
-{
- fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
- {
- formatter
- .debug_struct(type_name::<Self>())
- .finish_non_exhaustive()
- }
-}
-
-pub struct ProgramReflection<'a>
-{
- inner: &'a shader_slang::reflection::Shader,
-}
-
-impl<'a> ProgramReflection<'a>
-{
- #[must_use]
- pub fn get_entry_point_by_index(&self, index: u32)
- -> Option<EntryPointReflection<'a>>
- {
- Some(EntryPointReflection {
- inner: self.inner.entry_point_by_index(index)?,
- })
- }
-
- #[must_use]
- pub fn get_entry_point_by_name(&self, name: &str)
- -> Option<EntryPointReflection<'a>>
- {
- Some(EntryPointReflection {
- inner: self.inner.find_entry_point_by_name(name)?,
- })
- }
-
- #[must_use]
- pub fn entry_points(
+ pub fn get_entry_point_code(
&self,
- ) -> impl ExactSizeIterator<Item = EntryPointReflection<'a>> + use<'a>
- {
- self.inner
- .entry_points()
- .map(|entry_point| EntryPointReflection { inner: entry_point })
- }
-
- #[must_use]
- pub fn global_params_type_layout(&self) -> Option<TypeLayout<'a>>
- {
- Some(TypeLayout {
- inner: self.inner.global_params_type_layout()?,
- })
- }
-
- #[must_use]
- pub fn global_params_var_layout(&self) -> Option<VariableLayout<'a>>
- {
- Some(VariableLayout {
- inner: self.inner.global_params_var_layout()?,
- })
- }
-
- #[must_use]
- pub fn get_type(&self, name: &str) -> Option<TypeReflection<'a>>
- {
- Some(TypeReflection {
- inner: self.inner.find_type_by_name(name)?,
- })
- }
-
- #[must_use]
- pub fn get_type_layout(&self, ty: &TypeReflection<'a>) -> Option<TypeLayout<'a>>
- {
- Some(TypeLayout {
- inner: self
- .inner
- .type_layout(ty.inner, shader_slang::LayoutRules::Default)?,
- })
- }
-}
-
-#[derive(Clone, Copy)]
-pub struct VariableLayout<'a>
-{
- inner: &'a shader_slang::reflection::VariableLayout,
-}
-
-impl<'a> VariableLayout<'a>
-{
- #[must_use]
- pub fn name(&self) -> Option<&'a str>
+ entry_point_index: u32,
+ target_id: CompileTargetId,
+ ) -> Result<Blob, Error>
{
- self.inner.name()
- }
-
- #[must_use]
- pub fn semantic_name(&self) -> Option<&str>
- {
- self.inner.semantic_name()
- }
-
- #[must_use]
- pub fn binding_index(&self) -> u32
- {
- self.inner
- .offset(shader_slang::ParameterCategory::DescriptorTableSlot) as u32
-
- // self.inner.binding_index()
- }
-
- #[must_use]
- pub fn varying_input_offset(&self) -> Option<usize>
- {
- if !self
+ let blob = self
.inner
- .categories()
- .any(|category| category == SlangParameterCategory::VaryingInput)
- {
- return None;
- }
-
- Some(self.inner.offset(SlangParameterCategory::VaryingInput))
- }
-
- #[must_use]
- pub fn binding_space(&self) -> u32
- {
- self.inner.binding_space()
- }
-
- #[must_use]
- pub fn semantic_index(&self) -> usize
- {
- self.inner.semantic_index()
- }
-
- #[must_use]
- pub fn offset(&self) -> usize
- {
- self.inner.offset(shader_slang::ParameterCategory::Uniform)
- }
-
- #[must_use]
- pub fn ty(&self) -> Option<TypeReflection<'a>>
- {
- self.inner.ty().map(|ty| TypeReflection { inner: ty })
- }
-
- #[must_use]
- pub fn type_layout(&self) -> Option<TypeLayout<'a>>
- {
- Some(TypeLayout { inner: self.inner.type_layout()? })
- }
-}
-
-#[derive(Clone, Copy)]
-pub struct TypeLayout<'a>
-{
- inner: &'a shader_slang::reflection::TypeLayout,
-}
-
-impl<'a> TypeLayout<'a>
-{
- #[must_use]
- pub fn kind(&self) -> TypeKind
- {
- TypeKind::from_slang_type_kind(self.inner.kind())
- }
+ .entry_point_code(entry_point_index.into(), i64::from(target_id.index))?;
- #[must_use]
- pub fn scalar_type(&self) -> Option<ScalarType>
- {
- Some(ScalarType::from_slang_scalar_type(
- self.inner.scalar_type()?,
- ))
- }
-
- #[must_use]
- pub fn resource_shape(&self) -> Option<ResourceShape>
- {
- Some(ResourceShape::from_bits_retain(
- self.inner.resource_shape()? as u32,
- ))
- }
-
- #[must_use]
- pub fn get_field_by_name(&self, name: &str) -> Option<VariableLayout<'a>>
- {
- let index = self.inner.find_field_index_by_name(name);
-
- if index < 0 {
- return None;
- }
-
- let index = u32::try_from(index.cast_unsigned()).expect("Should not happend");
-
- let field = self.inner.field_by_index(index)?;
-
- Some(VariableLayout { inner: field })
- }
-
- #[must_use]
- pub fn parameter_category(&self) -> ParameterCategory
- {
- ParameterCategory::from_slang_parameter_category(self.inner.parameter_category())
- }
-
- #[must_use]
- pub fn binding_range_descriptor_set_index(&self, index: i64) -> i64
- {
- self.inner.binding_range_descriptor_set_index(index)
- }
-
- #[must_use]
- pub fn get_field_binding_range_offset_by_name(&self, name: &str) -> Option<u64>
- {
- let field_index = self.inner.find_field_index_by_name(name);
-
- if field_index < 0 {
- return None;
- }
-
- let field_binding_range_offset =
- self.inner.field_binding_range_offset(field_index);
-
- if field_binding_range_offset < 0 {
- return None;
- }
-
- Some(field_binding_range_offset.cast_unsigned())
- }
-
- #[must_use]
- pub fn ty(&self) -> Option<TypeReflection<'a>>
- {
- self.inner.ty().map(|ty| TypeReflection { inner: ty })
- }
-
- #[must_use]
- pub fn fields(&self) -> FieldIter<'a>
- {
- FieldIter {
- type_layout: *self,
- cnt: self.field_cnt(),
- index: 0,
- }
- }
-
- #[must_use]
- pub fn field_cnt(&self) -> u32
- {
- self.inner.field_count()
- }
-
- #[must_use]
- pub fn element_cnt(&self) -> Option<usize>
- {
- self.inner.element_count()
- }
-
- #[must_use]
- pub fn row_cnt(&self) -> Option<u32>
- {
- self.inner.row_count()
- }
-
- #[must_use]
- pub fn column_cnt(&self) -> Option<u32>
- {
- self.inner.column_count()
- }
-
- #[must_use]
- pub fn element_type_layout(&self) -> Option<TypeLayout<'a>>
- {
- self.inner
- .element_type_layout()
- .map(|type_layout| TypeLayout { inner: type_layout })
+ Ok(Blob { inner: blob })
}
- #[must_use]
- pub fn element_var_layout(&self) -> Option<VariableLayout<'a>>
+ pub fn reflection(
+ &self,
+ target_id: CompileTargetId,
+ ) -> Result<self::reflection::Program<'_>, Error>
{
- self.inner
- .element_var_layout()
- .map(|var_layout| VariableLayout { inner: var_layout })
- }
+ let reflection = self.inner.layout(i64::from(target_id.index))?;
- #[must_use]
- pub fn container_var_layout(&self) -> Option<VariableLayout<'a>>
- {
- self.inner
- .container_var_layout()
- .map(|var_layout| VariableLayout { inner: var_layout })
+ Ok(self::reflection::Program { inner: reflection })
}
- #[must_use]
- pub fn uniform_size(&self) -> Option<usize>
+ pub fn create_vertex_desc(
+ &self,
+ target_id: CompileTargetId,
+ ) -> Result<VertexDescription, VertexDescCreationError>
{
- // tracing::debug!(
- // "uniform_size: {:?} categories: {:?}",
- // self.inner.name(),
- // self.inner.categories().collect::<Vec<_>>(),
- // );
-
if !self
- .inner
- .categories()
- .any(|category| category == SlangParameterCategory::Uniform)
+ .metadata
+ .link_entrypoints
+ .contains(EntrypointFlags::VERTEX)
{
- return None;
+ return Err(VertexDescCreationError::NoVertexStageEntrypointInProgram);
}
- // let category = self.inner.categories().next().unwrap();
-
- // println!(
- // "AARGH size Category: {category:?} Category count: {}",
- // self.inner.category_count()
- // );
+ let program_reflection = self
+ .reflection(target_id)
+ .map_err(VertexDescCreationError::GetProgramReflectionFailed)?;
- // Some(self.inner.size(category))
-
- Some(self.inner.size(SlangParameterCategory::Uniform))
- }
-
- #[must_use]
- pub fn stride(&self) -> usize
- {
- self.inner.stride(self.inner.categories().next().unwrap())
+ VertexDescription::new(
+ &program_reflection
+ .get_entry_point_by_name("vertex_main")
+ .expect("Not possible"),
+ )
}
}
-pub struct FieldIter<'a>
-{
- type_layout: TypeLayout<'a>,
- cnt: u32,
- index: u32,
-}
-
-impl<'a> Iterator for FieldIter<'a>
-{
- type Item = VariableLayout<'a>;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- if self.index == self.cnt {
- return None;
- }
-
- let Some(field) = self.type_layout.inner.field_by_index(self.index) else {
- unreachable!();
- };
-
- self.index += 1;
-
- Some(VariableLayout { inner: field })
- }
-
- fn size_hint(&self) -> (usize, Option<usize>)
- {
- let len = (self.cnt - self.index) as usize;
-
- (len, Some(len))
- }
-}
-
-impl ExactSizeIterator for FieldIter<'_>
-{
- fn len(&self) -> usize
- {
- (self.cnt - self.index) as usize
- }
-}
-
-impl DoubleEndedIterator for FieldIter<'_>
-{
- fn next_back(&mut self) -> Option<Self::Item>
- {
- if self.cnt == 0 || self.index == self.cnt - 1 {
- return None;
- }
-
- let Some(field) = self.type_layout.inner.field_by_index(self.cnt - 1) else {
- unreachable!();
- };
-
- self.cnt -= 1;
-
- Some(VariableLayout { inner: field })
- }
-}
-
-pub struct TypeReflection<'a>
-{
- inner: &'a shader_slang::reflection::Type,
-}
-
-impl TypeReflection<'_>
-{
- #[must_use]
- pub fn kind(&self) -> TypeKind
- {
- TypeKind::from_slang_type_kind(self.inner.kind())
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[non_exhaustive]
-pub enum TypeKind
-{
- None,
- Struct,
- Enum,
- Array,
- Matrix,
- Vector,
- Scalar,
- ConstantBuffer,
- Resource,
- SamplerState,
- TextureBuffer,
- ShaderStorageBuffer,
- ParameterBlock,
- GenericTypeParameter,
- Interface,
- OutputStream,
- MeshOutput,
- Specialized,
- Feedback,
- Pointer,
- DynamicResource,
- Count,
-}
-
-impl TypeKind
-{
- fn from_slang_type_kind(type_kind: SlangTypeKind) -> Self
- {
- match type_kind {
- SlangTypeKind::None => Self::None,
- SlangTypeKind::Struct => Self::Struct,
- SlangTypeKind::Enum => Self::Enum,
- SlangTypeKind::Array => Self::Array,
- SlangTypeKind::Matrix => Self::Matrix,
- SlangTypeKind::Vector => Self::Vector,
- SlangTypeKind::Scalar => Self::Scalar,
- SlangTypeKind::ConstantBuffer => Self::ConstantBuffer,
- SlangTypeKind::Resource => Self::Resource,
- SlangTypeKind::SamplerState => Self::SamplerState,
- SlangTypeKind::TextureBuffer => Self::TextureBuffer,
- SlangTypeKind::ShaderStorageBuffer => Self::ShaderStorageBuffer,
- SlangTypeKind::ParameterBlock => Self::ParameterBlock,
- SlangTypeKind::GenericTypeParameter => Self::GenericTypeParameter,
- SlangTypeKind::Interface => Self::Interface,
- SlangTypeKind::OutputStream => Self::OutputStream,
- SlangTypeKind::MeshOutput => Self::MeshOutput,
- SlangTypeKind::Specialized => Self::Specialized,
- SlangTypeKind::Feedback => Self::Feedback,
- SlangTypeKind::Pointer => Self::Pointer,
- SlangTypeKind::DynamicResource => Self::DynamicResource,
- SlangTypeKind::Count => Self::Count,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-#[non_exhaustive]
-pub enum ScalarType
-{
- None,
- Void,
- Bool,
- Int32,
- Uint32,
- Int64,
- Uint64,
- Float16,
- Float32,
- Float64,
- Int8,
- Uint8,
- Int16,
- Uint16,
- Intptr,
- Uintptr,
- Bfloat16,
- FloatE4m3,
- FloatE5m2,
-}
-
-impl ScalarType
-{
- fn from_slang_scalar_type(scalar_type: SlangScalarType) -> Self
- {
- match scalar_type {
- SlangScalarType::None => Self::None,
- SlangScalarType::Void => Self::Void,
- SlangScalarType::Bool => Self::Bool,
- SlangScalarType::Int32 => Self::Int32,
- SlangScalarType::Uint32 => Self::Uint32,
- SlangScalarType::Int64 => Self::Int64,
- SlangScalarType::Uint64 => Self::Uint64,
- SlangScalarType::Float16 => Self::Float16,
- SlangScalarType::Float32 => Self::Float32,
- SlangScalarType::Float64 => Self::Float64,
- SlangScalarType::Int8 => Self::Int8,
- SlangScalarType::Uint8 => Self::Uint8,
- SlangScalarType::Int16 => Self::Int16,
- SlangScalarType::Uint16 => Self::Uint16,
- SlangScalarType::Intptr => Self::Intptr,
- SlangScalarType::Uintptr => Self::Uintptr,
- SlangScalarType::Bfloat16 => Self::Bfloat16,
- SlangScalarType::FloatE4m3 => Self::FloatE4m3,
- SlangScalarType::FloatE5m2 => Self::FloatE5m2,
- #[allow(unreachable_patterns)]
- _ => unimplemented!("conversion from slang scalar type"),
- }
- }
-}
-
-bitflags! {
-#[derive(Debug, Copy, Clone)]
-pub struct ResourceShape: u32 {
- const BASE = shader_slang::ResourceShape::SlangResourceBaseShapeMask as u32;
- const NONE = shader_slang::ResourceShape::SlangResourceNone as u32;
- const TEXTURE_1D = shader_slang::ResourceShape::SlangTexture1d as u32;
- const TEXTURE_2D = shader_slang::ResourceShape::SlangTexture2d as u32;
- const TEXTURE_3D = shader_slang::ResourceShape::SlangTexture3d as u32;
- const TEXTURE_CUBE = shader_slang::ResourceShape::SlangTextureCube as u32;
- const TEXTURE_BUFFER = shader_slang::ResourceShape::SlangTextureBuffer as u32;
- const STRUCTURED_BUFFER = shader_slang::ResourceShape::SlangStructuredBuffer as u32;
- const BYTE_ADDRESS_BUFFER = shader_slang::ResourceShape::SlangByteAddressBuffer as u32;
- const UNKNOWN = shader_slang::ResourceShape::SlangResourceUnknown as u32;
- const ACCELERATION_STRUCTURE = shader_slang::ResourceShape::SlangAccelerationStructure as u32;
- const TEXTURE_SUBPASS = shader_slang::ResourceShape::SlangTextureSubpass as u32;
- const EXT_SHAPE = shader_slang::ResourceShape::SlangResourceExtShapeMask as u32;
- const TEXTURE_FEEDBACK_FLAG = shader_slang::ResourceShape::SlangTextureFeedbackFlag as u32;
- const TEXTURE_SHADOW_FLAG = shader_slang::ResourceShape::SlangTextureShadowFlag as u32;
- const TEXTURE_ARRAY_FLAG = shader_slang::ResourceShape::SlangTextureArrayFlag as u32;
- const TEXTURE_MULTISAMPLE_FLAG = shader_slang::ResourceShape::SlangTextureMultisampleFlag as u32;
- const TEXTURE_COMBINED_FLAG = shader_slang::ResourceShape::SlangTextureCombinedFlag as u32;
- const TEXTURE_1D_ARRAY = shader_slang::ResourceShape::SlangTexture1dArray as u32;
- const TEXTURE_2D_ARRAY = shader_slang::ResourceShape::SlangTexture2dArray as u32;
- const TEXTURE_CUBE_ARRAY = shader_slang::ResourceShape::SlangTextureCubeArray as u32;
- const TEXTURE_2D_MULTISAMPLE = shader_slang::ResourceShape::SlangTexture2dMultisample as u32;
- const TEXTURE_2D_MULTISAMPLE_ARRAY = shader_slang::ResourceShape::SlangTexture2dMultisampleArray as u32;
- const TEXTURE_SUBPASS_MULTISAMPLE = shader_slang::ResourceShape::SlangTextureSubpassMultisample as u32;
-}
-}
-
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
-pub enum ParameterCategory
-{
- None,
- Mixed,
- ConstantBuffer,
- ShaderResource,
- UnorderedAccess,
- VaryingInput,
- VaryingOutput,
- SamplerState,
- Uniform,
- DescriptorTableSlot,
- SpecializationConstant,
- PushConstantBuffer,
- RegisterSpace,
- Generic,
- RayPayload,
- HitAttributes,
- CallablePayload,
- ShaderRecord,
- ExistentialTypeParam,
- ExistentialObjectParam,
- SubElementRegisterSpace,
- Subpass,
- MetalArgumentBufferElement,
- MetalAttribute,
- MetalPayload,
- Count,
-}
-
-impl ParameterCategory
+impl Debug for Program
{
- fn from_slang_parameter_category(parameter_category: SlangParameterCategory) -> Self
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
- match parameter_category {
- SlangParameterCategory::None => Self::None,
- SlangParameterCategory::Mixed => Self::Mixed,
- SlangParameterCategory::ConstantBuffer => Self::ConstantBuffer,
- SlangParameterCategory::ShaderResource => Self::ShaderResource,
- SlangParameterCategory::UnorderedAccess => Self::UnorderedAccess,
- SlangParameterCategory::VaryingInput => Self::VaryingInput,
- SlangParameterCategory::VaryingOutput => Self::VaryingOutput,
- SlangParameterCategory::SamplerState => Self::SamplerState,
- SlangParameterCategory::Uniform => Self::Uniform,
- SlangParameterCategory::DescriptorTableSlot => Self::DescriptorTableSlot,
- SlangParameterCategory::SpecializationConstant => {
- Self::SpecializationConstant
- }
- SlangParameterCategory::PushConstantBuffer => Self::PushConstantBuffer,
- SlangParameterCategory::RegisterSpace => Self::RegisterSpace,
- SlangParameterCategory::Generic => Self::Generic,
- SlangParameterCategory::RayPayload => Self::RayPayload,
- SlangParameterCategory::HitAttributes => Self::HitAttributes,
- SlangParameterCategory::CallablePayload => Self::CallablePayload,
- SlangParameterCategory::ShaderRecord => Self::ShaderRecord,
- SlangParameterCategory::ExistentialTypeParam => Self::ExistentialTypeParam,
- SlangParameterCategory::ExistentialObjectParam => {
- Self::ExistentialObjectParam
- }
- SlangParameterCategory::SubElementRegisterSpace => {
- Self::SubElementRegisterSpace
- }
- SlangParameterCategory::Subpass => Self::Subpass,
- SlangParameterCategory::MetalArgumentBufferElement => {
- Self::MetalArgumentBufferElement
- }
- SlangParameterCategory::MetalAttribute => Self::MetalAttribute,
- SlangParameterCategory::MetalPayload => Self::MetalPayload,
- SlangParameterCategory::Count => Self::Count,
- }
+ formatter
+ .debug_struct(type_name::<Self>())
+ .finish_non_exhaustive()
}
}
@@ -851,61 +214,13 @@ impl Blob
self.inner.as_slice()
}
+ #[allow(clippy::missing_errors_doc)]
pub fn as_str(&self) -> Result<&str, Utf8Error>
{
self.inner.as_str()
}
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[non_exhaustive]
-pub enum Stage
-{
- None,
- Vertex,
- Hull,
- Domain,
- Geometry,
- Fragment,
- Compute,
- RayGeneration,
- Intersection,
- AnyHit,
- ClosestHit,
- Miss,
- Callable,
- Mesh,
- Amplification,
- Dispatch,
- Count,
-}
-
-impl Stage
-{
- fn from_slang_stage(stage: shader_slang::Stage) -> Self
- {
- match stage {
- shader_slang::Stage::None => Self::None,
- shader_slang::Stage::Vertex => Self::Vertex,
- shader_slang::Stage::Hull => Self::Hull,
- shader_slang::Stage::Domain => Self::Domain,
- shader_slang::Stage::Geometry => Self::Geometry,
- shader_slang::Stage::Fragment => Self::Fragment,
- shader_slang::Stage::Compute => Self::Compute,
- shader_slang::Stage::RayGeneration => Self::RayGeneration,
- shader_slang::Stage::Intersection => Self::Intersection,
- shader_slang::Stage::AnyHit => Self::AnyHit,
- shader_slang::Stage::ClosestHit => Self::ClosestHit,
- shader_slang::Stage::Miss => Self::Miss,
- shader_slang::Stage::Callable => Self::Callable,
- shader_slang::Stage::Mesh => Self::Mesh,
- shader_slang::Stage::Amplification => Self::Amplification,
- shader_slang::Stage::Dispatch => Self::Dispatch,
- shader_slang::Stage::Count => Self::Count,
- }
- }
-}
-
builder! {
#[builder(name = SettingsBuilder, derives=(Debug))]
#[derive(Debug)]
@@ -919,14 +234,63 @@ pub struct Settings
#[derive(Sole)]
pub struct Context
{
- _global_session: SlangGlobalSession,
- session: SlangSession,
- modules: HashMap<AssetId, Module>,
- programs: HashMap<AssetId, Program>,
+ global_session: SlangGlobalSession,
+ session: Option<SlangSession>,
+ modules: hashbrown::HashMap<AssetId, Module>,
+ programs: hashbrown::HashMap<AssetId, Program>,
}
impl Context
{
+ #[allow(clippy::missing_errors_doc)]
+ pub fn init_session(
+ &mut self,
+ compile_targets: &[&CompileTarget],
+ ) -> Result<Vec<CompileTargetId>, ContextSessionInitError>
+ {
+ if self.session.is_some() {
+ return Err(ContextSessionInitError::AlreadyInitialized);
+ }
+
+ if compile_targets.len() > u32::MAX as usize {
+ return Err(ContextSessionInitError::TooManyCompileTargets);
+ }
+
+ let session_options = shader_slang::CompilerOptions::default()
+ .optimization(shader_slang::OptimizationLevel::None)
+ .matrix_layout_row(true)
+ .debug_information(SlangDebugInfoLevel::Maximal)
+ .no_mangle(true);
+
+ let (compile_target_ids, target_descs) = compile_targets
+ .iter()
+ .enumerate()
+ .map(|(index, compile_target)| {
+ // The length of compile_targets is not greater than u32::MAX
+ #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
+ (
+ CompileTargetId { index: index as u32 },
+ shader_slang::TargetDesc::default()
+ .format(compile_target.format.into_slang()),
+ )
+ })
+ .collect::<(Vec<_>, Vec<_>)>();
+
+ let session_desc = shader_slang::SessionDesc::default()
+ .targets(&target_descs)
+ .search_paths([""])
+ .options(&session_options);
+
+ let Some(session) = self.global_session.create_session(&session_desc) else {
+ tracing::error!("Failed to create shader-slang session");
+ return Err(ContextSessionInitError::Internal);
+ };
+
+ self.session = Some(session);
+
+ Ok(compile_target_ids)
+ }
+
#[must_use]
pub fn get_module(&self, asset_id: &AssetId) -> Option<&Module>
{
@@ -946,6 +310,10 @@ impl Context
link_entrypoints: EntrypointFlags,
) -> Result<Program, ComposeProgramError>
{
+ let Some(session) = self.session.as_ref() else {
+ return Err(ComposeProgramError::ContextSessionNotInitialized);
+ };
+
let entry_points = link_entrypoints
.iter()
.filter_map(|entrypoint_flag| {
@@ -971,42 +339,63 @@ impl Context
.chain([SlangComponentType::from(module.inner)])
.collect::<Vec<_>>();
- let program = self
- .session
+ let program = session
.create_composite_component_type(&components)
.map_err(|err| ComposeProgramError::Other(Error(err)))?;
- let vertex_desc = if link_entrypoints.contains(EntrypointFlags::VERTEX) {
- // TODO: Do not have hard coded target here
- let Ok(program_reflection) = program.layout(0) else {
- unreachable!();
- };
-
- let program_reflection = ProgramReflection { inner: program_reflection };
-
- Some(VertexDescription::new(
- &program_reflection
- .get_entry_point_by_name("vertex_main")
- .expect("Not possible"),
- )?)
- } else {
- None
- };
-
Ok(Program {
inner: program,
- metadata: ProgramMetadata { vertex_desc },
+ metadata: ProgramMetadata { link_entrypoints },
})
}
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CompileTargetId
+{
+ index: u32,
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[non_exhaustive]
+pub struct CompileTarget
+{
+ pub format: CompileTargetFormat,
+}
+
+impl CompileTarget
+{
+ #[must_use]
+ pub fn format(mut self, format: CompileTargetFormat) -> Self
+ {
+ self.format = format;
+ self
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[non_exhaustive]
+pub enum CompileTargetFormat
+{
+ #[default]
+ Glsl,
+}
+
+impl CompileTargetFormat
+{
+ fn into_slang(self) -> shader_slang::CompileTarget
+ {
+ match self {
+ Self::Glsl => shader_slang::CompileTarget::Glsl,
+ }
+ }
+}
+
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ProgramMetadata
{
- /// If the program has a entry point in the vertex stage, this field will contain a
- /// description of the vertex type passed to the entry point.
- pub vertex_desc: Option<VertexDescription>,
+ pub link_entrypoints: EntrypointFlags,
}
#[derive(Debug, Clone)]
@@ -1019,16 +408,12 @@ pub struct VertexDescription
impl VertexDescription
{
#[tracing::instrument(skip_all, fields(vs_entry_point_name = vs_entrypoint.name()))]
- pub fn new(
- vs_entrypoint: &EntryPointReflection<'_>,
- ) -> Result<Self, VertexDescriptionError>
+ fn new(
+ vs_entrypoint: &self::reflection::EntryPointReflection<'_>,
+ ) -> Result<Self, VertexDescCreationError>
{
- if vs_entrypoint.stage() != Stage::Vertex {
- return Err(VertexDescriptionError::EntrypointNotInVertexStage);
- }
-
let inputs = VarInputDfsIter::new(vs_entrypoint).inspect(|var_input| {
- if var_input.type_layout.kind() != TypeKind::Struct {
+ if var_input.type_layout.kind() != self::reflection::TypeKind::Struct {
return;
}
@@ -1043,17 +428,19 @@ impl VertexDescription
);
});
- let mut seen_inputs = HashSet::<VertexInputSemName>::new();
+ let mut seen_inputs = hashbrown::HashSet::<VertexInputSemName>::new();
Ok(Self {
inputs: inputs
- .filter(|var_input| var_input.type_layout.kind() != TypeKind::Struct)
+ .filter(|var_input| {
+ var_input.type_layout.kind() != self::reflection::TypeKind::Struct
+ })
.map(|var_input| {
let name = var_input.var_layout.name().unwrap_or("<unnamed>");
let semantic_name =
var_input.var_layout.semantic_name().ok_or_else(|| {
- VertexDescriptionError::VertexInputMissingSemanticName {
+ VertexDescCreationError::VertexInputMissingSemanticName {
name: name.to_owned(),
}
})?;
@@ -1063,19 +450,26 @@ impl VertexDescription
if seen_inputs.contains(&semantic_name) {
return Err(
- VertexDescriptionError::VertexInputHasOccupiedSemanticName {
+ VertexDescCreationError::VertexInputHasOccupiedSemanticName {
name: name.to_owned(),
},
);
}
- let (TypeKind::Scalar | TypeKind::Vector, Some(scalar_type)) = (
+ let (
+ self::reflection::TypeKind::Scalar
+ | self::reflection::TypeKind::Vector,
+ Some(scalar_type),
+ ) = (
var_input.type_layout.kind(),
var_input.type_layout.scalar_type(),
- ) else {
- return Err(VertexDescriptionError::UnsupportedVertexInputType {
- name: name.to_owned(),
- });
+ )
+ else {
+ return Err(
+ VertexDescCreationError::UnsupportedVertexInputType {
+ name: name.to_owned(),
+ },
+ );
};
seen_inputs.insert(semantic_name.clone());
@@ -1099,8 +493,8 @@ pub struct VertexInputDescription
{
pub semantic_name: VertexInputSemName,
pub index: usize,
- pub type_kind: TypeKind,
- pub scalar_type: ScalarType,
+ pub type_kind: self::reflection::TypeKind,
+ pub scalar_type: self::reflection::ScalarType,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1166,10 +560,13 @@ impl Display for VertexInputSemName
}
#[derive(Debug, thiserror::Error)]
-pub enum VertexDescriptionError
+pub enum VertexDescCreationError
{
- #[error("Entrypoint is not in vertex stage")]
- EntrypointNotInVertexStage,
+ #[error("Shader program has no entry point in vertex stage")]
+ NoVertexStageEntrypointInProgram,
+
+ #[error("Failed to get shader program reflection")]
+ GetProgramReflectionFailed(#[source] Error),
#[error("Type of vertex input '{name}' is not supported")]
UnsupportedVertexInputType
@@ -1195,6 +592,9 @@ pub enum VertexDescriptionError
#[derive(Debug, thiserror::Error)]
pub enum ComposeProgramError
{
+ #[error("A shader context session has not been initialized")]
+ ContextSessionNotInitialized,
+
#[error("Shader module does not have a '{entrypoint_name}' entry point")]
EntrypointNotFoundError
{
@@ -1202,24 +602,41 @@ pub enum ComposeProgramError
},
#[error("Failed to create vertex description")]
- VertexDescriptionCreationFailed(#[from] VertexDescriptionError),
+ VertexDescriptionCreationFailed(#[from] VertexDescCreationError),
#[error(transparent)]
Other(Error),
}
#[derive(Debug, thiserror::Error)]
+pub enum ContextSessionInitError
+{
+ #[error("A shader context session has already been initialized")]
+ AlreadyInitialized,
+
+ #[error("Too many compile targets. Count would overflow a u32")]
+ TooManyCompileTargets,
+
+ #[error("An internal error occurred")]
+ Internal,
+}
+
+#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct Error(#[from] shader_slang::Error);
struct VarInputDfsIter<'a>
{
- stack: Vec<(VariableLayout<'a>, TypeLayout<'a>, usize)>,
+ stack: Vec<(
+ self::reflection::VariableLayout<'a>,
+ self::reflection::TypeLayout<'a>,
+ usize,
+ )>,
}
impl<'a> VarInputDfsIter<'a>
{
- fn new(entry_point: &EntryPointReflection<'a>) -> Self
+ fn new(entry_point: &self::reflection::EntryPointReflection<'a>) -> Self
{
Self {
stack: entry_point
@@ -1251,7 +668,7 @@ impl<'a> Iterator for VarInputDfsIter<'a>
}
};
- if type_layout.kind() == TypeKind::Struct {
+ if type_layout.kind() == self::reflection::TypeKind::Struct {
self.stack.extend(type_layout.fields().map(|field| {
let Some(field_type_layout) = field.type_layout() else {
// I do not know in which cases type_layout can return
@@ -1269,8 +686,8 @@ impl<'a> Iterator for VarInputDfsIter<'a>
struct VarInput<'a>
{
- var_layout: VariableLayout<'a>,
- type_layout: TypeLayout<'a>,
+ var_layout: self::reflection::VariableLayout<'a>,
+ type_layout: self::reflection::TypeLayout<'a>,
index: usize,
}
@@ -1300,9 +717,7 @@ fn import(
source: std::fs::read_to_string(file_path)
.map_err(ImportError::ReadFileFailed)?
.into(),
- link_entrypoints: settings
- .map(|settings| settings.link_entrypoints)
- .unwrap_or_default(),
+ link_entrypoints: settings.map_or_default(|settings| settings.link_entrypoints),
});
Ok(())
@@ -1338,36 +753,12 @@ pub(super) fn prepare(collector: &mut crate::ecs::extension::Collector<'_>)
return;
};
- let session_options = shader_slang::CompilerOptions::default()
- .optimization(shader_slang::OptimizationLevel::None)
- .matrix_layout_row(true)
- .debug_information(SlangDebugInfoLevel::Maximal)
- .no_mangle(true);
-
- let target_desc = shader_slang::TargetDesc::default()
- .format(shader_slang::CompileTarget::Glsl)
- // .format(shader_slang::CompileTarget::Spirv)
- .profile(global_session.find_profile("glsl_330"));
- // .profile(global_session.find_profile("spirv_1_5"));
-
- let targets = [target_desc];
-
- let session_desc = shader_slang::SessionDesc::default()
- .targets(&targets)
- .search_paths([""])
- .options(&session_options);
-
- let Some(session) = global_session.create_session(&session_desc) else {
- tracing::error!("Failed to create shader-slang session");
- return;
- };
-
collector
.add_sole(Context {
- _global_session: global_session,
- session,
- modules: HashMap::new(),
- programs: HashMap::new(),
+ global_session,
+ session: None,
+ modules: hashbrown::HashMap::new(),
+ programs: hashbrown::HashMap::new(),
})
.ok();
@@ -1400,6 +791,10 @@ fn load_modules(
let assets = assets.get()?;
let asset_events = asset_events.get()?;
+ let Some(session) = context.session.as_ref() else {
+ return Ok(());
+ };
+
for AssetEvent::Loaded(asset_id, asset_label) in asset_events.iter_last_tick() {
let asset_handle = AssetHandle::<ModuleSource>::from_id(*asset_id);
@@ -1413,7 +808,7 @@ fn load_modules(
tracing::debug!(asset_label=?asset_label, "Loading shader module");
- let module = match load_module(&context.session, module_source) {
+ let module = match load_module(session, module_source) {
Ok(module) => module,
Err(err) => {
tracing::error!("Failed to load shader module: {err}");
diff --git a/engine/src/rendering/shader/cursor.rs b/engine/src/rendering/shader/cursor.rs
index 24d0135..7750623 100644
--- a/engine/src/rendering/shader/cursor.rs
+++ b/engine/src/rendering/shader/cursor.rs
@@ -8,7 +8,7 @@ use crate::color::{Color, Rgb, Rgba};
use crate::data_types::matrix::Matrix;
use crate::data_types::vector::Vec3;
use crate::rendering::object::Id as RenderingObjectId;
-use crate::rendering::shader::{
+use crate::rendering::shader::reflection::{
ResourceShape,
ScalarType,
TypeKind,
diff --git a/engine/src/rendering/shader/reflection.rs b/engine/src/rendering/shader/reflection.rs
new file mode 100644
index 0000000..c6d9248
--- /dev/null
+++ b/engine/src/rendering/shader/reflection.rs
@@ -0,0 +1,713 @@
+use bitflags::bitflags;
+
+pub struct Function<'a>
+{
+ pub(super) inner: &'a shader_slang::reflection::Function,
+}
+
+impl Function<'_>
+{
+ #[must_use]
+ pub fn name(&self) -> Option<&str>
+ {
+ self.inner.name()
+ }
+}
+
+pub struct EntryPointReflection<'a>
+{
+ inner: &'a shader_slang::reflection::EntryPoint,
+}
+
+impl<'a> EntryPointReflection<'a>
+{
+ #[must_use]
+ pub fn name(&self) -> Option<&str>
+ {
+ self.inner.name()
+ }
+
+ #[must_use]
+ pub fn name_override(&self) -> Option<&str>
+ {
+ self.inner.name_override()
+ }
+
+ #[must_use]
+ pub fn stage(&self) -> Stage
+ {
+ Stage::from_slang_stage(self.inner.stage())
+ }
+
+ #[must_use]
+ pub fn parameters(&self) -> impl ExactSizeIterator<Item = VariableLayout<'a>>
+ {
+ self.inner
+ .parameters()
+ .map(|param| VariableLayout { inner: param })
+ }
+
+ #[must_use]
+ pub fn var_layout(&self) -> Option<VariableLayout<'a>>
+ {
+ Some(VariableLayout { inner: self.inner.var_layout()? })
+ }
+}
+
+pub struct Program<'a>
+{
+ pub(super) inner: &'a shader_slang::reflection::Shader,
+}
+
+impl<'a> Program<'a>
+{
+ #[must_use]
+ pub fn get_entry_point_by_index(&self, index: u32)
+ -> Option<EntryPointReflection<'a>>
+ {
+ Some(EntryPointReflection {
+ inner: self.inner.entry_point_by_index(index)?,
+ })
+ }
+
+ #[must_use]
+ pub fn get_entry_point_by_name(&self, name: &str)
+ -> Option<EntryPointReflection<'a>>
+ {
+ Some(EntryPointReflection {
+ inner: self.inner.find_entry_point_by_name(name)?,
+ })
+ }
+
+ #[must_use]
+ pub fn entry_points(
+ &self,
+ ) -> impl ExactSizeIterator<Item = EntryPointReflection<'a>> + use<'a>
+ {
+ self.inner
+ .entry_points()
+ .map(|entry_point| EntryPointReflection { inner: entry_point })
+ }
+
+ #[must_use]
+ pub fn global_params_type_layout(&self) -> Option<TypeLayout<'a>>
+ {
+ Some(TypeLayout {
+ inner: self.inner.global_params_type_layout()?,
+ })
+ }
+
+ #[must_use]
+ pub fn global_params_var_layout(&self) -> Option<VariableLayout<'a>>
+ {
+ Some(VariableLayout {
+ inner: self.inner.global_params_var_layout()?,
+ })
+ }
+
+ #[must_use]
+ pub fn get_type(&self, name: &str) -> Option<TypeReflection<'a>>
+ {
+ Some(TypeReflection {
+ inner: self.inner.find_type_by_name(name)?,
+ })
+ }
+
+ #[must_use]
+ pub fn get_type_layout(&self, ty: &TypeReflection<'a>) -> Option<TypeLayout<'a>>
+ {
+ Some(TypeLayout {
+ inner: self
+ .inner
+ .type_layout(ty.inner, shader_slang::LayoutRules::Default)?,
+ })
+ }
+}
+
+#[derive(Clone, Copy)]
+pub struct VariableLayout<'a>
+{
+ inner: &'a shader_slang::reflection::VariableLayout,
+}
+
+impl<'a> VariableLayout<'a>
+{
+ #[must_use]
+ pub fn name(&self) -> Option<&'a str>
+ {
+ self.inner.name()
+ }
+
+ #[must_use]
+ pub fn semantic_name(&self) -> Option<&str>
+ {
+ self.inner.semantic_name()
+ }
+
+ #[must_use]
+ pub fn binding_index(&self) -> u32
+ {
+ self.inner
+ .offset(shader_slang::ParameterCategory::DescriptorTableSlot) as u32
+
+ // self.inner.binding_index()
+ }
+
+ #[must_use]
+ pub fn varying_input_offset(&self) -> Option<usize>
+ {
+ if !self
+ .inner
+ .categories()
+ .any(|category| category == shader_slang::ParameterCategory::VaryingInput)
+ {
+ return None;
+ }
+
+ Some(
+ self.inner
+ .offset(shader_slang::ParameterCategory::VaryingInput),
+ )
+ }
+
+ #[must_use]
+ pub fn binding_space(&self) -> u32
+ {
+ self.inner.binding_space()
+ }
+
+ #[must_use]
+ pub fn semantic_index(&self) -> usize
+ {
+ self.inner.semantic_index()
+ }
+
+ #[must_use]
+ pub fn offset(&self) -> usize
+ {
+ self.inner.offset(shader_slang::ParameterCategory::Uniform)
+ }
+
+ #[must_use]
+ pub fn ty(&self) -> Option<TypeReflection<'a>>
+ {
+ self.inner.ty().map(|ty| TypeReflection { inner: ty })
+ }
+
+ #[must_use]
+ pub fn type_layout(&self) -> Option<TypeLayout<'a>>
+ {
+ Some(TypeLayout { inner: self.inner.type_layout()? })
+ }
+}
+
+#[derive(Clone, Copy)]
+pub struct TypeLayout<'a>
+{
+ inner: &'a shader_slang::reflection::TypeLayout,
+}
+
+impl<'a> TypeLayout<'a>
+{
+ #[must_use]
+ pub fn kind(&self) -> TypeKind
+ {
+ TypeKind::from_slang_type_kind(self.inner.kind())
+ }
+
+ #[must_use]
+ pub fn scalar_type(&self) -> Option<ScalarType>
+ {
+ Some(ScalarType::from_slang_scalar_type(
+ self.inner.scalar_type()?,
+ ))
+ }
+
+ #[must_use]
+ pub fn resource_shape(&self) -> Option<ResourceShape>
+ {
+ Some(ResourceShape::from_bits_retain(
+ self.inner.resource_shape()? as u32,
+ ))
+ }
+
+ #[must_use]
+ pub fn get_field_by_name(&self, name: &str) -> Option<VariableLayout<'a>>
+ {
+ let index = self.inner.find_field_index_by_name(name);
+
+ if index < 0 {
+ return None;
+ }
+
+ let index = u32::try_from(index.cast_unsigned()).expect("Should not happend");
+
+ let field = self.inner.field_by_index(index)?;
+
+ Some(VariableLayout { inner: field })
+ }
+
+ #[must_use]
+ pub fn parameter_category(&self) -> ParameterCategory
+ {
+ ParameterCategory::from_slang_parameter_category(self.inner.parameter_category())
+ }
+
+ #[must_use]
+ pub fn binding_range_descriptor_set_index(&self, index: i64) -> i64
+ {
+ self.inner.binding_range_descriptor_set_index(index)
+ }
+
+ #[must_use]
+ pub fn get_field_binding_range_offset_by_name(&self, name: &str) -> Option<u64>
+ {
+ let field_index = self.inner.find_field_index_by_name(name);
+
+ if field_index < 0 {
+ return None;
+ }
+
+ let field_binding_range_offset =
+ self.inner.field_binding_range_offset(field_index);
+
+ if field_binding_range_offset < 0 {
+ return None;
+ }
+
+ Some(field_binding_range_offset.cast_unsigned())
+ }
+
+ #[must_use]
+ pub fn ty(&self) -> Option<TypeReflection<'a>>
+ {
+ self.inner.ty().map(|ty| TypeReflection { inner: ty })
+ }
+
+ #[must_use]
+ pub fn fields(&self) -> FieldIter<'a>
+ {
+ FieldIter {
+ type_layout: *self,
+ cnt: self.field_cnt(),
+ index: 0,
+ }
+ }
+
+ #[must_use]
+ pub fn field_cnt(&self) -> u32
+ {
+ self.inner.field_count()
+ }
+
+ #[must_use]
+ pub fn element_cnt(&self) -> Option<usize>
+ {
+ self.inner.element_count()
+ }
+
+ #[must_use]
+ pub fn row_cnt(&self) -> Option<u32>
+ {
+ self.inner.row_count()
+ }
+
+ #[must_use]
+ pub fn column_cnt(&self) -> Option<u32>
+ {
+ self.inner.column_count()
+ }
+
+ #[must_use]
+ pub fn element_type_layout(&self) -> Option<TypeLayout<'a>>
+ {
+ self.inner
+ .element_type_layout()
+ .map(|type_layout| TypeLayout { inner: type_layout })
+ }
+
+ #[must_use]
+ pub fn element_var_layout(&self) -> Option<VariableLayout<'a>>
+ {
+ self.inner
+ .element_var_layout()
+ .map(|var_layout| VariableLayout { inner: var_layout })
+ }
+
+ #[must_use]
+ pub fn container_var_layout(&self) -> Option<VariableLayout<'a>>
+ {
+ self.inner
+ .container_var_layout()
+ .map(|var_layout| VariableLayout { inner: var_layout })
+ }
+
+ #[must_use]
+ pub fn uniform_size(&self) -> Option<usize>
+ {
+ if !self
+ .inner
+ .categories()
+ .any(|category| category == shader_slang::ParameterCategory::Uniform)
+ {
+ return None;
+ }
+
+ Some(self.inner.size(shader_slang::ParameterCategory::Uniform))
+ }
+
+ #[must_use]
+ pub fn stride(&self) -> usize
+ {
+ self.inner.stride(self.inner.categories().next().unwrap())
+ }
+}
+
+pub struct FieldIter<'a>
+{
+ type_layout: TypeLayout<'a>,
+ cnt: u32,
+ index: u32,
+}
+
+impl<'a> Iterator for FieldIter<'a>
+{
+ type Item = VariableLayout<'a>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ if self.index == self.cnt {
+ return None;
+ }
+
+ let Some(field) = self.type_layout.inner.field_by_index(self.index) else {
+ unreachable!();
+ };
+
+ self.index += 1;
+
+ Some(VariableLayout { inner: field })
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>)
+ {
+ let len = (self.cnt - self.index) as usize;
+
+ (len, Some(len))
+ }
+}
+
+impl ExactSizeIterator for FieldIter<'_>
+{
+ fn len(&self) -> usize
+ {
+ (self.cnt - self.index) as usize
+ }
+}
+
+impl DoubleEndedIterator for FieldIter<'_>
+{
+ fn next_back(&mut self) -> Option<Self::Item>
+ {
+ if self.cnt == 0 || self.index == self.cnt - 1 {
+ return None;
+ }
+
+ let Some(field) = self.type_layout.inner.field_by_index(self.cnt - 1) else {
+ unreachable!();
+ };
+
+ self.cnt -= 1;
+
+ Some(VariableLayout { inner: field })
+ }
+}
+
+pub struct TypeReflection<'a>
+{
+ inner: &'a shader_slang::reflection::Type,
+}
+
+impl TypeReflection<'_>
+{
+ #[must_use]
+ pub fn kind(&self) -> TypeKind
+ {
+ TypeKind::from_slang_type_kind(self.inner.kind())
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum TypeKind
+{
+ None,
+ Struct,
+ Enum,
+ Array,
+ Matrix,
+ Vector,
+ Scalar,
+ ConstantBuffer,
+ Resource,
+ SamplerState,
+ TextureBuffer,
+ ShaderStorageBuffer,
+ ParameterBlock,
+ GenericTypeParameter,
+ Interface,
+ OutputStream,
+ MeshOutput,
+ Specialized,
+ Feedback,
+ Pointer,
+ DynamicResource,
+ Count,
+}
+
+impl TypeKind
+{
+ fn from_slang_type_kind(type_kind: shader_slang::TypeKind) -> Self
+ {
+ match type_kind {
+ shader_slang::TypeKind::None => Self::None,
+ shader_slang::TypeKind::Struct => Self::Struct,
+ shader_slang::TypeKind::Enum => Self::Enum,
+ shader_slang::TypeKind::Array => Self::Array,
+ shader_slang::TypeKind::Matrix => Self::Matrix,
+ shader_slang::TypeKind::Vector => Self::Vector,
+ shader_slang::TypeKind::Scalar => Self::Scalar,
+ shader_slang::TypeKind::ConstantBuffer => Self::ConstantBuffer,
+ shader_slang::TypeKind::Resource => Self::Resource,
+ shader_slang::TypeKind::SamplerState => Self::SamplerState,
+ shader_slang::TypeKind::TextureBuffer => Self::TextureBuffer,
+ shader_slang::TypeKind::ShaderStorageBuffer => Self::ShaderStorageBuffer,
+ shader_slang::TypeKind::ParameterBlock => Self::ParameterBlock,
+ shader_slang::TypeKind::GenericTypeParameter => Self::GenericTypeParameter,
+ shader_slang::TypeKind::Interface => Self::Interface,
+ shader_slang::TypeKind::OutputStream => Self::OutputStream,
+ shader_slang::TypeKind::MeshOutput => Self::MeshOutput,
+ shader_slang::TypeKind::Specialized => Self::Specialized,
+ shader_slang::TypeKind::Feedback => Self::Feedback,
+ shader_slang::TypeKind::Pointer => Self::Pointer,
+ shader_slang::TypeKind::DynamicResource => Self::DynamicResource,
+ shader_slang::TypeKind::Count => Self::Count,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[non_exhaustive]
+pub enum ScalarType
+{
+ None,
+ Void,
+ Bool,
+ Int32,
+ Uint32,
+ Int64,
+ Uint64,
+ Float16,
+ Float32,
+ Float64,
+ Int8,
+ Uint8,
+ Int16,
+ Uint16,
+ Intptr,
+ Uintptr,
+ Bfloat16,
+ FloatE4m3,
+ FloatE5m2,
+}
+
+impl ScalarType
+{
+ fn from_slang_scalar_type(scalar_type: shader_slang::ScalarType) -> Self
+ {
+ match scalar_type {
+ shader_slang::ScalarType::None => Self::None,
+ shader_slang::ScalarType::Void => Self::Void,
+ shader_slang::ScalarType::Bool => Self::Bool,
+ shader_slang::ScalarType::Int32 => Self::Int32,
+ shader_slang::ScalarType::Uint32 => Self::Uint32,
+ shader_slang::ScalarType::Int64 => Self::Int64,
+ shader_slang::ScalarType::Uint64 => Self::Uint64,
+ shader_slang::ScalarType::Float16 => Self::Float16,
+ shader_slang::ScalarType::Float32 => Self::Float32,
+ shader_slang::ScalarType::Float64 => Self::Float64,
+ shader_slang::ScalarType::Int8 => Self::Int8,
+ shader_slang::ScalarType::Uint8 => Self::Uint8,
+ shader_slang::ScalarType::Int16 => Self::Int16,
+ shader_slang::ScalarType::Uint16 => Self::Uint16,
+ shader_slang::ScalarType::Intptr => Self::Intptr,
+ shader_slang::ScalarType::Uintptr => Self::Uintptr,
+ shader_slang::ScalarType::Bfloat16 => Self::Bfloat16,
+ shader_slang::ScalarType::FloatE4m3 => Self::FloatE4m3,
+ shader_slang::ScalarType::FloatE5m2 => Self::FloatE5m2,
+ #[allow(unreachable_patterns)]
+ _ => unimplemented!("conversion from slang scalar type"),
+ }
+ }
+}
+
+bitflags! {
+#[derive(Debug, Copy, Clone)]
+pub struct ResourceShape: u32 {
+ const BASE = shader_slang::ResourceShape::SlangResourceBaseShapeMask as u32;
+ const NONE = shader_slang::ResourceShape::SlangResourceNone as u32;
+ const TEXTURE_1D = shader_slang::ResourceShape::SlangTexture1d as u32;
+ const TEXTURE_2D = shader_slang::ResourceShape::SlangTexture2d as u32;
+ const TEXTURE_3D = shader_slang::ResourceShape::SlangTexture3d as u32;
+ const TEXTURE_CUBE = shader_slang::ResourceShape::SlangTextureCube as u32;
+ const TEXTURE_BUFFER = shader_slang::ResourceShape::SlangTextureBuffer as u32;
+ const STRUCTURED_BUFFER = shader_slang::ResourceShape::SlangStructuredBuffer as u32;
+ const BYTE_ADDRESS_BUFFER = shader_slang::ResourceShape::SlangByteAddressBuffer as u32;
+ const UNKNOWN = shader_slang::ResourceShape::SlangResourceUnknown as u32;
+ const ACCELERATION_STRUCTURE = shader_slang::ResourceShape::SlangAccelerationStructure as u32;
+ const TEXTURE_SUBPASS = shader_slang::ResourceShape::SlangTextureSubpass as u32;
+ const EXT_SHAPE = shader_slang::ResourceShape::SlangResourceExtShapeMask as u32;
+ const TEXTURE_FEEDBACK_FLAG = shader_slang::ResourceShape::SlangTextureFeedbackFlag as u32;
+ const TEXTURE_SHADOW_FLAG = shader_slang::ResourceShape::SlangTextureShadowFlag as u32;
+ const TEXTURE_ARRAY_FLAG = shader_slang::ResourceShape::SlangTextureArrayFlag as u32;
+ const TEXTURE_MULTISAMPLE_FLAG = shader_slang::ResourceShape::SlangTextureMultisampleFlag as u32;
+ const TEXTURE_COMBINED_FLAG = shader_slang::ResourceShape::SlangTextureCombinedFlag as u32;
+ const TEXTURE_1D_ARRAY = shader_slang::ResourceShape::SlangTexture1dArray as u32;
+ const TEXTURE_2D_ARRAY = shader_slang::ResourceShape::SlangTexture2dArray as u32;
+ const TEXTURE_CUBE_ARRAY = shader_slang::ResourceShape::SlangTextureCubeArray as u32;
+ const TEXTURE_2D_MULTISAMPLE = shader_slang::ResourceShape::SlangTexture2dMultisample as u32;
+ const TEXTURE_2D_MULTISAMPLE_ARRAY = shader_slang::ResourceShape::SlangTexture2dMultisampleArray as u32;
+ const TEXTURE_SUBPASS_MULTISAMPLE = shader_slang::ResourceShape::SlangTextureSubpassMultisample as u32;
+}
+}
+
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum ParameterCategory
+{
+ None,
+ Mixed,
+ ConstantBuffer,
+ ShaderResource,
+ UnorderedAccess,
+ VaryingInput,
+ VaryingOutput,
+ SamplerState,
+ Uniform,
+ DescriptorTableSlot,
+ SpecializationConstant,
+ PushConstantBuffer,
+ RegisterSpace,
+ Generic,
+ RayPayload,
+ HitAttributes,
+ CallablePayload,
+ ShaderRecord,
+ ExistentialTypeParam,
+ ExistentialObjectParam,
+ SubElementRegisterSpace,
+ Subpass,
+ MetalArgumentBufferElement,
+ MetalAttribute,
+ MetalPayload,
+ Count,
+}
+
+impl ParameterCategory
+{
+ fn from_slang_parameter_category(
+ parameter_category: shader_slang::ParameterCategory,
+ ) -> Self
+ {
+ match parameter_category {
+ shader_slang::ParameterCategory::None => Self::None,
+ shader_slang::ParameterCategory::Mixed => Self::Mixed,
+ shader_slang::ParameterCategory::ConstantBuffer => Self::ConstantBuffer,
+ shader_slang::ParameterCategory::ShaderResource => Self::ShaderResource,
+ shader_slang::ParameterCategory::UnorderedAccess => Self::UnorderedAccess,
+ shader_slang::ParameterCategory::VaryingInput => Self::VaryingInput,
+ shader_slang::ParameterCategory::VaryingOutput => Self::VaryingOutput,
+ shader_slang::ParameterCategory::SamplerState => Self::SamplerState,
+ shader_slang::ParameterCategory::Uniform => Self::Uniform,
+ shader_slang::ParameterCategory::DescriptorTableSlot => {
+ Self::DescriptorTableSlot
+ }
+ shader_slang::ParameterCategory::SpecializationConstant => {
+ Self::SpecializationConstant
+ }
+ shader_slang::ParameterCategory::PushConstantBuffer => {
+ Self::PushConstantBuffer
+ }
+ shader_slang::ParameterCategory::RegisterSpace => Self::RegisterSpace,
+ shader_slang::ParameterCategory::Generic => Self::Generic,
+ shader_slang::ParameterCategory::RayPayload => Self::RayPayload,
+ shader_slang::ParameterCategory::HitAttributes => Self::HitAttributes,
+ shader_slang::ParameterCategory::CallablePayload => Self::CallablePayload,
+ shader_slang::ParameterCategory::ShaderRecord => Self::ShaderRecord,
+ shader_slang::ParameterCategory::ExistentialTypeParam => {
+ Self::ExistentialTypeParam
+ }
+ shader_slang::ParameterCategory::ExistentialObjectParam => {
+ Self::ExistentialObjectParam
+ }
+ shader_slang::ParameterCategory::SubElementRegisterSpace => {
+ Self::SubElementRegisterSpace
+ }
+ shader_slang::ParameterCategory::Subpass => Self::Subpass,
+ shader_slang::ParameterCategory::MetalArgumentBufferElement => {
+ Self::MetalArgumentBufferElement
+ }
+ shader_slang::ParameterCategory::MetalAttribute => Self::MetalAttribute,
+ shader_slang::ParameterCategory::MetalPayload => Self::MetalPayload,
+ shader_slang::ParameterCategory::Count => Self::Count,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum Stage
+{
+ None,
+ Vertex,
+ Hull,
+ Domain,
+ Geometry,
+ Fragment,
+ Compute,
+ RayGeneration,
+ Intersection,
+ AnyHit,
+ ClosestHit,
+ Miss,
+ Callable,
+ Mesh,
+ Amplification,
+ Dispatch,
+ Count,
+}
+
+impl Stage
+{
+ fn from_slang_stage(stage: shader_slang::Stage) -> Self
+ {
+ match stage {
+ shader_slang::Stage::None => Self::None,
+ shader_slang::Stage::Vertex => Self::Vertex,
+ shader_slang::Stage::Hull => Self::Hull,
+ shader_slang::Stage::Domain => Self::Domain,
+ shader_slang::Stage::Geometry => Self::Geometry,
+ shader_slang::Stage::Fragment => Self::Fragment,
+ shader_slang::Stage::Compute => Self::Compute,
+ shader_slang::Stage::RayGeneration => Self::RayGeneration,
+ shader_slang::Stage::Intersection => Self::Intersection,
+ shader_slang::Stage::AnyHit => Self::AnyHit,
+ shader_slang::Stage::ClosestHit => Self::ClosestHit,
+ shader_slang::Stage::Miss => Self::Miss,
+ shader_slang::Stage::Callable => Self::Callable,
+ shader_slang::Stage::Mesh => Self::Mesh,
+ shader_slang::Stage::Amplification => Self::Amplification,
+ shader_slang::Stage::Dispatch => Self::Dispatch,
+ shader_slang::Stage::Count => Self::Count,
+ }
+ }
+}
diff --git a/engine/src/ui/dear_imgui.rs b/engine/src/ui/dear_imgui.rs
index 0047394..b3a102b 100644
--- a/engine/src/ui/dear_imgui.rs
+++ b/engine/src/ui/dear_imgui.rs
@@ -9,7 +9,6 @@ use dear_imgui_rs::{
MouseButton as ImguiMouseButton,
};
use ecs::component::local::Local;
-use ecs::component::Component;
use ecs::error::Context as _;
use ecs::event::component::{Changed, EventMatchExt};
use ecs::pair::Pair;
@@ -56,6 +55,7 @@ use crate::rendering::shader::{
};
use crate::rendering::{
AssetOrValue,
+ BackendProperties as RenderingBackendProperties,
Command as RenderingCommand,
DrawMeshOptions,
DrawProperties,
@@ -211,7 +211,7 @@ fn handle_window_changed(
let Some(event_match) = observe
.iter()
- .find(|event_match| event_match.get_entity().has_component(TargetWindow::id()))
+ .find(|event_match| event_match.get_entity().has_component::<TargetWindow>())
else {
return;
};
@@ -244,6 +244,7 @@ fn update(
mut assets: Single<Assets>,
mut render_passes: Single<RenderPasses>,
shader_context: Single<ShaderContext>,
+ rendering_backend_properties: Single<RenderingBackendProperties>,
keyboard: Single<Keyboard>,
mouse: Single<Mouse>,
mouse_buttons: Single<MouseButtons>,
@@ -261,6 +262,10 @@ fn update(
let mouse = mouse.get()?;
let mouse_buttons = mouse_buttons.get()?;
+ let Ok(rendering_backend_properties) = rendering_backend_properties.get() else {
+ return Ok(());
+ };
+
let Some((window, window_surface)) = target_window_query.iter().next() else {
return Ok(());
};
@@ -302,6 +307,7 @@ fn update(
context,
render_passes,
shader_context,
+ rendering_backend_properties,
window_surface.id,
shader_asset.clone(),
mesh_obj_id,
@@ -479,6 +485,7 @@ fn add_drawing_render_pass(
context: &mut Context,
render_passes: &mut RenderPasses,
shader_context: &ShaderContext,
+ rendering_backend_properties: &RenderingBackendProperties,
window_surface_id: SurfaceId,
shader_asset: AssetHandle<ShaderModuleSource>,
mesh_obj_id: RenderingObjectId,
@@ -507,7 +514,7 @@ fn add_drawing_render_pass(
let shader_cursor = ShaderCursor::new(
shader_program
- .reflection(0)
+ .reflection(rendering_backend_properties.shader_target_id)
.unwrap()
.global_params_var_layout()
.unwrap(),
diff --git a/engine/src/ui/view/world.rs b/engine/src/ui/view/world.rs
index 411acd7..bf92859 100644
--- a/engine/src/ui/view/world.rs
+++ b/engine/src/ui/view/world.rs
@@ -4,15 +4,15 @@ use std::io::Cursor;
use std::ops::{Deref, DerefMut};
use ecs::error::Context;
+use ecs::phase::Phase;
+use ecs::query::term::{With, Without};
+use ecs::sole::Is as IsSole;
+use ecs::system::Function as SystemFunction;
use crate::color::{Rgb, Rgba};
use crate::ecs::actions::Actions;
use crate::ecs::component::local::Local;
-use crate::ecs::component::{
- Handle as ComponentHandle,
- Info as ComponentInfo,
- Parts as ComponentParts,
-};
+use crate::ecs::component::{Info as ComponentInfo, Parts as ComponentParts};
use crate::ecs::entity::{Handle as EntityHandle, Name as EntityName};
use crate::ecs::sole::Single;
use crate::ecs::uid::Uid;
@@ -47,7 +47,13 @@ pub struct State
}
pub fn show(
- query: Query<()>,
+ non_sole_query: Query<(
+ Without<ComponentInfo>,
+ Without<Phase>,
+ Without<SystemFunction>,
+ Without<IsSole>,
+ )>,
+ sole_query: Query<(With<IsSole>,)>,
mut dear_imgui_context: Single<DearImguiContext>,
mut state: Local<State>,
world: &World,
@@ -84,7 +90,18 @@ pub fn show(
frame.spacing();
frame.spacing();
- for ent_handle in &query.into_flexible_query() {
+ for ent_handle in &non_sole_query.into_flexible_query() {
+ create_entity_widgets(
+ frame,
+ popup_state,
+ icon_textures,
+ &ent_handle,
+ world,
+ &mut actions,
+ );
+ }
+
+ for ent_handle in &sole_query.into_flexible_query() {
create_entity_widgets(
frame,
popup_state,
@@ -169,18 +186,6 @@ fn create_entity_widgets(
actions: &mut Actions,
)
{
- if ent_handle.component_ids().next().is_some()
- && !ent_handle.component_ids().any(|comp_id| {
- world.get_entity(comp_id).is_some_and(|comp_ent| {
- comp_ent
- .get::<ComponentInfo>()
- .is_some_and(|comp_info| comp_info.type_reflection.is_some())
- })
- })
- {
- return;
- }
-
let ent_title = create_entity_title(ent_handle);
let mut is_open = false;
@@ -266,14 +271,6 @@ fn create_component_widgets(
return;
};
- let Some(component_type) = component_info.type_reflection else {
- return;
- };
-
- let Some(mut component) = ent_handle.get_any_by_id_mut(component_id) else {
- unreachable!();
- };
-
{
let _color_token = frame.push_style_color(
dear_imgui_bindings::StyleColor::Button,
@@ -301,42 +298,43 @@ fn create_component_widgets(
frame.same_line();
- let mut component_changed = false;
+ frame.text(component_info.name);
- create_item_title_widget(
- frame,
- component_info.name,
- &ItemType::Reflected(component_type),
- None,
- );
+ if let Some(component_type) = component_info.type_reflection {
+ let item_type = ItemType::Reflected(component_type);
- let item_type = ItemType::Reflected(component_type);
+ let _token = item_type.indent_or_same_line(frame);
- let _token = item_type.indent_or_same_line(frame);
+ let Some(mut component) = ent_handle.get_any_by_id_mut(component_id) else {
+ unreachable!();
+ };
- add_item_to_frame(
- frame,
- ItemRef::Mutable(&mut *component),
- &mut component_changed,
- ItemInfo {
- title: component_info.name,
- tag: &format!(
- "world_view_entity_{}_component_{}",
- ent_handle.uid(),
- component_info.name,
- ),
- type_name: None,
- ty: item_type,
- is_read_only: false,
- },
- &[],
- );
+ let mut component_changed = false;
- frame.spacing();
+ add_item_to_frame(
+ frame,
+ ItemRef::Mutable(&mut *component),
+ &mut component_changed,
+ ItemInfo {
+ title: component_info.name,
+ tag: &format!(
+ "world_view_entity_{}_component_{}",
+ ent_handle.uid(),
+ component_info.name,
+ ),
+ type_name: None,
+ ty: item_type,
+ is_read_only: false,
+ },
+ &[],
+ );
- if component_changed {
- component.set_changed();
+ if component_changed {
+ component.set_changed();
+ }
}
+
+ frame.spacing();
}
fn create_pair_component_widgets(
@@ -375,22 +373,8 @@ fn create_pair_component_widgets(
frame.same_line();
- let relation_comp_info_or_ent_name =
- get_comp_info_or_ent_name(world, pair_id.relation());
-
- let relation_name: Cow<'_, str> = match &relation_comp_info_or_ent_name {
- Some(Either::A(comp_info)) => comp_info.name.into(),
- Some(Either::B(ent_name)) => format!("🌐 {}", ent_name.name).into(),
- None => format!("<unnamed> {}", pair_id.relation()).into(),
- };
-
- let target_comp_info_or_ent_name = get_comp_info_or_ent_name(world, pair_id.target());
-
- let target_name: Cow<'_, str> = match &target_comp_info_or_ent_name {
- Some(Either::A(comp_info)) => comp_info.name.into(),
- Some(Either::B(ent_name)) => format!("🌐 {}", ent_name.name).into(),
- None => format!("<unnamed> {}", pair_id.target()).into(),
- };
+ let relation_name = get_comp_name_or_ent_name(world, pair_id.relation());
+ let target_name = get_comp_name_or_ent_name(world, pair_id.target());
// TODO: Make relation & target ids editable
@@ -402,15 +386,14 @@ fn create_pair_component_widgets(
let pair_data_ty_id = (*pair_data).type_id();
- let Some(pair_data_comp_info) = [
- &relation_comp_info_or_ent_name,
- &target_comp_info_or_ent_name,
- ]
- .map(Option::as_ref)
- .map(|item| item.and_then(Either::as_a))
- .into_iter()
- .flatten()
- .find(|comp_info| comp_info.ty_id == pair_data_ty_id) else {
+ let pair_member_comp_infos = [pair_id.relation(), pair_id.target()]
+ .map(|member_id| world.get_entity(member_id)?.get::<ComponentInfo>());
+
+ let Some(pair_data_comp_info) = pair_member_comp_infos
+ .into_iter()
+ .filter_map(std::convert::identity)
+ .find(|comp_info| comp_info.ty_id == pair_data_ty_id)
+ else {
return;
};
@@ -465,22 +448,21 @@ fn create_pair_component_widgets(
// pairs as event targets
}
-fn get_comp_info_or_ent_name(
- world: &World,
- id: Uid,
-) -> Option<Either<ComponentHandle<'_, ComponentInfo>, ComponentHandle<'_, EntityName>>>
+fn get_comp_name_or_ent_name(world: &World, id: Uid) -> Cow<'static, str>
{
- let ent = world.get_entity(id)?;
+ let Some(ent) = world.get_entity(id) else {
+ return format!("<unnamed> {id}").into();
+ };
if let Some(component_info) = ent.get::<ComponentInfo>() {
- return Some(Either::A(component_info));
+ return component_info.name.into();
}
if let Some(entity_name) = ent.get::<EntityName>() {
- return Some(Either::B(entity_name));
+ return format!("🌐 {}", entity_name.name).into();
}
- None
+ format!("🌐 <unnamed> {id}").into()
}
fn create_add_component_button_widget(
@@ -506,10 +488,24 @@ fn create_add_component_button_widget(
[90.0, 162.0, 51.0, 255.0].map(|num| num / 255.0),
);
- let is_button_clicked = frame.button(format!(
- "+##add_component_to_entity_button_{}",
- ent_handle.uid()
- ));
+ let ent_is_sole = ent_handle.has_component::<IsSole>();
+
+ let is_button_clicked = {
+ let _disabled_token = frame.begin_disabled_with_cond(ent_is_sole);
+
+ frame.button(format!(
+ "+##add_component_to_entity_button_{}",
+ ent_handle.uid()
+ ))
+ };
+
+ if ent_is_sole {
+ if frame.is_item_hovered_with_flags(
+ dear_imgui_bindings::ItemHoveredFlags::ALLOW_WHEN_DISABLED,
+ ) {
+ frame.tooltip_text("Cannot add a component to the entity of a sole");
+ }
+ }
if is_button_clicked {
enter_add_component_popup_state(popup_state, ent_handle, ent_title, world);
diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs
index 3e27075..b76d942 100644
--- a/engine/src/windowing.rs
+++ b/engine/src/windowing.rs
@@ -23,7 +23,6 @@ use winit::keyboard::PhysicalKey;
use winit::window::{Window as WinitWindow, WindowId as WinitWindowId};
use crate::ecs::actions::Actions;
-use crate::ecs::component::Component;
use crate::ecs::event::component::{Added, Changed, EventMatchExt, Removed};
use crate::ecs::pair::{ChildOf, Pair};
use crate::ecs::phase::{Phase, PRE_UPDATE as PRE_UPDATE_PHASE};
@@ -90,7 +89,7 @@ impl crate::ecs::extension::Extension for Extension
collector.spawn_declared_entity(&PHASE);
- collector.add_system(*PHASE, update_stuff);
+ collector.add_system(*PHASE, update);
collector.add_observer(handle_window_changed);
collector.add_observer(handle_window_removed);
@@ -112,7 +111,7 @@ fn handle_window_creation_ready(
unreachable!();
};
- if ent.has_component(Window::id()) || ent.has_component(WindowClosed::id()) {
+ if ent.has_component::<Window>() || ent.has_component::<WindowClosed>() {
continue;
}
@@ -128,7 +127,7 @@ fn handle_window_creation_ready(
}
#[tracing::instrument(skip_all)]
-fn update_stuff(
+fn update(
mut context: Single<Context>,
mut keyboard: Single<Keyboard>,
mut mouse: Single<Mouse>,
@@ -153,7 +152,7 @@ fn update_stuff(
update_inputs(context, keyboard, mouse, mouse_buttons);
- receive_app_messages(context, &mut actions, world);
+ handle_app_messages(context, &mut actions, world);
if context.shared_state.thread_panicked.load(Ordering::Relaxed) {
cold_path();
@@ -181,7 +180,7 @@ fn update_stuff(
Ok(())
}
-fn receive_app_messages(context: &mut Context, actions: &mut Actions, world: &World)
+fn handle_app_messages(context: &mut Context, actions: &mut Actions, world: &World)
{
let Context {
ref mut windows, ref shared_state, ..