Skip to content

Commit

Permalink
Add RenderCommand abstraction and cleanup pipeline generation in prep…
Browse files Browse the repository at this point in the history
… for draw work.
  • Loading branch information
tychedelia committed Jan 31, 2024
1 parent ffcd528 commit a37611e
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 129 deletions.
58 changes: 5 additions & 53 deletions bevy_nannou/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,14 @@ pub struct NannouPlugin;

impl Plugin for NannouPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup).add_plugins((
bevy_nannou_render::NannouRenderPlugin,
bevy_nannou_draw::NannouDrawPlugin,
));
app
.add_plugins((
bevy_nannou_render::NannouRenderPlugin,
bevy_nannou_draw::NannouDrawPlugin,
));
}
}

fn setup(
mut commands: Commands,
assets: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let mut mesh = ViewMesh::default();
let image = assets.load("images/img.png");
mesh.texture = Some(image);
mesh.extend_from_slices(
&[
Vec3::new(-512.0, -512.0, 0.0),
Vec3::new(512.0, -512.0, 0.0),
Vec3::new(-512.0, 512.0, 0.0),
Vec3::new(512.0, 512.0, 0.0),
],
&[1, 0, 2, 1, 2, 3],
&[Vec4::new(1.0, 0.0, 0.0, 1.0); 4],
&[
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 1.0),
Vec2::new(0.0, 0.0),
Vec2::new(1.0, 0.0),
],
);
commands.spawn((
Camera3dBundle {
transform: Transform::from_xyz(0.0, 0.0, -10.0).looking_at(Vec3::ZERO, Vec3::Z),
projection: OrthographicProjection {
scale: 1.0,
..Default::default()
}
.into(),
..Default::default()
},
mesh,
));
commands.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 1.0,
});
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(Cube::new(10.0))),
material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..Default::default()
});
}

