render: add RenderPass queries. move ui to its own pass

This commit is contained in:
Carter Anderson 2020-07-28 20:11:27 -07:00
parent bd8e979de8
commit 2929197d9b
8 changed files with 109 additions and 36 deletions

View File

@ -1,6 +1,7 @@
use crate::ArchetypeAccess;
use hecs::{
Archetype, Component, ComponentError, Entity, Fetch, Query as HecsQuery, Ref, RefMut, World,
Archetype, Component, ComponentError, Entity, Fetch, Query as HecsQuery, QueryOne, Ref, RefMut,
World,
};
use std::marker::PhantomData;
@ -11,10 +12,11 @@ pub struct Query<'a, Q: HecsQuery> {
}
#[derive(Debug)]
pub enum QueryComponentError {
pub enum QueryError {
CannotReadArchetype,
CannotWriteArchetype,
ComponentError(ComponentError),
NoSuchEntity,
}
impl<'a, Q: HecsQuery> Query<'a, Q> {
@ -34,7 +36,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
/// Gets a reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type or if the given component type does not match this query.
pub fn get<T: Component>(&self, entity: Entity) -> Result<Ref<'_, T>, QueryComponentError> {
pub fn get<T: Component>(&self, entity: Entity) -> Result<Ref<'_, T>, QueryError> {
if let Some(location) = self.world.get_entity_location(entity) {
if self
.archetype_access
@ -47,23 +49,38 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
{
self.world
.get(entity)
.map_err(|err| QueryComponentError::ComponentError(err))
.map_err(|err| QueryError::ComponentError(err))
} else {
Err(QueryComponentError::CannotReadArchetype)
Err(QueryError::CannotReadArchetype)
}
} else {
Err(QueryComponentError::ComponentError(
ComponentError::NoSuchEntity,
))
Err(QueryError::ComponentError(ComponentError::NoSuchEntity))
}
}
pub fn entity(&self, entity: Entity) -> Result<QueryOne<'_, Q>, QueryError> {
if let Some(location) = self.world.get_entity_location(entity) {
if self
.archetype_access
.immutable
.contains(location.archetype as usize)
|| self
.archetype_access
.mutable
.contains(location.archetype as usize)
{
Ok(self.world.query_one(entity).unwrap())
} else {
Err(QueryError::CannotReadArchetype)
}
} else {
Err(QueryError::NoSuchEntity)
}
}
/// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type or if the given component type does not match this query.
pub fn get_mut<T: Component>(
&self,
entity: Entity,
) -> Result<RefMut<'_, T>, QueryComponentError> {
pub fn get_mut<T: Component>(&self, entity: Entity) -> Result<RefMut<'_, T>, QueryError> {
if let Some(location) = self.world.get_entity_location(entity) {
if self
.archetype_access
@ -72,14 +89,12 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
{
self.world
.get_mut(entity)
.map_err(|err| QueryComponentError::ComponentError(err))
.map_err(|err| QueryError::ComponentError(err))
} else {
Err(QueryComponentError::CannotWriteArchetype)
Err(QueryError::CannotWriteArchetype)
}
} else {
Err(QueryComponentError::ComponentError(
ComponentError::NoSuchEntity,
))
Err(QueryError::ComponentError(ComponentError::NoSuchEntity))
}
}
@ -89,11 +104,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
/// Sets the entity's component to the given value. This will fail if the entity does not already have
/// the given component type or if the given component type does not match this query.
pub fn set<T: Component>(
&self,
entity: Entity,
component: T,
) -> Result<(), QueryComponentError> {
pub fn set<T: Component>(&self, entity: Entity, component: T) -> Result<(), QueryError> {
let mut current = self.get_mut::<T>(entity)?;
*current = component;
Ok(())

View File

@ -4,7 +4,7 @@ use bevy_ecs::Bundle;
use bevy_render::{
draw::Draw,
mesh::Mesh,
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines},
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines}, prelude::MainPass,
};
use bevy_transform::prelude::{Rotation, Scale, Transform, Translation};
@ -12,6 +12,7 @@ use bevy_transform::prelude::{Rotation, Scale, Transform, Translation};
pub struct PbrComponents {
pub mesh: Handle<Mesh>,
pub material: Handle<StandardMaterial>,
pub main_pass: MainPass,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
@ -43,6 +44,7 @@ impl Default for PbrComponents {
)]),
mesh: Default::default(),
material: Default::default(),
main_pass: Default::default(),
draw: Default::default(),
transform: Default::default(),
translation: Default::default(),

