render: "Immediate Mode" draw api

This replaces Renderable with Draw/RenderPipelines components and makes various aspects of the renderer much simpler and legible
This commit is contained in:
Carter Anderson 2020-06-09 23:16:48 -07:00
parent 3ccaebf9a5
commit 3d07fbdc81
34 changed files with 767 additions and 873 deletions

View file

@ -1,7 +1,7 @@
use crate::{light::Light, material::StandardMaterial};
use bevy_asset::Handle;
use bevy_derive::EntityArchetype;
use bevy_render::{mesh::Mesh, Renderable};
use bevy_render::{mesh::Mesh, draw::{RenderPipelines, Draw}};
use bevy_transform::prelude::{Transform, Rotation, Scale, Translation};
#[derive(EntityArchetype, Default)]
@ -10,7 +10,8 @@ pub struct MeshEntity {
pub mesh: Handle<Mesh>,
// #[tag]
pub material: Handle<StandardMaterial>,
pub renderable: Renderable,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,

View file

@ -4,7 +4,7 @@ use bevy_render::{
base_render_graph,
pipeline::PipelineDescriptor,
render_graph::{
nodes::{AssetUniformNode, PassNode, UniformNode},
nodes::{AssetUniformNode, UniformNode},
RenderGraph,
},
shader::Shader,
@ -36,12 +36,7 @@ impl ForwardPbrRenderGraphBuilder for RenderGraph {
self.add_system_node(node::LIGHTS, LightsNode::new(10));
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
{
let main_pass: &mut PassNode = self
.get_node_mut(base_render_graph::node::MAIN_PASS)
.unwrap();
main_pass.add_pipeline(pipelines.add_default(build_forward_pipeline(&mut shaders)));
}
pipelines.add_default(build_forward_pipeline(&mut shaders));
// TODO: replace these with "autowire" groups
self.add_node_edge(node::STANDARD_MATERIAL, base_render_graph::node::MAIN_PASS)

View file

@ -4,7 +4,7 @@ use crate::{
RenderPassDepthStencilAttachmentDescriptor, StoreOp, TextureAttachment,
},
render_graph::{
nodes::{CameraNode, PassNode, TextureCopyNode, WindowSwapChainNode, WindowTextureNode},
nodes::{CameraNode, MainPassNode, TextureCopyNode, WindowSwapChainNode, WindowTextureNode},
RenderGraph,
},
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
@ -95,7 +95,7 @@ impl BaseRenderGraphBuilder for RenderGraph {
if config.add_main_pass {
self.add_node(
node::MAIN_PASS,
PassNode::new(PassDescriptor {
MainPassNode::new(PassDescriptor {
color_attachments: vec![RenderPassColorAttachmentDescriptor {
attachment: TextureAttachment::Input("color".to_string()),
resolve_target: None,

View file

@ -1,41 +1,221 @@
use crate::{render_resource::RenderResourceId, pipeline::PipelineDescriptor};
use bevy_asset::Handle;
use std::ops::Range;
use crate::{
pipeline::{BindGroupDescriptor, BindGroupDescriptorId, PipelineDescriptor},
render_resource::{
RenderResourceAssignments, RenderResourceId, RenderResourceSet, RenderResourceSetId,
ResourceInfo,
},
renderer::{RenderResourceContext, RenderResources},
};
use bevy_asset::{Assets, Handle};
use bevy_property::Properties;
use legion::{
prelude::{Com, ComMut, Res},
storage::Component,
};
use std::{ops::Range, sync::Arc};
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DrawType {
Instanced {
pub enum RenderCommand {
SetPipeline {
pipeline: Handle<PipelineDescriptor>,
},
SetVertexBuffer {
slot: u32,
buffer: RenderResourceId,
offset: u64,
},
SetIndexBuffer {
buffer: RenderResourceId,
offset: u64,
},
SetBindGroup {
index: u32,
bind_group_descriptor: BindGroupDescriptorId,
render_resource_set: RenderResourceSetId,
dynamic_uniform_indices: Option<Arc<Vec<u32>>>,
},
DrawIndexed {
indices: Range<u32>,
base_vertex: i32,
instances: Range<u32>,
},
}
#[derive(Properties)]
pub struct Draw {
pub is_visible: bool,
#[property(ignore)]
pub render_commands: Vec<RenderCommand>,
}
impl Default for Draw {
fn default() -> Self {
Self {
is_visible: true,
render_commands: Default::default(),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct VertexBufferBinding {
pub slot: u32,
pub vertex_buffer: RenderResourceId,
pub offset: u64,
#[derive(Properties)]
pub struct RenderPipelines {
pub pipelines: Vec<Handle<PipelineDescriptor>>,
// TODO: make these pipeline specific
#[property(ignore)]
pub render_resource_assignments: RenderResourceAssignments,
#[property(ignore)]
pub compiled_pipelines: Vec<Handle<PipelineDescriptor>>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct IndexBufferBinding {
pub vertex_buffer: RenderResourceId,
pub offset: u64,
impl Default for RenderPipelines {
fn default() -> Self {
Self {
render_resource_assignments: Default::default(),
compiled_pipelines: Default::default(),
pipelines: vec![Handle::default()],
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct BindGroupBinding {
pub vertex_buffer: RenderResourceId,
pub offset: u64,
impl Draw {
pub fn get_context<'a>(
&'a mut self,
pipelines: &'a Assets<PipelineDescriptor>,
render_resource_context: &'a dyn RenderResourceContext,
render_resource_assignments: &'a RenderResourceAssignments,
) -> DrawContext {
DrawContext {
draw: self,
pipelines,
render_resource_context,
render_resource_assignments,
}
}
pub fn clear_render_commands(&mut self) {
self.render_commands.clear();
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DrawCall {
pub pipeline: Handle<PipelineDescriptor>,
pub draw_type: DrawType,
pub vertex_buffers: Vec<VertexBufferBinding>,
pub index_buffer: Option<IndexBufferBinding>,
pub struct DrawContext<'a> {
pub draw: &'a mut Draw,
pub pipelines: &'a Assets<PipelineDescriptor>,
pub render_resource_context: &'a dyn RenderResourceContext,
pub render_resource_assignments: &'a RenderResourceAssignments,
}
pub struct Draw {}
impl<'a> DrawContext<'a> {
pub fn set_pipeline(&mut self, pipeline: Handle<PipelineDescriptor>) {
self.render_command(RenderCommand::SetPipeline { pipeline });
}
pub fn set_vertex_buffer(&mut self, slot: u32, buffer: RenderResourceId, offset: u64) {
self.render_command(RenderCommand::SetVertexBuffer {
slot,
buffer,
offset,
});
}
pub fn set_index_buffer(&mut self, buffer: RenderResourceId, offset: u64) {
self.render_command(RenderCommand::SetIndexBuffer { buffer, offset });
}
pub fn set_bind_group(
&mut self,
bind_group_descriptor: &BindGroupDescriptor,
render_resource_set: &RenderResourceSet,
) {
self.render_command(RenderCommand::SetBindGroup {
index: bind_group_descriptor.index,
bind_group_descriptor: bind_group_descriptor.id,
render_resource_set: render_resource_set.id,
dynamic_uniform_indices: render_resource_set.dynamic_uniform_indices.clone(),
});
}
pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
self.render_command(RenderCommand::DrawIndexed {
base_vertex,
indices,
instances,
});
}
#[inline]
pub fn render_command(&mut self, render_command: RenderCommand) {
self.draw.render_commands.push(render_command);
}
pub fn draw<T: Drawable>(&mut self, drawable: &T) {
drawable.draw(self);
}
}
pub trait Drawable {
fn draw(&self, draw: &mut DrawContext);
}
impl Drawable for RenderPipelines {
fn draw(&self, draw: &mut DrawContext) {
for pipeline_handle in self.compiled_pipelines.iter() {
let pipeline = draw.pipelines.get(pipeline_handle).unwrap();
let layout = pipeline.get_layout().unwrap();
draw.set_pipeline(*pipeline_handle);
for bind_group in layout.bind_groups.iter() {
if let Some(local_render_resource_set) = self
.render_resource_assignments
.get_bind_group_render_resource_set(bind_group.id)
{
draw.set_bind_group(bind_group, local_render_resource_set);
} else if let Some(global_render_resource_set) = draw
.render_resource_assignments
.get_bind_group_render_resource_set(bind_group.id)
{
draw.set_bind_group(bind_group, global_render_resource_set);
}
}
let mut indices = 0..0;
for (slot, vertex_buffer_descriptor) in
layout.vertex_buffer_descriptors.iter().enumerate()
{
if let Some((vertex_buffer, index_buffer)) = self
.render_resource_assignments
.get_vertex_buffer(&vertex_buffer_descriptor.name)
{
draw.set_vertex_buffer(slot as u32, vertex_buffer, 0);
if let Some(index_buffer) = index_buffer {
draw.render_resource_context.get_resource_info(
index_buffer,
&mut |resource_info| match resource_info {
Some(ResourceInfo::Buffer(Some(buffer_info))) => {
indices = 0..(buffer_info.size / 2) as u32;
}
_ => panic!("expected a buffer type"),
},
);
draw.set_index_buffer(index_buffer, 0);
}
}
}
draw.draw_indexed(indices, 0, 0..1);
}
}
}
pub fn draw_system<T: Drawable + Component>(
pipelines: Res<Assets<PipelineDescriptor>>,
render_resource_assignments: Res<RenderResourceAssignments>,
render_resources: Res<RenderResources>,
mut draw: ComMut<Draw>,
drawable: Com<T>,
) {
let context = &*render_resources.context;
let mut draw_context = draw.get_context(&pipelines, context, &render_resource_assignments);
draw_context.draw(drawable.as_ref());
}
pub fn clear_draw_system(mut draw: ComMut<Draw>) {
draw.clear_render_commands();
}

View file

@ -1,6 +1,6 @@
use crate::{
base_render_graph, mesh::Mesh, Camera, OrthographicProjection, PerspectiveProjection,
Renderable, WindowOrigin,
RenderPipelines, WindowOrigin, draw::Draw,
};
use bevy_asset::Handle;
use bevy_derive::EntityArchetype;
@ -10,7 +10,8 @@ use bevy_transform::components::{Transform, Rotation, Scale, Translation};
pub struct MeshMaterialEntity<T: Default + Send + Sync + 'static> {
pub mesh: Handle<Mesh>,
pub material: Handle<T>,
pub renderable: Renderable,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,

View file

@ -12,7 +12,6 @@ mod color;
pub use camera::*;
pub use color::*;
pub use renderable::*;
pub use vertex::Vertex;
@ -20,20 +19,14 @@ pub mod base_render_graph;
pub mod pass;
pub mod pipeline;
pub mod render_resource;
mod renderable;
pub mod texture;
pub use once_cell;
use self::{
mesh::Mesh,
pipeline::{
PipelineAssignments, PipelineCompiler, PipelineDescriptor, VertexBufferDescriptors,
},
render_resource::{
entity_render_resource_assignments_system, EntityRenderResourceAssignments,
RenderResourceAssignments,
},
pipeline::{PipelineCompiler, PipelineDescriptor, VertexBufferDescriptors},
render_resource::RenderResourceAssignments,
shader::Shader,
texture::Texture,
};
@ -42,6 +35,7 @@ use base_render_graph::{BaseRenderGraphBuilder, BaseRenderGraphConfig};
use bevy_app::{AppBuilder, AppPlugin};
use bevy_asset::AddAsset;
use bevy_type_registry::RegisterType;
use draw::{clear_draw_system, Draw, RenderPipelines};
use legion::prelude::IntoSystem;
use mesh::mesh_resource_provider_system;
use render_graph::RenderGraph;
@ -51,6 +45,7 @@ use texture::{PngTextureLoader, TextureResourceSystemState};
pub mod stage {
pub static RENDER_RESOURCE: &str = "render_resource";
pub static PRE_RENDER: &str = "pre_render";
pub static RENDER: &str = "render";
}
@ -70,30 +65,27 @@ impl Default for RenderPlugin {
impl AppPlugin for RenderPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_stage_after(bevy_asset::stage::ASSET_EVENTS, stage::RENDER_RESOURCE)
.add_stage_after(stage::RENDER_RESOURCE, stage::RENDER)
.add_stage_after(stage::RENDER_RESOURCE, stage::PRE_RENDER)
.add_stage_after(stage::PRE_RENDER, stage::RENDER)
.add_asset::<Mesh>()
.add_asset::<Texture>()
.add_asset::<Shader>()
.add_asset::<PipelineDescriptor>()
.add_asset_loader::<Texture, PngTextureLoader>()
.register_component::<Camera>()
.register_component::<Draw>()
.register_component::<RenderPipelines>()
.register_component::<OrthographicProjection>()
.register_component::<PerspectiveProjection>()
.register_component::<Renderable>()
.register_property_type::<Color>()
.register_property_type::<Range<f32>>()
.init_resource::<RenderGraph>()
.init_resource::<PipelineAssignments>()
.init_resource::<PipelineCompiler>()
.init_resource::<RenderResourceAssignments>()
.init_resource::<VertexBufferDescriptors>()
.init_resource::<EntityRenderResourceAssignments>()
.init_resource::<EntitiesWaitingForAssets>()
.init_resource::<TextureResourceSystemState>()
.add_system_to_stage(
bevy_app::stage::POST_UPDATE,
entity_render_resource_assignments_system(),
)
.add_system_to_stage(bevy_app::stage::PRE_UPDATE, clear_draw_system.system())
.init_system_to_stage(
bevy_app::stage::POST_UPDATE,
camera::camera_system::<OrthographicProjection>,

View file

@ -1,12 +1,11 @@
use crate::{
pipeline::{
state_descriptors::{IndexFormat, PrimitiveTopology},
VertexBufferDescriptor, VertexBufferDescriptors, VertexFormat,
AsVertexBufferDescriptor,
AsVertexBufferDescriptor, VertexBufferDescriptor, VertexBufferDescriptors, VertexFormat,
},
render_resource::{BufferInfo, BufferUsage},
renderer::{RenderResourceContext, RenderResources},
Renderable, Vertex,
RenderPipelines, Vertex,
};
use bevy_app::{EventReader, Events};
use bevy_asset::{AssetEvent, Assets, Handle};
@ -345,7 +344,7 @@ pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box<dyn Sched
render_resources: Res<RenderResources>,
meshes: Res<Assets<Mesh>>,
mesh_events: Res<Events<AssetEvent<Mesh>>>,
query: &mut Query<(Read<Handle<Mesh>>, Write<Renderable>)>| {
query: &mut Query<(Read<Handle<Mesh>>, Write<RenderPipelines>)>| {
let render_resources = &*render_resources.context;
let mut changed_meshes = HashSet::new();
for event in mesh_event_reader.iter(&mesh_events) {
@ -403,9 +402,9 @@ pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box<dyn Sched
}
// TODO: remove this once batches are pipeline specific and deprecate assigned_meshes draw target
for (handle, mut renderable) in query.iter_mut(world) {
for (handle, mut render_pipelines) in query.iter_mut(world) {
if let Some(mesh) = meshes.get(&handle) {
renderable
render_pipelines
.render_resource_assignments
.pipeline_specialization
.primitive_topology = mesh.primitive_topology;
@ -417,11 +416,9 @@ pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box<dyn Sched
let index_buffer =
render_resources.get_asset_resource(*handle, INDEX_BUFFER_ASSET_INDEX);
renderable.render_resource_assignments.set_vertex_buffer(
"Vertex",
vertex_buffer,
index_buffer,
);
render_pipelines
.render_resource_assignments
.set_vertex_buffer("Vertex", vertex_buffer, index_buffer);
}
}
})
@ -430,7 +427,7 @@ pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box<dyn Sched
#[cfg(test)]
mod tests {
use super::{Mesh, VertexAttribute, AsVertexBufferDescriptor};
use super::{AsVertexBufferDescriptor, Mesh, VertexAttribute};
use crate::{pipeline::state_descriptors::PrimitiveTopology, Vertex};
use bevy_core::bytes::AsBytes;

View file

@ -1,6 +1,6 @@
use crate::{
pipeline::{BindGroupDescriptor, PipelineDescriptor},
render_resource::{RenderResourceId, RenderResourceSet},
pipeline::{PipelineDescriptor, BindGroupDescriptorId},
render_resource::{RenderResourceId, RenderResourceSetId},
renderer::RenderContext,
};
use bevy_asset::Handle;
@ -17,7 +17,9 @@ pub trait RenderPass {
fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);
fn set_bind_group(
&mut self,
bind_group_descriptor: &BindGroupDescriptor,
render_resource_set: &RenderResourceSet,
index: u32,
bind_group_descriptor: BindGroupDescriptorId,
render_resource_set: RenderResourceSetId,
dynamic_uniform_indices: Option<&[u32]>,
);
}

View file

@ -13,23 +13,10 @@ use crate::{
};
use bevy_asset::Assets;
// TODO: consider removing this in favor of Option<Layout>
#[derive(Clone, Debug)]
pub enum PipelineLayoutType {
Manual(PipelineLayout),
Reflected(Option<PipelineLayout>),
}
#[derive(Clone, Debug)]
pub enum DescriptorType<T> {
Manual(T),
Reflected(Option<T>),
}
#[derive(Clone, Debug)]
pub struct PipelineDescriptor {
pub name: Option<String>,
pub layout: PipelineLayoutType,
pub layout: Option<PipelineLayout>,
pub shader_stages: ShaderStages,
pub rasterization_state: Option<RasterizationStateDescriptor>,
@ -63,7 +50,7 @@ impl PipelineDescriptor {
pub fn new(shader_stages: ShaderStages) -> Self {
PipelineDescriptor {
name: None,
layout: PipelineLayoutType::Reflected(None),
layout: None,
color_states: Vec::new(),
depth_stencil_state: None,
shader_stages,
@ -80,7 +67,7 @@ impl PipelineDescriptor {
PipelineDescriptor {
name: None,
primitive_topology: PrimitiveTopology::TriangleList,
layout: PipelineLayoutType::Reflected(None),
layout: None,
index_format: IndexFormat::Uint16,
sample_count: 1,
sample_mask: !0,
@ -120,17 +107,11 @@ impl PipelineDescriptor {
}
pub fn get_layout(&self) -> Option<&PipelineLayout> {
match self.layout {
PipelineLayoutType::Reflected(ref layout) => layout.as_ref(),
PipelineLayoutType::Manual(ref layout) => Some(layout),
}
self.layout.as_ref()
}
pub fn get_layout_mut(&mut self) -> Option<&mut PipelineLayout> {
match self.layout {
PipelineLayoutType::Reflected(ref mut layout) => layout.as_mut(),
PipelineLayoutType::Manual(ref mut layout) => Some(layout),
}
self.layout.as_mut()
}
/// Reflects the pipeline layout from its shaders.
@ -190,6 +171,6 @@ impl PipelineDescriptor {
}
}
self.layout = PipelineLayoutType::Reflected(Some(layout));
self.layout = Some(layout);
}
}

View file

@ -1,8 +1,8 @@
use super::{state_descriptors::PrimitiveTopology, PipelineDescriptor, VertexBufferDescriptors};
use crate::{
render_resource::{RenderResourceAssignments, RenderResourceAssignmentsId},
draw::RenderPipelines,
renderer::{RenderResourceContext, RenderResources},
shader::{Shader, ShaderSource},
Renderable,
};
use bevy_asset::{Assets, Handle};
use std::collections::{HashMap, HashSet};
@ -78,14 +78,15 @@ impl PipelineCompiler {
vertex_buffer_descriptors: &VertexBufferDescriptors,
shaders: &mut Assets<Shader>,
pipeline_descriptor: &PipelineDescriptor,
render_resource_assignments: &RenderResourceAssignments,
render_pipelines: &RenderPipelines,
) -> PipelineDescriptor {
let mut compiled_pipeline_descriptor = pipeline_descriptor.clone();
compiled_pipeline_descriptor.shader_stages.vertex = self.compile_shader(
shaders,
&pipeline_descriptor.shader_stages.vertex,
&render_resource_assignments
&render_pipelines
.render_resource_assignments
.pipeline_specialization
.shader_specialization,
);
@ -97,7 +98,8 @@ impl PipelineCompiler {
self.compile_shader(
shaders,
fragment,
&render_resource_assignments
&render_pipelines
.render_resource_assignments
.pipeline_specialization
.shader_specialization,
)
@ -107,72 +109,78 @@ impl PipelineCompiler {
shaders,
true,
Some(vertex_buffer_descriptors),
Some(render_resource_assignments),
Some(&render_pipelines.render_resource_assignments),
);
compiled_pipeline_descriptor.primitive_topology = render_resource_assignments
compiled_pipeline_descriptor.primitive_topology = render_pipelines
.render_resource_assignments
.pipeline_specialization
.primitive_topology;
compiled_pipeline_descriptor
}
fn update_shader_assignments(
fn compile_pipelines(
&mut self,
vertex_buffer_descriptors: &VertexBufferDescriptors,
shader_pipeline_assignments: &mut PipelineAssignments,
pipelines: &mut Assets<PipelineDescriptor>,
shaders: &mut Assets<Shader>,
pipeline_handles: &[Handle<PipelineDescriptor>],
render_resource_assignments: &RenderResourceAssignments,
render_pipelines: &mut RenderPipelines,
render_resource_context: &dyn RenderResourceContext,
) {
for pipeline_handle in pipeline_handles.iter() {
for (i, pipeline_handle) in render_pipelines.pipelines.iter().enumerate() {
if let None = self.pipeline_source_to_compiled.get(pipeline_handle) {
self.pipeline_source_to_compiled
.insert(*pipeline_handle, Vec::new());
}
let final_handle = if let Some((_shader_defs, macroed_pipeline_handle)) = self
.pipeline_source_to_compiled
.get_mut(pipeline_handle)
.unwrap()
.iter()
.find(|(pipeline_specialization, _macroed_pipeline_handle)| {
*pipeline_specialization == render_resource_assignments.pipeline_specialization
}) {
*macroed_pipeline_handle
let compiled_pipeline_handle = if let Some((_shader_defs, compiled_pipeline_handle)) =
self.pipeline_source_to_compiled
.get_mut(pipeline_handle)
.unwrap()
.iter()
.find(|(pipeline_specialization, _compiled_pipeline_handle)| {
*pipeline_specialization
== render_pipelines
.render_resource_assignments
.pipeline_specialization
}) {
*compiled_pipeline_handle
} else {
let pipeline_descriptor = pipelines.get(pipeline_handle).unwrap();
let compiled_pipeline = self.compile_pipeline(
let compiled_pipeline_descriptor = self.compile_pipeline(
vertex_buffer_descriptors,
shaders,
pipeline_descriptor,
render_resource_assignments,
render_pipelines,
);
let compiled_pipeline_handle = pipelines.add(compiled_pipeline_descriptor);
render_resource_context.create_render_pipeline(
compiled_pipeline_handle,
pipelines.get(&compiled_pipeline_handle).unwrap(),
&shaders,
);
let compiled_pipeline_handle = pipelines.add(compiled_pipeline);
let macro_pipelines = self
let compiled_pipelines = self
.pipeline_source_to_compiled
.get_mut(pipeline_handle)
.unwrap();
macro_pipelines.push((
render_resource_assignments.pipeline_specialization.clone(),
compiled_pipelines.push((
render_pipelines
.render_resource_assignments
.pipeline_specialization
.clone(),
compiled_pipeline_handle,
));
compiled_pipeline_handle
};
// TODO: this will break down if pipeline layout changes. fix this with "auto-layout"
if let None = shader_pipeline_assignments.assignments.get(&final_handle) {
shader_pipeline_assignments
.assignments
.insert(final_handle, Vec::new());
if i == render_pipelines.compiled_pipelines.len() {
render_pipelines
.compiled_pipelines
.push(compiled_pipeline_handle);
} else {
render_pipelines.compiled_pipelines[i] = compiled_pipeline_handle;
}
let assignments = shader_pipeline_assignments
.assignments
.get_mut(&final_handle)
.unwrap();
assignments.push(render_resource_assignments.id);
}
}
@ -199,49 +207,34 @@ impl PipelineCompiler {
}
}
#[derive(Default)]
pub struct PipelineAssignments {
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<RenderResourceAssignmentsId>>,
}
// TODO: make this a system
pub fn update_shader_assignments(world: &mut World, resources: &Resources) {
// PERF: this seems like a lot of work for things that don't change that often.
// lots of string + hashset allocations. sees uniform_resource_provider for more context
{
let mut shader_pipeline_assignments = resources.get_mut::<PipelineAssignments>().unwrap();
let mut pipeline_compiler = resources.get_mut::<PipelineCompiler>().unwrap();
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
let vertex_buffer_descriptors = resources.get::<VertexBufferDescriptors>().unwrap();
let mut pipeline_descriptor_storage =
resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
pub fn compile_pipelines_system(
world: &mut SubWorld,
mut pipeline_compiler: ResMut<PipelineCompiler>,
mut shaders: ResMut<Assets<Shader>>,
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
vertex_buffer_descriptors: Res<VertexBufferDescriptors>,
render_resources: Res<RenderResources>,
query: &mut Query<Write<RenderPipelines>>,
) {
let render_resource_context = &*render_resources.context;
// reset assignments so they are updated every frame
shader_pipeline_assignments.assignments = HashMap::new();
// TODO: only update when RenderPipelines is changed
for mut render_pipelines in query.iter_mut(world) {
pipeline_compiler.compile_pipelines(
&vertex_buffer_descriptors,
&mut pipelines,
&mut shaders,
&mut render_pipelines,
render_resource_context,
);
// TODO: only update when renderable is changed
for mut renderable in <Write<Renderable>>::query().iter_mut(world) {
// skip instanced entities. their batched RenderResourceAssignments will handle shader assignments
if renderable.is_instanced {
continue;
}
pipeline_compiler.update_shader_assignments(
&vertex_buffer_descriptors,
&mut shader_pipeline_assignments,
&mut pipeline_descriptor_storage,
&mut shaders,
&renderable.pipelines,
&renderable.render_resource_assignments,
);
// reset shader_defs so they can be changed next frame
renderable
.render_resource_assignments
.pipeline_specialization
.shader_specialization
.shader_defs
.clear();
}
// reset shader_defs so they can be changed next frame
render_pipelines
.render_resource_assignments
.pipeline_specialization
.shader_specialization
.shader_defs
.clear();
}
}

View file

@ -1,28 +1,20 @@
use crate::{
pass::{PassDescriptor, RenderPass, TextureAttachment},
pipeline::{PipelineAssignments, PipelineCompiler, PipelineDescriptor},
draw::{Draw, RenderCommand},
pass::{PassDescriptor, TextureAttachment},
render_graph::{Node, ResourceSlotInfo, ResourceSlots},
render_resource::{
EntitiesWaitingForAssets, EntityRenderResourceAssignments, RenderResourceAssignments,
ResourceInfo,
},
render_resource::{EntitiesWaitingForAssets, RenderResourceAssignments, ResourceInfo},
renderer::RenderContext,
shader::Shader,
Renderable,
};
use bevy_asset::{Assets, Handle};
use legion::prelude::*;
use std::ops::Range;
pub struct PassNode {
pub struct MainPassNode {
descriptor: PassDescriptor,
pipelines: Vec<Handle<PipelineDescriptor>>,
inputs: Vec<ResourceSlotInfo>,
color_attachment_input_indices: Vec<Option<usize>>,
depth_stencil_attachment_input_index: Option<usize>,
}
impl PassNode {
impl MainPassNode {
pub fn new(descriptor: PassDescriptor) -> Self {
let mut inputs = Vec::new();
let mut color_attachment_input_indices = Vec::new();
@ -49,77 +41,16 @@ impl PassNode {
}
}
PassNode {
MainPassNode {
descriptor,
pipelines: Vec::new(),
inputs,
color_attachment_input_indices,
depth_stencil_attachment_input_index,
}
}
pub fn add_pipeline(&mut self, pipeline_handle: Handle<PipelineDescriptor>) {
self.pipelines.push(pipeline_handle);
}
fn set_render_resources(
render_pass: &mut dyn RenderPass,
pipeline_descriptor: &PipelineDescriptor,
render_resource_assignments: &RenderResourceAssignments,
) -> Option<Range<u32>> {
let pipeline_layout = pipeline_descriptor.get_layout().unwrap();
// PERF: vertex buffer lookup comes at a cost when vertex buffers aren't in render_resource_assignments. iterating over render_resource_assignment vertex buffers
// would likely be faster
let mut indices = None;
for (i, vertex_buffer_descriptor) in
pipeline_layout.vertex_buffer_descriptors.iter().enumerate()
{
if let Some((vertex_buffer, index_buffer)) =
render_resource_assignments.get_vertex_buffer(&vertex_buffer_descriptor.name)
{
log::trace!(
"set vertex buffer {}: {} ({:?})",
i,
vertex_buffer_descriptor.name,
vertex_buffer
);
render_pass.set_vertex_buffer(i as u32, vertex_buffer, 0);
if let Some(index_buffer) = index_buffer {
log::trace!(
"set index buffer: {} ({:?})",
vertex_buffer_descriptor.name,
index_buffer
);
render_pass.set_index_buffer(index_buffer, 0);
render_pass
.get_render_context()
.resources()
.get_resource_info(
index_buffer,
&mut |resource_info| match resource_info {
Some(ResourceInfo::Buffer(Some(buffer_info))) => {
indices = Some(0..(buffer_info.size / 2) as u32)
}
_ => panic!("expected a buffer type"),
},
);
}
}
}
for bind_group in pipeline_layout.bind_groups.iter() {
if let Some(render_resource_set) =
render_resource_assignments.get_render_resource_set(bind_group.id)
{
render_pass.set_bind_group(bind_group, &render_resource_set);
}
}
indices
}
}
impl Node for PassNode {
impl Node for MainPassNode {
fn input(&self) -> &[ResourceSlotInfo] {
&self.inputs
}
@ -132,14 +63,8 @@ impl Node for PassNode {
input: &ResourceSlots,
_output: &mut ResourceSlots,
) {
let pipeline_compiler = resources.get::<PipelineCompiler>().unwrap();
let pipelines = resources.get::<Assets<PipelineDescriptor>>().unwrap();
let shader_pipeline_assignments = resources.get::<PipelineAssignments>().unwrap();
let entity_render_resource_assignments =
resources.get::<EntityRenderResourceAssignments>().unwrap();
let entities_waiting_for_assets = resources.get::<EntitiesWaitingForAssets>().unwrap();
let mut render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
let render_resource_assignments = resources.get::<RenderResourceAssignments>().unwrap();
for (i, color_attachment) in self.descriptor.color_attachments.iter_mut().enumerate() {
if let Some(input_index) = self.color_attachment_input_indices[i] {
@ -156,117 +81,56 @@ impl Node for PassNode {
.attachment = TextureAttachment::RenderResource(input.get(input_index).unwrap());
}
{
let render_resource_context = render_context.resources();
// TODO: try merging the two pipeline loops below
let shaders = resources.get::<Assets<Shader>>().unwrap();
for pipeline_handle in self.pipelines.iter() {
if let Some(compiled_pipelines_iter) =
pipeline_compiler.iter_compiled_pipelines(*pipeline_handle)
{
for compiled_pipeline_handle in compiled_pipelines_iter {
let compiled_pipeline_descriptor =
pipelines.get(compiled_pipeline_handle).unwrap();
let pipeline_layout = compiled_pipeline_descriptor.get_layout().unwrap();
{
// TODO: this breaks down in a parallel setting. it needs to change. ideally in a way that
// doesn't require modifying RenderResourceAssignments
for bind_group in pipeline_layout.bind_groups.iter() {
render_resource_assignments
.update_render_resource_set_id(bind_group);
}
}
render_resource_context.create_render_pipeline(
*compiled_pipeline_handle,
&compiled_pipeline_descriptor,
&shaders,
);
render_resource_context.setup_bind_groups(
compiled_pipeline_descriptor,
&render_resource_assignments,
);
let assigned_render_resource_assignments = shader_pipeline_assignments
.assignments
.get(&compiled_pipeline_handle);
if let Some(assigned_render_resource_assignments) =
assigned_render_resource_assignments
{
for assignment_id in assigned_render_resource_assignments.iter() {
let entity = entity_render_resource_assignments
.get(*assignment_id)
.unwrap();
let renderable =
world.get_component::<Renderable>(*entity).unwrap();
if !renderable.is_visible || renderable.is_instanced {
continue;
}
render_resource_context.setup_bind_groups(
compiled_pipeline_descriptor,
&renderable.render_resource_assignments,
);
}
}
}
}
}
}
render_context.begin_pass(
&self.descriptor,
&render_resource_assignments,
&mut |render_pass| {
for pipeline_handle in self.pipelines.iter() {
if let Some(compiled_pipelines_iter) =
pipeline_compiler.iter_compiled_pipelines(*pipeline_handle)
{
for compiled_pipeline_handle in compiled_pipelines_iter {
let compiled_pipeline_descriptor =
pipelines.get(compiled_pipeline_handle).unwrap();
render_pass.set_pipeline(*compiled_pipeline_handle);
for (entity, draw) in <Read<Draw>>::query().iter_entities(&world) {
if !draw.is_visible || entities_waiting_for_assets.contains(&entity) {
continue;
}
// set global render resources
Self::set_render_resources(
render_pass,
compiled_pipeline_descriptor,
&render_resource_assignments,
);
// draw entities assigned to this pipeline
let assigned_render_resource_assignments = shader_pipeline_assignments
.assignments
.get(&compiled_pipeline_handle);
if let Some(assigned_render_resource_assignments) =
assigned_render_resource_assignments
{
for assignment_id in assigned_render_resource_assignments.iter() {
// TODO: hopefully legion has better random access apis that are more like queries?
let entity = entity_render_resource_assignments
.get(*assignment_id)
.unwrap();
let renderable =
world.get_component::<Renderable>(*entity).unwrap();
if !renderable.is_visible
|| renderable.is_instanced
|| entities_waiting_for_assets.contains(entity)
{
continue;
}
// set local render resources
if let Some(indices) = Self::set_render_resources(
render_pass,
compiled_pipeline_descriptor,
&renderable.render_resource_assignments,
) {
render_pass.draw_indexed(indices, 0, 0..1);
}
}
for render_command in draw.render_commands.iter() {
match render_command {
RenderCommand::SetPipeline { pipeline } => {
// TODO: Filter pipelines
render_pass.set_pipeline(*pipeline);
}
RenderCommand::DrawIndexed {
base_vertex,
indices,
instances,
} => {
render_pass.draw_indexed(
indices.clone(),
*base_vertex,
instances.clone(),
);
}
RenderCommand::SetVertexBuffer {
buffer,
offset,
slot,
} => {
render_pass.set_vertex_buffer(*slot, *buffer, *offset);
}
RenderCommand::SetIndexBuffer { buffer, offset } => {
render_pass.set_index_buffer(*buffer, *offset);
}
RenderCommand::SetBindGroup {
index,
bind_group_descriptor,
render_resource_set,
dynamic_uniform_indices,
} => {
render_pass.set_bind_group(
*index,
*bind_group_descriptor,
*render_resource_set,
dynamic_uniform_indices
.as_ref()
.map(|indices| indices.as_slice()),
);
}
}
}

View file

@ -1,11 +1,13 @@
use crate::{
draw::{Draw, RenderPipelines},
render_graph::{CommandQueue, Node, ResourceSlots, SystemNode},
render_resource::{
self, BufferInfo, BufferUsage, EntitiesWaitingForAssets, RenderResourceAssignment,
RenderResourceAssignments, RenderResourceAssignmentsId, RenderResourceId, RenderResourceHints,
RenderResourceAssignments, RenderResourceAssignmentsId, RenderResourceHints,
RenderResourceId,
},
renderer::{RenderContext, RenderResourceContext, RenderResources},
texture, Renderable,
texture,
};
use bevy_asset::{Assets, Handle};
@ -217,12 +219,14 @@ where
None => {
// TODO: RE-ADD support for BufferUsage::STORAGE type
let mut usage = BufferUsage::UNIFORM;
if let Some(render_resource_hints) = uniforms.get_render_resource_hints(i) {
if let Some(render_resource_hints) =
uniforms.get_render_resource_hints(i)
{
if render_resource_hints.contains(RenderResourceHints::BUFFER) {
usage = BufferUsage::STORAGE
}
}
}
let resource = render_resources.create_buffer(BufferInfo {
size,
@ -396,8 +400,8 @@ where
.read_resource::<RenderResources>()
.read_resource::<EntitiesWaitingForAssets>()
// TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same
.with_query(<(Read<T>, Read<Renderable>)>::query())
.with_query(<(Read<T>, Write<Renderable>)>::query())
.with_query(<(Read<T>, Read<Draw>, Read<RenderPipelines>)>::query())
.with_query(<(Read<T>, Read<Draw>, Write<RenderPipelines>)>::query())
.build(
move |_,
world,
@ -407,60 +411,50 @@ where
uniform_buffer_arrays.reset_new_item_counts();
// update uniforms info
for (uniforms, renderable) in read_uniform_query.iter(world) {
if !renderable.is_visible {
for (uniforms, draw, _render_pipelines) in read_uniform_query.iter(world) {
if !draw.is_visible {
return;
}
if renderable.is_instanced {
panic!("instancing not currently supported");
} else {
uniform_buffer_arrays.increment_uniform_counts(&uniforms);
}
uniform_buffer_arrays.increment_uniform_counts(&uniforms);
}
uniform_buffer_arrays
.setup_buffer_arrays(render_resource_context, dynamic_uniforms);
let staging_buffer_size = uniform_buffer_arrays.update_staging_buffer_offsets();
for (entity, (uniforms, mut renderable)) in
for (entity, (uniforms, draw, mut render_pipelines)) in
write_uniform_query.iter_entities_mut(world)
{
if !renderable.is_visible {
if !draw.is_visible {
return;
}
if renderable.is_instanced {
panic!("instancing not currently supported");
} else {
setup_uniform_texture_resources::<T>(
entity,
&uniforms,
render_resource_context,
entities_waiting_for_assets,
&mut renderable.render_resource_assignments,
)
}
setup_uniform_texture_resources::<T>(
entity,
&uniforms,
render_resource_context,
entities_waiting_for_assets,
&mut render_pipelines.render_resource_assignments,
)
}
if staging_buffer_size == 0 {
let mut staging_buffer: [u8; 0] = [];
for (uniforms, mut renderable) in write_uniform_query.iter_mut(world) {
if !renderable.is_visible {
for (uniforms, draw, mut render_pipelines) in
write_uniform_query.iter_mut(world)
{
if !draw.is_visible {
return;
}
if renderable.is_instanced {
panic!("instancing not currently supported");
} else {
uniform_buffer_arrays.setup_uniform_buffer_resources(
&uniforms,
dynamic_uniforms,
render_resource_context,
&mut renderable.render_resource_assignments,
&mut staging_buffer,
);
}
uniform_buffer_arrays.setup_uniform_buffer_resources(
&uniforms,
dynamic_uniforms,
render_resource_context,
&mut render_pipelines.render_resource_assignments,
&mut staging_buffer,
);
}
} else {
let staging_buffer = render_resource_context.create_buffer_mapped(
@ -470,22 +464,20 @@ where
..Default::default()
},
&mut |mut staging_buffer, _render_resources| {
for (uniforms, mut renderable) in write_uniform_query.iter_mut(world) {
if !renderable.is_visible {
for (uniforms, draw, mut render_pipelines) in
write_uniform_query.iter_mut(world)
{
if !draw.is_visible {
return;
}
if renderable.is_instanced {
panic!("instancing not currently supported");
} else {
uniform_buffer_arrays.setup_uniform_buffer_resources(
&uniforms,
dynamic_uniforms,
render_resource_context,
&mut renderable.render_resource_assignments,
&mut staging_buffer,
);
}
uniform_buffer_arrays.setup_uniform_buffer_resources(
&uniforms,
dynamic_uniforms,
render_resource_context,
&mut render_pipelines.render_resource_assignments,
&mut staging_buffer,
);
}
},
);
@ -552,8 +544,8 @@ where
.read_resource::<RenderResources>()
.read_resource::<EntitiesWaitingForAssets>()
// TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same
.with_query(<(Read<Handle<T>>, Read<Renderable>)>::query())
.with_query(<(Read<Handle<T>>, Write<Renderable>)>::query())
.with_query(<(Read<Handle<T>>, Read<Draw>, Read<RenderPipelines>)>::query())
.with_query(<(Read<Handle<T>>, Read<Draw>, Write<RenderPipelines>)>::query())
.build(
move |_,
world,
@ -563,20 +555,18 @@ where
uniform_buffer_arrays.reset_new_item_counts();
// update uniform handles info
for (entity, (handle, renderable)) in read_handle_query.iter_entities(world) {
if !renderable.is_visible {
for (entity, (handle, draw, _render_pipelines)) in
read_handle_query.iter_entities(world)
{
if !draw.is_visible {
return;
}
if renderable.is_instanced {
panic!("instancing not currently supported");
if let Some(uniforms) = assets.get(&handle) {
// TODO: only increment count if we haven't seen this uniform handle before
uniform_buffer_arrays.increment_uniform_counts(&uniforms);
} else {
if let Some(uniforms) = assets.get(&handle) {
// TODO: only increment count if we haven't seen this uniform handle before
uniform_buffer_arrays.increment_uniform_counts(&uniforms);
} else {
entities_waiting_for_assets.add(entity)
}
entities_waiting_for_assets.add(entity)
}
}
@ -584,46 +574,40 @@ where
.setup_buffer_arrays(render_resource_context, dynamic_uniforms);
let staging_buffer_size = uniform_buffer_arrays.update_staging_buffer_offsets();
for (entity, (handle, mut renderable)) in
for (entity, (handle, draw, mut render_pipelines)) in
write_handle_query.iter_entities_mut(world)
{
if !renderable.is_visible {
if !draw.is_visible {
return;
}
if renderable.is_instanced {
panic!("instancing not currently supported");
} else {
if let Some(uniforms) = assets.get(&handle) {
setup_uniform_texture_resources::<T>(
entity,
&uniforms,
render_resource_context,
entities_waiting_for_assets,
&mut renderable.render_resource_assignments,
)
}
if let Some(uniforms) = assets.get(&handle) {
setup_uniform_texture_resources::<T>(
entity,
&uniforms,
render_resource_context,
entities_waiting_for_assets,
&mut render_pipelines.render_resource_assignments,
)
}
}
if staging_buffer_size == 0 {
let mut staging_buffer: [u8; 0] = [];
for (handle, mut renderable) in write_handle_query.iter_mut(world) {
if !renderable.is_visible {
for (handle, draw, mut render_pipelines) in
write_handle_query.iter_mut(world)
{
if !draw.is_visible {
return;
}
if renderable.is_instanced {
panic!("instancing not currently supported");
} else {
if let Some(uniforms) = assets.get(&handle) {
// TODO: only setup buffer if we haven't seen this handle before
uniform_buffer_arrays.setup_uniform_buffer_resources(
&uniforms,
dynamic_uniforms,
render_resource_context,
&mut renderable.render_resource_assignments,
&mut staging_buffer,
);
}
if let Some(uniforms) = assets.get(&handle) {
// TODO: only setup buffer if we haven't seen this handle before
uniform_buffer_arrays.setup_uniform_buffer_resources(
&uniforms,
dynamic_uniforms,
render_resource_context,
&mut render_pipelines.render_resource_assignments,
&mut staging_buffer,
);
}
}
} else {
@ -634,23 +618,21 @@ where
..Default::default()
},
&mut |mut staging_buffer, _render_resources| {
for (handle, mut renderable) in write_handle_query.iter_mut(world) {
if !renderable.is_visible {
for (handle, draw, mut render_pipelines) in
write_handle_query.iter_mut(world)
{
if !draw.is_visible {
return;
}
if renderable.is_instanced {
panic!("instancing not currently supported");
} else {
if let Some(uniforms) = assets.get(&handle) {
// TODO: only setup buffer if we haven't seen this handle before
uniform_buffer_arrays.setup_uniform_buffer_resources(
&uniforms,
dynamic_uniforms,
render_resource_context,
&mut renderable.render_resource_assignments,
&mut staging_buffer,
);
}
if let Some(uniforms) = assets.get(&handle) {
// TODO: only setup buffer if we haven't seen this handle before
uniform_buffer_arrays.setup_uniform_buffer_resources(
&uniforms,
dynamic_uniforms,
render_resource_context,
&mut render_pipelines.render_resource_assignments,
&mut staging_buffer,
);
}
}
},

View file

@ -1,31 +0,0 @@
use super::RenderResourceAssignmentsId;
use crate::Renderable;
use legion::prelude::*;
use std::collections::HashMap;
#[derive(Default)]
pub struct EntityRenderResourceAssignments {
entity_assignments: HashMap<RenderResourceAssignmentsId, Entity>,
}
impl EntityRenderResourceAssignments {
pub fn set(&mut self, id: RenderResourceAssignmentsId, entity: Entity) {
self.entity_assignments.insert(id, entity);
}
pub fn get(&self, id: RenderResourceAssignmentsId) -> Option<&Entity> {
self.entity_assignments.get(&id)
}
}
// TODO: make sure this runs right before rendering
pub fn entity_render_resource_assignments_system() -> Box<dyn Schedulable> {
SystemBuilder::new("entity_render_resource_assignments")
.write_resource::<EntityRenderResourceAssignments>()
.with_query(<Write<Renderable>>::query().filter(changed::<Renderable>()))
.build(|_, world, entity_assignments, query| {
for (entity, renderable) in query.iter_entities_mut(world) {
entity_assignments.set(renderable.render_resource_assignments.id, entity);
}
})
}

View file

@ -1,13 +1,13 @@
mod buffer;
mod entities_waiting_for_assets;
mod entity_render_resource_assignments;
mod render_resource;
mod render_resource_assignments;
mod resource_info;
mod systems;
pub use buffer::*;
pub use entities_waiting_for_assets::*;
pub use entity_render_resource_assignments::*;
pub use render_resource::*;
pub use render_resource_assignments::*;
pub use resource_info::*;
pub use systems::*;

View file

@ -4,6 +4,7 @@ use std::{
collections::{hash_map::DefaultHasher, HashMap, HashSet},
hash::{Hash, Hasher},
ops::Range,
sync::Arc,
};
use uuid::Uuid;
@ -28,10 +29,25 @@ impl RenderResourceAssignment {
}
}
#[derive(Eq, PartialEq, Debug)]
pub struct IndexedRenderResourceAssignment {
pub index: u32,
pub assignment: RenderResourceAssignment,
}
// TODO: consider renaming this to BindGroup for parity with renderer terminology
#[derive(Eq, PartialEq, Debug)]
pub struct RenderResourceSet {
pub id: RenderResourceSetId,
pub dynamic_uniform_indices: Option<Vec<u32>>,
pub indexed_assignments: Vec<IndexedRenderResourceAssignment>,
pub dynamic_uniform_indices: Option<Arc<Vec<u32>>>,
}
#[derive(Eq, PartialEq, Debug)]
pub enum RenderResourceSetStatus {
Changed(RenderResourceSetId),
Unchanged(RenderResourceSetId),
NoMatch,
}
// PERF: if the assignments are scoped to a specific pipeline layout, then names could be replaced with indices here for a perf boost
@ -40,8 +56,9 @@ pub struct RenderResourceAssignments {
pub id: RenderResourceAssignmentsId,
render_resources: HashMap<String, RenderResourceAssignment>,
vertex_buffers: HashMap<String, (RenderResourceId, Option<RenderResourceId>)>,
bind_group_resource_sets: HashMap<BindGroupDescriptorId, RenderResourceSet>,
dirty_bind_groups: HashSet<BindGroupDescriptorId>,
render_resource_sets: HashMap<RenderResourceSetId, RenderResourceSet>,
bind_group_render_resource_sets: HashMap<BindGroupDescriptorId, RenderResourceSetId>,
dirty_render_resource_sets: HashSet<RenderResourceSetId>,
pub pipeline_specialization: PipelineSpecialization,
}
@ -58,9 +75,8 @@ impl RenderResourceAssignments {
fn try_set_dirty(&mut self, name: &str, assignment: &RenderResourceAssignment) {
if let Some(current_assignment) = self.render_resources.get(name) {
if current_assignment != assignment {
// TODO: this is pretty crude. can we do better?
for bind_group_id in self.bind_group_resource_sets.keys() {
self.dirty_bind_groups.insert(*bind_group_id);
for id in self.render_resource_sets.keys() {
self.dirty_render_resource_sets.insert(*id);
}
}
}
@ -83,36 +99,54 @@ impl RenderResourceAssignments {
.insert(name.to_string(), (vertices_resource, indices_resource));
}
pub fn update_render_resource_set_id(
fn create_render_resource_set(
&mut self,
bind_group_descriptor: &BindGroupDescriptor,
) -> Option<RenderResourceSetId> {
if !self
.bind_group_resource_sets
.contains_key(&bind_group_descriptor.id)
|| self.dirty_bind_groups.contains(&bind_group_descriptor.id)
{
let resource_set = self.generate_render_resource_set(bind_group_descriptor);
if let Some(resource_set) = resource_set {
let id = resource_set.id;
self.bind_group_resource_sets
.insert(bind_group_descriptor.id, resource_set);
Some(id)
} else {
None
}
) -> RenderResourceSetStatus {
let resource_set = self.generate_render_resource_set(bind_group_descriptor);
if let Some(resource_set) = resource_set {
let id = resource_set.id;
self.render_resource_sets.insert(id, resource_set);
self.bind_group_render_resource_sets
.insert(bind_group_descriptor.id, id);
RenderResourceSetStatus::Changed(id)
} else {
self.bind_group_resource_sets
.get(&bind_group_descriptor.id)
.map(|set| set.id)
RenderResourceSetStatus::NoMatch
}
}
pub fn get_render_resource_set(
pub fn update_bind_group(
&mut self,
bind_group_descriptor: &BindGroupDescriptor,
) -> RenderResourceSetStatus {
if let Some(id) = self
.bind_group_render_resource_sets
.get(&bind_group_descriptor.id)
{
if self.dirty_render_resource_sets.contains(id) {
self.dirty_render_resource_sets.remove(id);
self.create_render_resource_set(bind_group_descriptor)
} else {
RenderResourceSetStatus::Unchanged(*id)
}
} else {
self.create_render_resource_set(bind_group_descriptor)
}
}
pub fn get_render_resource_set(&self, id: RenderResourceSetId) -> Option<&RenderResourceSet> {
self.render_resource_sets.get(&id)
}
pub fn get_bind_group_render_resource_set(
&self,
bind_group_descriptor_id: BindGroupDescriptorId,
id: BindGroupDescriptorId,
) -> Option<&RenderResourceSet> {
self.bind_group_resource_sets.get(&bind_group_descriptor_id)
self.bind_group_render_resource_sets
.get(&id)
.and_then(|render_resource_set_id| {
self.get_render_resource_set(*render_resource_set_id)
})
}
fn generate_render_resource_set(
@ -121,8 +155,13 @@ impl RenderResourceAssignments {
) -> Option<RenderResourceSet> {
let mut hasher = DefaultHasher::new();
let mut indices = Vec::new();
let mut indexed_assignments = Vec::new();
for binding_descriptor in bind_group_descriptor.bindings.iter() {
if let Some(assignment) = self.get(&binding_descriptor.name) {
indexed_assignments.push(IndexedRenderResourceAssignment {
assignment: assignment.clone(),
index: binding_descriptor.index,
});
let resource = assignment.get_resource();
resource.hash(&mut hasher);
if let RenderResourceAssignment::Buffer {
@ -139,10 +178,11 @@ impl RenderResourceAssignments {
Some(RenderResourceSet {
id: RenderResourceSetId(hasher.finish()),
indexed_assignments,
dynamic_uniform_indices: if indices.is_empty() {
None
} else {
Some(indices)
Some(Arc::new(indices))
},
})
}
@ -215,22 +255,34 @@ mod tests {
equal_assignments.set("a", resource1.clone());
equal_assignments.set("b", resource2.clone());
let set_id = assignments.update_render_resource_set_id(&bind_group_descriptor);
assert_ne!(set_id, None);
let status = assignments.update_bind_group(&bind_group_descriptor);
let id = if let RenderResourceSetStatus::Changed(id) = status {
id
} else {
panic!("expected a changed render resource set");
};
let different_set_id =
different_assignments.update_render_resource_set_id(&bind_group_descriptor);
assert_ne!(different_set_id, None);
assert_ne!(different_set_id, set_id);
let different_set_status = different_assignments.update_bind_group(&bind_group_descriptor);
if let RenderResourceSetStatus::Changed(different_set_id) = different_set_status {
assert_ne!(
id, different_set_id,
"different set shouldn't have the same id"
);
different_set_id
} else {
panic!("expected a changed render resource set");
};
let equal_set_id = equal_assignments.update_render_resource_set_id(&bind_group_descriptor);
assert_ne!(equal_set_id, None);
assert_eq!(equal_set_id, set_id);
let equal_set_status = equal_assignments.update_bind_group(&bind_group_descriptor);
if let RenderResourceSetStatus::Changed(equal_set_id) = equal_set_status {
assert_eq!(id, equal_set_id, "equal set should have the same id");
} else {
panic!("expected a changed render resource set");
};
let mut unmatched_assignments = RenderResourceAssignments::default();
unmatched_assignments.set("a", resource1.clone());
let unmatched_set_id =
unmatched_assignments.update_render_resource_set_id(&bind_group_descriptor);
assert_eq!(unmatched_set_id, None);
let unmatched_set_status = unmatched_assignments.update_bind_group(&bind_group_descriptor);
assert_eq!(unmatched_set_status, RenderResourceSetStatus::NoMatch);
}
}

View file

@ -0,0 +1,68 @@
use bevy_asset::Assets;
use crate::{
draw::RenderPipelines,
pipeline::{PipelineCompiler, PipelineDescriptor},
render_resource::{RenderResourceAssignments, RenderResourceSetStatus},
renderer::{RenderResourceContext, RenderResources},
};
use legion::prelude::*;
fn update_bind_groups(
pipeline: &PipelineDescriptor,
render_resource_assignments: &mut RenderResourceAssignments,
render_resource_context: &dyn RenderResourceContext,
) {
let layout = pipeline.get_layout().unwrap();
for bind_group in layout.bind_groups.iter() {
match render_resource_assignments.update_bind_group(bind_group) {
RenderResourceSetStatus::Changed(id) => {
let render_resource_set = render_resource_assignments
.get_render_resource_set(id)
.expect("RenderResourceSet was just changed, so it should exist");
render_resource_context.create_bind_group(bind_group.id, render_resource_set);
},
// TODO: Don't re-create bind groups if they havent changed. this will require cleanup of orphan bind groups and
// removal of global context.clear_bind_groups()
// PERF: see above
RenderResourceSetStatus::Unchanged(id) => {
let render_resource_set = render_resource_assignments
.get_render_resource_set(id)
.expect("RenderResourceSet was just changed, so it should exist");
render_resource_context.create_bind_group(bind_group.id, render_resource_set);
},
RenderResourceSetStatus::NoMatch => {
// ignore unchanged / unmatched render resource sets
}
}
}
}
pub fn render_resource_sets_system(
world: &mut SubWorld,
pipelines: Res<Assets<PipelineDescriptor>>,
pipeline_compiler: Res<PipelineCompiler>,
render_resources: Res<RenderResources>,
mut render_resource_assignments: ResMut<RenderResourceAssignments>,
query: &mut Query<Write<RenderPipelines>>,
) {
let render_resource_context = &*render_resources.context;
for compiled_pipeline_handle in pipeline_compiler.iter_all_compiled_pipelines() {
let pipeline = pipelines.get(compiled_pipeline_handle).unwrap();
update_bind_groups(
pipeline,
&mut render_resource_assignments,
render_resource_context,
)
}
for mut render_pipelines in query.iter_mut(world) {
let render_pipelines = render_pipelines.as_mut();
for pipeline in render_pipelines.compiled_pipelines.iter() {
let pipeline = pipelines.get(pipeline).unwrap();
update_bind_groups(
pipeline,
&mut render_pipelines.render_resource_assignments,
render_resource_context,
)
}
}
}

View file

@ -1,32 +0,0 @@
use crate::{pipeline::PipelineDescriptor, render_resource::RenderResourceAssignments};
use bevy_asset::Handle;
use bevy_property::Properties;
#[derive(Properties)]
pub struct Renderable {
pub is_visible: bool,
pub is_instanced: bool,
pub pipelines: Vec<Handle<PipelineDescriptor>>,
#[property(ignore)]
pub render_resource_assignments: RenderResourceAssignments,
}
impl Renderable {
pub fn instanced() -> Self {
Renderable {
is_instanced: true,
..Default::default()
}
}
}
impl Default for Renderable {
fn default() -> Self {
Renderable {
is_visible: true,
pipelines: vec![Handle::default()],
render_resource_assignments: RenderResourceAssignments::default(),
is_instanced: false,
}
}
}

View file

@ -1,9 +1,7 @@
use super::RenderResourceContext;
use crate::{
pipeline::{BindGroupDescriptor, PipelineDescriptor},
render_resource::{
BufferInfo, RenderResourceId, RenderResourceAssignments, RenderResourceSetId, ResourceInfo,
},
pipeline::{BindGroupDescriptorId, PipelineDescriptor},
render_resource::{BufferInfo, RenderResourceId, RenderResourceSet, ResourceInfo},
shader::Shader,
texture::{SamplerDescriptor, TextureDescriptor},
};
@ -113,16 +111,9 @@ impl RenderResourceContext for HeadlessRenderResourceContext {
}
fn create_bind_group(
&self,
bind_group_descriptor: &BindGroupDescriptor,
render_resource_assignments: &RenderResourceAssignments,
) -> Option<RenderResourceSetId> {
if let Some(resource_set) =
render_resource_assignments.get_render_resource_set(bind_group_descriptor.id)
{
Some(resource_set.id)
} else {
None
}
_bind_group_descriptor_id: BindGroupDescriptorId,
_render_resource_set: &RenderResourceSet,
) {
}
fn create_shader_module_from_source(&self, _shader_handle: Handle<Shader>, _shader: &Shader) {}
fn remove_asset_resource_untyped(&self, handle: HandleUntyped, index: usize) {

View file

@ -1,8 +1,6 @@
use crate::{
pipeline::{BindGroupDescriptor, PipelineDescriptor},
render_resource::{
BufferInfo, RenderResourceId, RenderResourceAssignments, RenderResourceSetId, ResourceInfo,
},
pipeline::{BindGroupDescriptorId, PipelineDescriptor},
render_resource::{BufferInfo, RenderResourceId, RenderResourceSet, ResourceInfo},
shader::Shader,
texture::{SamplerDescriptor, TextureDescriptor},
};
@ -70,29 +68,15 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static {
);
fn create_bind_group(
&self,
bind_group_descriptor: &BindGroupDescriptor,
render_resource_assignments: &RenderResourceAssignments,
) -> Option<RenderResourceSetId>;
fn setup_bind_groups(
&self,
pipeline_descriptor: &PipelineDescriptor,
render_resource_assignments: &RenderResourceAssignments,
) {
let pipeline_layout = pipeline_descriptor.get_layout().unwrap();
for bind_group in pipeline_layout.bind_groups.iter() {
self.create_bind_group(bind_group, render_resource_assignments);
}
}
bind_group_descriptor_id: BindGroupDescriptorId,
render_resource_set: &RenderResourceSet,
);
fn clear_bind_groups(&self);
}
impl dyn RenderResourceContext {
pub fn set_asset_resource<T>(
&self,
handle: Handle<T>,
resource: RenderResourceId,
index: usize,
) where
pub fn set_asset_resource<T>(&self, handle: Handle<T>, resource: RenderResourceId, index: usize)
where
T: 'static,
{
self.set_asset_resource_untyped(handle.into(), resource, index);

View file

@ -1,7 +1,6 @@
use crate::{texture::Texture, RenderPipelines};
use bevy_asset::{Assets, Handle};
use crate::{Renderable, texture::Texture};
use legion::prelude::{Res, Com, ComMut};
use legion::prelude::{Com, ComMut, Res};
pub use bevy_derive::ShaderDefs;
@ -15,7 +14,6 @@ pub trait ShaderDefs {
fn iter_shader_defs(&self) -> ShaderDefIterator;
}
pub struct ShaderDefIterator<'a> {
shader_defs: &'a dyn ShaderDefs,
index: usize,
@ -35,13 +33,11 @@ impl<'a> Iterator for ShaderDefIterator<'a> {
loop {
if self.index == self.shader_defs.shader_defs_len() {
return None;
}
let shader_def = self
.shader_defs
.get_shader_def(self.index);
}
let shader_def = self.shader_defs.get_shader_def(self.index);
self.index += 1;
if shader_def.is_some() {
return shader_def;
return shader_def;
}
}
}
@ -59,12 +55,12 @@ impl ShaderDef for Option<Handle<Texture>> {
}
}
pub fn shader_def_system<T>(shader_defs: Com<T>, mut renderable: ComMut<Renderable>)
pub fn shader_def_system<T>(shader_defs: Com<T>, mut render_pipelines: ComMut<RenderPipelines>)
where
T: ShaderDefs + Send + Sync + 'static,
{
for shader_def in shader_defs.iter_shader_defs() {
renderable
render_pipelines
.render_resource_assignments
.pipeline_specialization
.shader_specialization
@ -76,17 +72,17 @@ where
pub fn asset_shader_def_system<T>(
assets: Res<Assets<T>>,
asset_handle: Com<Handle<T>>,
mut renderable: ComMut<Renderable>,
mut render_pipelines: ComMut<RenderPipelines>,
) where
T: ShaderDefs + Send + Sync + 'static,
{
let shader_defs = assets.get(&asset_handle).unwrap();
for shader_def in shader_defs.iter_shader_defs() {
renderable
render_pipelines
.render_resource_assignments
.pipeline_specialization
.shader_specialization
.shader_defs
.insert(shader_def.to_string());
}
}
}

View file

@ -4,7 +4,7 @@ use crate::{
};
use bevy_asset::Handle;
use bevy_app::EntityArchetype;
use bevy_render::{mesh::Mesh, Renderable};
use bevy_render::{mesh::Mesh, draw::{Draw, RenderPipelines}};
#[derive(EntityArchetype)]
pub struct SpriteEntity {
@ -12,7 +12,8 @@ pub struct SpriteEntity {
pub quad: Quad,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
pub material: Handle<ColorMaterial>,
pub renderable: Renderable,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
}
impl Default for SpriteEntity {
@ -22,7 +23,8 @@ impl Default for SpriteEntity {
quad: Default::default(),
mesh: QUAD_HANDLE,
material: Default::default(),
renderable: Renderable {
draw: Default::default(),
render_pipelines: RenderPipelines {
pipelines: vec![SPRITE_PIPELINE_HANDLE],
..Default::default()
},
@ -34,7 +36,8 @@ impl Default for SpriteEntity {
pub struct SpriteSheetEntity {
pub sprite: TextureAtlasSprite,
pub texture_atlas: Handle<TextureAtlas>,
pub renderable: Renderable,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
// pub transform: Transform,
// pub translation: Translation,
@ -47,7 +50,8 @@ impl Default for SpriteSheetEntity {
Self {
sprite: Default::default(),
texture_atlas: Default::default(),
renderable: Renderable {
draw: Default::default(),
render_pipelines: RenderPipelines {
pipelines: vec![SPRITE_SHEET_PIPELINE_HANDLE],
..Default::default()
},

View file

@ -4,7 +4,7 @@ use bevy_render::{
base_render_graph,
pipeline::{state_descriptors::*, PipelineDescriptor},
render_graph::{
nodes::{AssetUniformNode, PassNode, UniformNode},
nodes::{AssetUniformNode, UniformNode},
RenderGraph,
},
shader::{Shader, ShaderStage, ShaderStages},
@ -149,11 +149,6 @@ impl SpriteRenderGraphBuilder for RenderGraph {
SPRITE_SHEET_PIPELINE_HANDLE,
build_sprite_sheet_pipeline(&mut shaders),
);
let main_pass: &mut PassNode = self
.get_node_mut(base_render_graph::node::MAIN_PASS)
.unwrap();
main_pass.add_pipeline(SPRITE_PIPELINE_HANDLE);
main_pass.add_pipeline(SPRITE_SHEET_PIPELINE_HANDLE);
self
}
}

View file

@ -2,7 +2,7 @@ use super::Node;
use crate::{render::UI_PIPELINE_HANDLE, widget::Label};
use bevy_asset::Handle;
use bevy_derive::EntityArchetype;
use bevy_render::{mesh::Mesh, Renderable};
use bevy_render::{draw::{Draw, RenderPipelines}, mesh::Mesh};
use bevy_sprite::{ColorMaterial, Quad, QUAD_HANDLE};
#[derive(EntityArchetype)]
@ -11,7 +11,8 @@ pub struct UiEntity {
pub quad: Quad,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
pub material: Handle<ColorMaterial>,
pub renderable: Renderable,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
}
impl Default for UiEntity {
@ -21,7 +22,8 @@ impl Default for UiEntity {
quad: Default::default(),
mesh: QUAD_HANDLE,
material: Default::default(),
renderable: Renderable {
draw: Default::default(),
render_pipelines: RenderPipelines {
pipelines: vec![UI_PIPELINE_HANDLE],
..Default::default()
},
@ -35,7 +37,8 @@ pub struct LabelEntity {
pub quad: Quad,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
pub material: Handle<ColorMaterial>,
pub renderable: Renderable,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub label: Label,
}
@ -47,7 +50,8 @@ impl Default for LabelEntity {
mesh: QUAD_HANDLE,
// NOTE: labels each get their own material.
material: Handle::new(), // TODO: maybe abstract this out
renderable: Renderable {
draw: Default::default(),
render_pipelines: RenderPipelines {
pipelines: vec![UI_PIPELINE_HANDLE],
..Default::default()
},

View file

@ -2,10 +2,7 @@ use bevy_asset::{Assets, Handle};
use bevy_render::{
base_render_graph,
pipeline::{state_descriptors::*, PipelineDescriptor},
render_graph::{
nodes::{CameraNode, PassNode},
RenderGraph,
},
render_graph::{nodes::CameraNode, RenderGraph},
shader::{Shader, ShaderStage, ShaderStages},
texture::TextureFormat,
};
@ -79,10 +76,6 @@ impl UiRenderGraphBuilder for RenderGraph {
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
pipelines.set(UI_PIPELINE_HANDLE, build_ui_pipeline(&mut shaders));
let main_pass: &mut PassNode = self
.get_node_mut(base_render_graph::node::MAIN_PASS)
.unwrap();
main_pass.add_pipeline(UI_PIPELINE_HANDLE);
self
}
}

View file

@ -9,9 +9,8 @@ pub use wgpu_render_pass::*;
pub use wgpu_renderer::*;
pub use wgpu_resources::*;
use bevy_app::{AppBuilder, AppPlugin, Events};
use bevy_app::{AppBuilder, AppPlugin};
use bevy_render::renderer::RenderResources;
use bevy_window::{WindowCreated, WindowResized};
use legion::prelude::*;
use renderer::WgpuRenderResourceContext;
@ -26,14 +25,7 @@ impl AppPlugin for WgpuPlugin {
}
pub fn wgpu_render_system(resources: &mut Resources) -> impl FnMut(&mut World, &mut Resources) {
let mut wgpu_renderer = {
let window_resized_event = resources.get::<Events<WindowResized>>().unwrap();
let window_created_event = resources.get::<Events<WindowCreated>>().unwrap();
pollster::block_on(WgpuRenderer::new(
window_resized_event.get_reader(),
window_created_event.get_reader(),
))
};
let mut wgpu_renderer = pollster::block_on(WgpuRenderer::new());
resources.insert(RenderResources::new(WgpuRenderResourceContext::new(
wgpu_renderer.device.clone(),
)));

View file

@ -1,9 +1,7 @@
mod systems;
mod wgpu_render_context;
mod wgpu_render_graph_executor;
mod wgpu_render_resource_context;
pub use systems::*;
pub use wgpu_render_context::*;
pub use wgpu_render_graph_executor::*;
pub use wgpu_render_resource_context::*;

View file

@ -1,92 +0,0 @@
use bevy_asset::Assets;
use bevy_render::{
pipeline::{PipelineAssignments, PipelineCompiler, PipelineDescriptor},
render_resource::EntityRenderResourceAssignments,
Renderable,
};
use legion::prelude::*;
// TODO: replace with system_fn once configurable "archetype access" is sorted out
// pub fn render_resource_sets_system(
// world: &mut SubWorld,
// pipelines: Res<Assets<PipelineDescriptor>>,
// pipeline_compiler: Res<PipelineCompiler>,
// pipeline_assignments: Res<PipelineAssignments>,
// entity_render_resource_assignments: Res<EntityRenderResourceAssignments>,
// query: &mut Query<Write<Renderable>>, // gives SubWorld write access to Renderable
// ) {
// // PERF: consider doing a par-iter over all renderable components so this can be parallelized
// for compiled_pipeline_handle in pipeline_compiler.iter_all_compiled_pipelines() {
// if let Some(compiled_pipeline_assignments) = pipeline_assignments
// .assignments
// .get(compiled_pipeline_handle)
// {
// let compiled_pipeline = pipelines.get(compiled_pipeline_handle).unwrap();
// let pipeline_layout = compiled_pipeline.get_layout().unwrap();
// for assignment_id in compiled_pipeline_assignments.iter() {
// let entity = entity_render_resource_assignments
// .get(*assignment_id)
// .unwrap();
// let mut renderable = world.get_component_mut::<Renderable>(*entity).unwrap();
// if !renderable.is_visible || renderable.is_instanced {
// continue;
// }
// for bind_group in pipeline_layout.bind_groups.iter() {
// renderable
// .render_resource_assignments
// .update_render_resource_set_id(bind_group);
// }
// }
// }
// }
// }
pub fn render_resource_sets_system() -> Box<dyn Schedulable> {
SystemBuilder::new("update_render_resource_sets")
.read_resource::<Assets<PipelineDescriptor>>()
.read_resource::<PipelineCompiler>()
.read_resource::<PipelineAssignments>()
.read_resource::<EntityRenderResourceAssignments>()
.write_component::<Renderable>()
.build(
|_,
world,
(
pipelines,
pipeline_compiler,
pipeline_assignments,
entity_render_resource_assignments,
),
_| {
// PERF: consider doing a par-iter over all renderable components so this can be parallelized
for compiled_pipeline_handle in pipeline_compiler.iter_all_compiled_pipelines() {
if let Some(compiled_pipeline_assignments) = pipeline_assignments
.assignments
.get(compiled_pipeline_handle)
{
let compiled_pipeline = pipelines.get(compiled_pipeline_handle).unwrap();
let pipeline_layout = compiled_pipeline.get_layout().unwrap();
for assignment_id in compiled_pipeline_assignments.iter() {
let entity = entity_render_resource_assignments
.get(*assignment_id)
.unwrap();
let mut renderable =
world.get_component_mut::<Renderable>(*entity).unwrap();
if !renderable.is_visible || renderable.is_instanced {
continue;
}
for bind_group in pipeline_layout.bind_groups.iter() {
renderable
.render_resource_assignments
.update_render_resource_set_id(bind_group);
}
}
}
}
},
)
}

View file

@ -5,10 +5,9 @@ use crate::{
use bevy_asset::{Assets, Handle, HandleUntyped};
use bevy_render::{
pipeline::{BindGroupDescriptor, PipelineDescriptor},
pipeline::{BindGroupDescriptor, BindGroupDescriptorId, PipelineDescriptor},
render_resource::{
BufferInfo, RenderResourceId, RenderResourceAssignment, RenderResourceAssignments,
RenderResourceSetId, ResourceInfo,
BufferInfo, RenderResourceAssignment, RenderResourceId, RenderResourceSet, ResourceInfo,
},
renderer::RenderResourceContext,
shader::Shader,
@ -207,7 +206,11 @@ impl RenderResourceContext for WgpuRenderResourceContext {
resource
}
fn create_buffer_with_data(&self, mut buffer_info: BufferInfo, data: &[u8]) -> RenderResourceId {
fn create_buffer_with_data(
&self,
mut buffer_info: BufferInfo,
data: &[u8],
) -> RenderResourceId {
// TODO: consider moving this below "create" for efficiency
let mut resource_info = self.resources.resource_info.write().unwrap();
let mut buffers = self.resources.buffers.write().unwrap();
@ -450,87 +453,69 @@ impl RenderResourceContext for WgpuRenderResourceContext {
fn create_bind_group(
&self,
bind_group_descriptor: &BindGroupDescriptor,
render_resource_assignments: &RenderResourceAssignments,
) -> Option<RenderResourceSetId> {
if let Some(render_resource_set) =
render_resource_assignments.get_render_resource_set(bind_group_descriptor.id)
bind_group_descriptor_id: BindGroupDescriptorId,
render_resource_set: &RenderResourceSet,
) {
if !self
.resources
.has_bind_group(bind_group_descriptor_id, render_resource_set.id)
{
if !self
.resources
.has_bind_group(bind_group_descriptor.id, render_resource_set.id)
{
log::trace!(
"start creating bind group for RenderResourceSet {:?}",
render_resource_set.id
);
let texture_views = self.resources.texture_views.read().unwrap();
let samplers = self.resources.samplers.read().unwrap();
let buffers = self.resources.buffers.read().unwrap();
let bind_group_layouts = self.resources.bind_group_layouts.read().unwrap();
let mut bind_groups = self.resources.bind_groups.write().unwrap();
log::trace!(
"start creating bind group for RenderResourceSet {:?}",
render_resource_set.id
);
let texture_views = self.resources.texture_views.read().unwrap();
let samplers = self.resources.samplers.read().unwrap();
let buffers = self.resources.buffers.read().unwrap();
let bind_group_layouts = self.resources.bind_group_layouts.read().unwrap();
let mut bind_groups = self.resources.bind_groups.write().unwrap();
let bindings = bind_group_descriptor
.bindings
.iter()
.map(|binding| {
if let Some(assignment) = render_resource_assignments.get(&binding.name) {
log::trace!(
"found binding {} ({}) assignment: {:?}",
binding.index,
binding.name,
assignment,
);
let wgpu_resource = match assignment {
RenderResourceAssignment::Texture(resource) => {
let texture = texture_views.get(&resource).unwrap();
wgpu::BindingResource::TextureView(texture)
}
RenderResourceAssignment::Sampler(resource) => {
let sampler = samplers.get(&resource).unwrap();
wgpu::BindingResource::Sampler(sampler)
}
RenderResourceAssignment::Buffer { resource, range , .. } => {
let buffer = buffers.get(&resource).unwrap();
wgpu::BindingResource::Buffer(buffer.slice(range.clone()))
}
};
wgpu::Binding {
binding: binding.index,
resource: wgpu_resource,
}
} else {
panic!(
"No resource assigned to uniform \"{}\" for RenderResourceAssignments {:?}",
binding.name,
render_resource_assignments.id
);
let bindings = render_resource_set
.indexed_assignments
.iter()
.map(|indexed_assignment| {
let wgpu_resource = match &indexed_assignment.assignment {
RenderResourceAssignment::Texture(resource) => {
let texture = texture_views.get(&resource).unwrap();
wgpu::BindingResource::TextureView(texture)
}
})
.collect::<Vec<wgpu::Binding>>();
RenderResourceAssignment::Sampler(resource) => {
let sampler = samplers.get(&resource).unwrap();
wgpu::BindingResource::Sampler(sampler)
}
RenderResourceAssignment::Buffer {
resource, range, ..
} => {
let buffer = buffers.get(&resource).unwrap();
wgpu::BindingResource::Buffer(buffer.slice(range.clone()))
}
};
wgpu::Binding {
binding: indexed_assignment.index,
resource: wgpu_resource,
}
})
.collect::<Vec<wgpu::Binding>>();
let bind_group_layout = bind_group_layouts.get(&bind_group_descriptor.id).unwrap();
let wgpu_bind_group_descriptor = wgpu::BindGroupDescriptor {
label: None,
layout: bind_group_layout,
bindings: bindings.as_slice(),
};
let wgpu_bind_group = self.device.create_bind_group(&wgpu_bind_group_descriptor);
let bind_group_layout = bind_group_layouts.get(&bind_group_descriptor_id).unwrap();
let wgpu_bind_group_descriptor = wgpu::BindGroupDescriptor {
label: None,
layout: bind_group_layout,
bindings: bindings.as_slice(),
};
let wgpu_bind_group = self.device.create_bind_group(&wgpu_bind_group_descriptor);
let bind_group_info = bind_groups
.entry(bind_group_descriptor.id)
.or_insert_with(|| WgpuBindGroupInfo::default());
bind_group_info
.bind_groups
.insert(render_resource_set.id, wgpu_bind_group);
log::trace!(
"created bind group for RenderResourceSet {:?}",
render_resource_set.id
);
return Some(render_resource_set.id);
}
let bind_group_info = bind_groups
.entry(bind_group_descriptor_id)
.or_insert_with(|| WgpuBindGroupInfo::default());
bind_group_info
.bind_groups
.insert(render_resource_set.id, wgpu_bind_group);
log::trace!(
"created bind group for RenderResourceSet {:?}",
render_resource_set.id
);
}
None
}
fn clear_bind_groups(&self) {

View file

@ -2,8 +2,8 @@ use crate::{renderer::WgpuRenderContext, WgpuResourceRefs};
use bevy_asset::Handle;
use bevy_render::{
pass::RenderPass,
pipeline::{BindGroupDescriptor, PipelineDescriptor},
render_resource::{RenderResourceId, RenderResourceSet},
pipeline::{PipelineDescriptor, BindGroupDescriptorId},
render_resource::{RenderResourceId, RenderResourceSetId},
renderer::RenderContext,
};
use std::ops::Range;
@ -51,33 +51,35 @@ impl<'a> RenderPass for WgpuRenderPass<'a> {
fn set_bind_group(
&mut self,
bind_group_descriptor: &BindGroupDescriptor,
render_resource_set: &RenderResourceSet,
index: u32,
bind_group_descriptor: BindGroupDescriptorId,
render_resource_set: RenderResourceSetId,
dynamic_uniform_indices: Option<&[u32]>,
) {
if let Some(bind_group_info) = self
.render_resources
.bind_groups
.get(&bind_group_descriptor.id)
.get(&bind_group_descriptor)
{
if let Some(wgpu_bind_group) = bind_group_info.bind_groups.get(&render_resource_set.id)
if let Some(wgpu_bind_group) = bind_group_info.bind_groups.get(&render_resource_set)
{
const EMPTY: &'static [u32] = &[];
let dynamic_uniform_indices = if let Some(ref dynamic_uniform_indices) =
render_resource_set.dynamic_uniform_indices
let dynamic_uniform_indices = if let Some(dynamic_uniform_indices) =
dynamic_uniform_indices
{
dynamic_uniform_indices.as_slice()
dynamic_uniform_indices
} else {
EMPTY
};
log::trace!(
"set bind group {:?} {:?}: {:?}",
bind_group_descriptor.id,
bind_group_descriptor,
dynamic_uniform_indices,
render_resource_set.id
render_resource_set
);
self.render_pass.set_bind_group(
bind_group_descriptor.index,
index,
wgpu_bind_group,
dynamic_uniform_indices,
);

View file

@ -1,10 +1,10 @@
use crate::renderer::{
render_resource_sets_system, WgpuRenderGraphExecutor, WgpuRenderResourceContext,
};
use crate::renderer::{WgpuRenderGraphExecutor, WgpuRenderResourceContext};
use bevy_app::{EventReader, Events};
use bevy_render::{
pipeline::update_shader_assignments,
draw::{draw_system, RenderPipelines},
pipeline::compile_pipelines_system,
render_graph::{DependentNodeStager, RenderGraph, RenderGraphStager},
render_resource::render_resource_sets_system,
renderer::RenderResources,
};
use bevy_window::{WindowCreated, WindowResized, Windows};
@ -20,10 +20,7 @@ pub struct WgpuRenderer {
}
impl WgpuRenderer {
pub async fn new(
window_resized_event_reader: EventReader<WindowResized>,
window_created_event_reader: EventReader<WindowCreated>,
) -> Self {
pub async fn new() -> Self {
let instance = wgpu::Instance::new();
let adapter = instance
.request_adapter(
@ -53,8 +50,8 @@ impl WgpuRenderer {
instance,
device,
queue,
window_resized_event_reader,
window_created_event_reader,
window_resized_event_reader: Default::default(),
window_created_event_reader: Default::default(),
intialized: false,
}
}
@ -85,6 +82,7 @@ impl WgpuRenderer {
}
pub fn run_graph(&mut self, world: &mut World, resources: &mut Resources) {
// TODO: move this to a thread-local system
// run systems
let mut system_executor = {
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
@ -95,8 +93,12 @@ impl WgpuRenderer {
executor.execute(world, resources);
}
update_shader_assignments(world, resources);
render_resource_sets_system().run(world, resources);
// TODO: move these to a scheduler
compile_pipelines_system.system().run(world, resources);
render_resource_sets_system.system().run(world, resources);
draw_system::<RenderPipelines>
.system()
.run(world, resources);
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
if let Some(executor) = system_executor.take() {

View file

@ -283,13 +283,12 @@ fn stateful_system(mut state: ComMut<State>, player: Com<Player>, score: ComMut<
// NOTE: this doesn't do anything relevant to our game, it is just here for illustrative purposes
#[allow(dead_code)]
fn complex_system(resources: &mut Resources) -> Box<dyn Schedulable> {
let mut counter = 0;
let game_state = resources.get::<GameState>().unwrap();
let initial_player_count = game_state.total_players;
SystemBuilder::new("complex_system")
.read_resource::<GameState>()
.write_resource::<GameRules>()
.read_component::<Renderable>()
.read_component::<Draw>()
// this query is equivalent to the system we saw above: system(player: Com<Player>, mut score: ComMut<Score>)
.with_query(<(Read<Player>, Write<Score>)>::query())
// this query only returns entities with a Player component that has changed since the last update
@ -303,7 +302,6 @@ fn complex_system(resources: &mut Resources) -> Box<dyn Schedulable> {
for (player, score) in player_score_query.iter_mut(world) {
println!("processed : {} {}", player.name, score.value);
counter += 1;
}
for player in player_changed_query.iter(world) {

View file

@ -54,8 +54,6 @@ fn setup(
}));
render_graph.add_system_node("my_material", AssetUniformNode::<MyMaterial>::new(true));
let main_pass: &mut PassNode = render_graph.get_node_mut("main_pass").unwrap();
main_pass.add_pipeline(pipeline_handle);
pipeline_handle
};
@ -70,7 +68,7 @@ fn setup(
// cube
.add_entity(MeshMaterialEntity::<MyMaterial> {
mesh: cube_handle,
renderable: Renderable {
render_pipelines: RenderPipelines {
pipelines: vec![pipeline_handle],
..Default::default()
},

View file

@ -64,8 +64,6 @@ fn setup(
fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))),
}));
render_graph.add_system_node("my_material", AssetUniformNode::<MyMaterial>::new(true));
let main_pass: &mut PassNode = render_graph.get_node_mut("main_pass").unwrap();
main_pass.add_pipeline(pipeline_handle);
pipeline_handle
};
@ -87,7 +85,7 @@ fn setup(
// cube
.add_entity(MeshMaterialEntity::<MyMaterial> {
mesh: cube_handle,
renderable: Renderable {
render_pipelines: RenderPipelines {
pipelines: vec![pipeline_handle],
..Default::default()
},
@ -98,7 +96,7 @@ fn setup(
// cube
.add_entity(MeshMaterialEntity::<MyMaterial> {
mesh: cube_handle,
renderable: Renderable {
render_pipelines: RenderPipelines {
pipelines: vec![pipeline_handle],
..Default::default()
},

View file

@ -19,7 +19,7 @@ pub use crate::{
pipeline::PipelineDescriptor,
render_graph::{
nodes::{
AssetUniformNode, CameraNode, PassNode, UniformNode, WindowSwapChainNode,
AssetUniformNode, CameraNode, MainPassNode, UniformNode, WindowSwapChainNode,
WindowTextureNode,
},
RenderGraph,
@ -27,7 +27,8 @@ pub use crate::{
render_resource::RenderResources,
shader::{Shader, ShaderDefs, ShaderStage, ShaderStages},
texture::Texture,
Camera, Color, ColorSource, OrthographicProjection, PerspectiveProjection, Renderable,
draw::{Draw, RenderPipelines},
Camera, Color, ColorSource, OrthographicProjection, PerspectiveProjection,
},
scene::{Scene, SceneSpawner},
sprite::{