#[cfg(test)]
mod tests {
use bevy::app::App;
Expand Down
40 changes: 33 additions & 7 deletions bevy_nannou_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,15 @@ use bevy::prelude::*;
use bevy::render::extract_component::ExtractComponentPlugin;
use bevy::render::render_asset::RenderAsset;
use bevy::render::render_graph::{RenderGraphApp, ViewNode, ViewNodeRunner};
use bevy::render::render_phase::{AddRenderCommand, PhaseItem, RenderCommand};
use bevy::render::render_resource::{
ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines,
};
use bevy::render::render_resource::{CachedRenderPipelineId, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines};
use bevy::render::renderer::RenderDevice;
use bevy::render::view::ViewUniforms;
use bevy::render::{render_resource as wgpu, RenderSet};
use bevy::render::{Render, RenderApp};

use mesh::ViewMesh;
use pipeline::queue_pipelines;

use crate::pipeline::{NannouPipeline, NannouViewNode};
use crate::pipeline::{NannouPipeline, NannouViewNode, TextureBindGroupCache};

pub mod mesh;
mod pipeline;
Expand All @@ -44,11 +40,11 @@ impl Plugin for NannouRenderPlugin {
app.get_sub_app_mut(RenderApp)
.unwrap()
.init_resource::<SpecializedRenderPipelines<NannouPipeline>>()
.init_resource::<TextureBindGroupCache>()
.add_systems(
Render,
prepare_view_uniform.in_set(RenderSet::PrepareBindGroups),
)
.add_systems(Render, queue_pipelines.in_set(RenderSet::PrepareAssets))

// Register the NannouViewNode with the render graph
// The node runs at the last stage of the main 3d pass
Expand Down Expand Up @@ -125,6 +121,36 @@ pub enum VertexMode {
Text = 2,
}

/// Commands that map to wgpu encodable commands.
#[derive(Debug, Clone)]
enum RenderCommand {
/// Change pipeline for the new blend mode and topology.
SetPipeline(CachedRenderPipelineId),
/// Change bind group for a new image.
SetBindGroup(Handle<Image>),
/// Set the rectangular scissor.
SetScissor(Scissor),
/// Draw the given vertex range.
DrawIndexed {
start_vertex: i32,
index_range: std::ops::Range<u32>,
},
}

#[derive(Component)]
pub struct ViewRenderCommands {
commands: Vec<RenderCommand>,
}

/// The position and dimensions of the scissor.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Scissor {
left: u32,
bottom: u32,
width: u32,
height: u32,
}

pub struct GlyphCache {
/// Tracks glyphs and their location within the cache.
pub cache: text::GlyphCache<'static>,
Expand Down
4 changes: 2 additions & 2 deletions bevy_nannou_render/src/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use nannou_mesh::{self, MeshPoints, WithColors, WithIndices, WithTexCoords};
use std::ops::{Deref, DerefMut};
use bevy::render::extract_component::ExtractComponent;
use crate::mesh::vertex::Point;
use crate::RenderCommand;

pub mod builder;
pub mod vertex;
Expand All @@ -27,7 +28,6 @@ pub type MeshType =
#[derive(Component, Debug, Clone, ExtractComponent)]
pub struct ViewMesh {
mesh: MeshType,
pub texture: Option<Handle<Image>>,
}

impl ViewMesh {
Expand Down Expand Up @@ -184,7 +184,7 @@ impl ViewMesh {
impl Default for ViewMesh {
fn default() -> Self {
let mesh = Default::default();
ViewMesh { mesh, texture: None }
ViewMesh { mesh }
}
}

Expand Down
108 changes: 41 additions & 67 deletions bevy_nannou_render/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use bevy::utils;

use crate::mesh::vertex::Point;
use crate::mesh::{TexCoords, ViewMesh};
use crate::{GlyphCache, mesh, NANNOU_SHADER_HANDLE, VertexMode, ViewUniformBindGroup};
use crate::{GlyphCache, mesh, NANNOU_SHADER_HANDLE, RenderCommand, Scissor, VertexMode, ViewRenderCommands, ViewUniformBindGroup};

#[derive(Resource)]
pub struct NannouPipeline {
Expand Down Expand Up @@ -293,36 +293,11 @@ impl FromWorld for NannouPipeline {
}
}

// A resource that caches the render pipelines for each camera.
#[derive(Resource, Deref, DerefMut)]
pub struct NannouPipelines(pub utils::HashMap<Entity, CachedRenderPipelineId>);

// Ensures that the render pipeline is cached for each camera.
pub fn queue_pipelines(
mut commands: Commands,
pipeline: Res<NannouPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<NannouPipeline>>,
pipeline_cache: Res<PipelineCache>,
cameras: Query<Entity, Has<ExtractedCamera>>,
) {
let pipelines = cameras
.iter()
.filter_map(|entity| {
let key = NannouPipelineKey {
// TODO: this should be configurable based on a user registered resource
sample_count: NannouPipeline::DEFAULT_SAMPLE_COUNT,
depth_format: NannouPipeline::DEFAULT_DEPTH_FORMAT,
blend_state: NannouPipeline::DEFAULT_BLEND_STATE,
topology: NannouPipeline::DEFAULT_PRIMITIVE_TOPOLOGY,
};
let pipeline_id = pipelines.specialize(&pipeline_cache, &pipeline, key);

Some((entity, pipeline_id))
})
.collect();

commands.insert_resource(NannouPipelines(pipelines));
#[derive(Resource, Default)]
pub struct TextureBindGroupCache {
pub bind_groups: HashMap<Handle<Image>, wgpu::BindGroup>,
}

pub struct NannouViewNode;

impl NannouViewNode {
Expand All @@ -341,25 +316,20 @@ impl ViewNode for NannouViewNode {
&'static ViewTarget,
&'static ViewUniformOffset,
&'static ViewMesh,
&'static ViewRenderCommands,
&'static ViewDepthTexture,
);

fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
(entity, target, uniform_offset, mesh, depth_texture): QueryItem<Self::ViewQuery>,
(entity, target, uniform_offset, mesh, render_commands, depth_texture): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
let nannou_pipeline = world.resource::<NannouPipeline>();
let nannou_pipelines = world.resource::<NannouPipelines>();
let pipeline_cache = world.resource::<PipelineCache>();
let Some(pipeline_id) = nannou_pipelines.get(&entity) else {
return Ok(());
};
let Some(pipeline) = pipeline_cache.get_render_pipeline(*pipeline_id) else {
return Ok(());
};
let bind_group_cache = world.resource::<TextureBindGroupCache>();

// Create render pass builder.
let render_pass_builder = bevy_nannou_wgpu::RenderPassBuilder::new()
Expand Down Expand Up @@ -401,32 +371,7 @@ impl ViewNode for NannouViewNode {
usage: wgpu::BufferUsages::INDEX,
});

let texture_bind_group = match &mesh.texture {
Some(texture) => {
let images = world.resource::<RenderAssets<Image>>();
if let Some(gpu_image) = images.get(texture) {
let texture_bind_group_layout = NannouPipeline::create_texture_bind_group_layout(
render_device,
true,
wgpu::TextureSampleType::Float { filterable: true },
);
NannouPipeline::create_texture_bind_group(
render_device,
&texture_bind_group_layout,
&gpu_image.sampler,
&gpu_image.texture_view,
)
} else {
nannou_pipeline.texture_bind_group.clone()
}
}
None => {
nannou_pipeline.texture_bind_group.clone()
}
};

let mut render_pass = render_pass_builder.begin(render_context);
render_pass.set_render_pipeline(pipeline);

// Set the buffers.
render_pass.set_index_buffer(index_buffer.slice(..), 0, wgpu::IndexFormat::Uint32);
Expand All @@ -439,11 +384,40 @@ impl ViewNode for NannouViewNode {
let uniform_bind_group = world.resource::<ViewUniformBindGroup>();
render_pass.set_bind_group(0, &uniform_bind_group.bind_group, &[uniform_offset.offset]);
render_pass.set_bind_group(1, &nannou_pipeline.text_bind_group, &[]);
render_pass.set_bind_group(2, &texture_bind_group, &[]);

// Draw the mesh.
let indices = 0..mesh.indices().len() as u32;
render_pass.draw_indexed(indices, 0, 0..1);
// Follow the render commands.
for cmd in render_commands.commands.clone() {
match cmd {
RenderCommand::SetPipeline(id) => {
let pipeline = pipeline_cache.get_render_pipeline(id).expect("Expected pipeline to exist");
render_pass.set_render_pipeline(pipeline);
}

RenderCommand::SetBindGroup(texture) => {
let bind_group = bind_group_cache
.bind_groups
.get(&texture)
.expect("Expected texture bind group to exist");
render_pass.set_bind_group(2, bind_group, &[]);
}
RenderCommand::SetScissor(Scissor {
left,
bottom,
width,
height,
}) => {
render_pass.set_scissor_rect(left, bottom, width, height);
}
RenderCommand::DrawIndexed {
start_vertex,
index_range,
} => {
let instance_range = 0..1u32;
render_pass.draw_indexed(index_range, start_vertex, instance_range);
}
}
}

Ok(())
}
}

0 comments on commit a37611e

Please sign in to comment.