View File

@ -8,11 +8,15 @@ use bevy_asset::Handle;
use bevy_ecs::Bundle;
use bevy_transform::components::{Rotation, Scale, Transform, Translation};
#[derive(Default)]
pub struct MainPass;
#[derive(Bundle, Default)]
pub struct MeshComponents {
pub mesh: Handle<Mesh>,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub main_pass: MainPass,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,

View File

@ -7,6 +7,7 @@ use crate::{
LoadOp, Operations, PassDescriptor, RenderPassColorAttachmentDescriptor,
RenderPassDepthStencilAttachmentDescriptor, TextureAttachment,
},
prelude::MainPass,
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
Color,
};
@ -89,7 +90,7 @@ impl BaseRenderGraphBuilder for RenderGraph {
}
if config.add_main_pass {
let mut main_pass_node = PassNode::new(PassDescriptor {
let mut main_pass_node = PassNode::<&MainPass>::new(PassDescriptor {
color_attachments: vec![RenderPassColorAttachmentDescriptor {
attachment: TextureAttachment::Input("color".to_string()),
resolve_target: None,

View File

@ -11,14 +11,15 @@ use crate::{
},
};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{Resources, World};
use bevy_ecs::{Resources, World, HecsQuery};
use std::marker::PhantomData;
struct CameraInfo {
name: String,
bind_group_id: Option<BindGroupId>,
}
pub struct PassNode {
pub struct PassNode<Q: HecsQuery> {
descriptor: PassDescriptor,
inputs: Vec<ResourceSlotInfo>,
cameras: Vec<CameraInfo>,
@ -26,9 +27,10 @@ pub struct PassNode {
depth_stencil_attachment_input_index: Option<usize>,
default_clear_color_inputs: Vec<usize>,
camera_bind_group_descriptor: BindGroupDescriptor,
_marker: PhantomData<Q>,
}
impl PassNode {
impl<Q: HecsQuery> PassNode<Q> {
pub fn new(descriptor: PassDescriptor) -> Self {
let mut inputs = Vec::new();
let mut color_attachment_input_indices = Vec::new();
@ -75,6 +77,7 @@ impl PassNode {
depth_stencil_attachment_input_index,
default_clear_color_inputs: Vec::new(),
camera_bind_group_descriptor,
_marker: PhantomData::default(),
}
}
@ -90,7 +93,7 @@ impl PassNode {
}
}
impl Node for PassNode {
impl<Q: HecsQuery + Send + Sync + 'static> Node for PassNode<Q> {
fn input(&self) -> &[ResourceSlotInfo] {
&self.inputs
}
@ -167,6 +170,13 @@ impl Node for PassNode {
// attempt to draw each visible entity
let mut draw_state = DrawState::default();
for visible_entity in visible_entities.iter() {
if let Ok(mut query_one) = world.query_one::<Q>(visible_entity.entity) {
if query_one.get().is_none() {
// visible entity does not match the Pass query
continue;
}
}
let draw = if let Ok(draw) = world.get::<Draw>(visible_entity.entity) {
draw
} else {

View File

@ -7,7 +7,7 @@ use bevy_ecs::Bundle;
use bevy_render::{
mesh::Mesh,
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines},
prelude::Draw,
prelude::{Draw, MainPass},
};
use bevy_transform::prelude::{Rotation, Scale, Transform, Translation};
@ -16,6 +16,7 @@ pub struct SpriteComponents {
pub sprite: Sprite,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
pub material: Handle<ColorMaterial>,
pub main_pass: MainPass,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
@ -51,6 +52,7 @@ impl Default for SpriteComponents {
..Default::default()
},
sprite: Default::default(),
main_pass: MainPass,
material: Default::default(),
transform: Default::default(),
translation: Default::default(),
@ -66,6 +68,7 @@ pub struct SpriteSheetComponents {
pub texture_atlas: Handle<TextureAtlas>,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub main_pass: MainPass,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
pub transform: Transform,
pub translation: Translation,
@ -99,6 +102,7 @@ impl Default for SpriteSheetComponents {
..Default::default()
},
mesh: QUAD_HANDLE,
main_pass: MainPass,
sprite: Default::default(),
texture_atlas: Default::default(),
transform: Default::default(),

View File

@ -4,9 +4,9 @@ use bevy_ecs::Resources;
use bevy_render::{
camera::ActiveCameras,
pipeline::*,
render_graph::{base, CameraNode, PassNode, RenderGraph, RenderResourcesNode},
render_graph::{base, CameraNode, PassNode, RenderGraph, RenderResourcesNode, WindowSwapChainNode, WindowTextureNode},
shader::{Shader, ShaderStage, ShaderStages},
texture::TextureFormat,
texture::TextureFormat, prelude::{Color, MainPass}, pass::{RenderPassColorAttachmentDescriptor, PassDescriptor, TextureAttachment, LoadOp, Operations, RenderPassDepthStencilAttachmentDescriptor},
};
pub const UI_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
@ -60,6 +60,7 @@ pub fn build_ui_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
pub mod node {
pub const UI_CAMERA: &'static str = "ui_camera";
pub const NODE: &'static str = "node";
pub const UI_PASS: &'static str = "ui_pass";
}
pub mod camera {
@ -76,16 +77,56 @@ impl UiRenderGraphBuilder for RenderGraph {
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
pipelines.set(UI_PIPELINE_HANDLE, build_ui_pipeline(&mut shaders));
let mut ui_pass_node = PassNode::<&Node>::new(PassDescriptor {
color_attachments: vec![RenderPassColorAttachmentDescriptor {
attachment: TextureAttachment::Input("color".to_string()),
resolve_target: None,
ops: Operations {
load: LoadOp::Load,
store: true,
},
}],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor {
attachment: TextureAttachment::Input("depth".to_string()),
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
sample_count: 1,
});
ui_pass_node.add_camera(camera::UI_CAMERA);
self.add_node(node::UI_PASS, ui_pass_node);
self.add_slot_edge(
base::node::PRIMARY_SWAP_CHAIN,
WindowSwapChainNode::OUT_TEXTURE,
node::UI_PASS,
"color",
)
.unwrap();
self.add_slot_edge(
base::node::MAIN_DEPTH_TEXTURE,
WindowTextureNode::OUT_TEXTURE,
node::UI_PASS,
"depth",
)
.unwrap();
// ensure ui pass runs after main pass
self.add_node_edge(base::node::MAIN_PASS, node::UI_PASS)
.unwrap();
// setup ui camera
self.add_system_node(node::UI_CAMERA, CameraNode::new(camera::UI_CAMERA));
self.add_node_edge(node::UI_CAMERA, base::node::MAIN_PASS)
self.add_node_edge(node::UI_CAMERA, node::UI_PASS)
.unwrap();
self.add_system_node(node::NODE, RenderResourcesNode::<Node>::new(true));
self.add_node_edge(node::NODE, base::node::MAIN_PASS)
self.add_node_edge(node::NODE, node::UI_PASS)
.unwrap();
let mut active_cameras = resources.get_mut::<ActiveCameras>().unwrap();
let main_pass_node: &mut PassNode = self.get_node_mut(base::node::MAIN_PASS).unwrap();
main_pass_node.add_camera(camera::UI_CAMERA);
active_cameras.add(camera::UI_CAMERA);
self
}

View File

@ -63,7 +63,7 @@ fn setup(
render_graph.add_system_node("secondary_camera", CameraNode::new("Secondary"));
// add a new render pass for our new camera
let mut second_window_pass = PassNode::new(PassDescriptor {
let mut second_window_pass = PassNode::<&MainPass>::new(PassDescriptor {
color_attachments: vec![RenderPassColorAttachmentDescriptor {
attachment: TextureAttachment::Input("color".to_string()),
resolve_target: None,