Initial RenderGraph2. Port CameraResourceProvider

This commit is contained in:
Carter Anderson 2020-04-20 14:29:21 -07:00
parent 3c83e34cc1
commit 210a50e781
5 changed files with 284 additions and 147 deletions

View file

@ -54,7 +54,8 @@ use bevy_app::{stage, AppBuilder, AppPlugin, GetEventReader};
use bevy_asset::AssetStorage;
use bevy_transform::prelude::LocalToWorld;
use bevy_window::WindowResized;
use render_resource::resource_providers::mesh_resource_provider_system;
use render_resource::resource_providers::{CameraNode, mesh_resource_provider_system};
use render_graph_2::RenderGraph2;
pub static RENDER_RESOURCE_STAGE: &str = "render_resource";
pub static RENDER_STAGE: &str = "render";
@ -77,9 +78,6 @@ impl RenderPlugin {
.add_draw_target(AssignedBatchesDrawTarget::default())
.add_draw_target(AssignedMeshesDrawTarget::default())
.add_draw_target(UiDrawTarget::default())
.add_resource_provider(CameraResourceProvider::new(
app.resources().get_event_reader::<WindowResized>(),
))
.add_resource_provider(Camera2dResourceProvider::new(
resources.get_event_reader::<WindowResized>(),
))
@ -93,21 +91,24 @@ impl RenderPlugin {
impl AppPlugin for RenderPlugin {
fn build(&self, app: &mut AppBuilder) {
let mut render_graph = RenderGraph2::default();
render_graph.add_system_node(CameraNode::default(), app.resources_mut());
let mut asset_batchers = AssetBatchers::default();
asset_batchers.batch_types2::<Mesh, StandardMaterial>();
app.add_stage_after(stage::POST_UPDATE, RENDER_RESOURCE_STAGE)
.add_stage_after(RENDER_RESOURCE_STAGE, RENDER_STAGE)
// resources
.add_resource(RenderGraph::default())
.add_resource(render_graph)
.add_resource(AssetStorage::<Mesh>::new())
.add_resource(AssetStorage::<Texture>::new())
.add_resource(AssetStorage::<Shader>::new())
.add_resource(AssetStorage::<StandardMaterial>::new())
.add_resource(AssetStorage::<PipelineDescriptor>::new())
.add_resource(PipelineAssignments::new())
.add_resource(VertexBufferDescriptors::default())
.add_resource(PipelineCompiler::new())
.add_resource(RenderResourceAssignments::default())
.add_resource(VertexBufferDescriptors::default())
.add_resource(EntityRenderResourceAssignments::default())
.add_resource(asset_batchers)
// core systems

View file

@ -1 +1,159 @@
use crate::{
render_resource::{RenderResource, ResourceInfo},
renderer_2::RenderContext,
};
use legion::prelude::{Executor, Resources, Schedulable, World};
use std::{
collections::{HashMap, VecDeque},
sync::{Arc, Mutex},
};
use uuid::Uuid;
pub enum Command {
CopyBufferToBuffer {
source_buffer: RenderResource,
source_offset: u64,
destination_buffer: RenderResource,
destination_offset: u64,
size: u64,
},
}
#[derive(Default, Clone)]
pub struct CommandQueue {
queue: Arc<Mutex<VecDeque<Command>>>,
}
impl CommandQueue {
fn push(&mut self, command: Command) {
self.queue.lock().unwrap().push_front(command);
}
pub fn copy_buffer_to_buffer(
&mut self,
source_buffer: RenderResource,
source_offset: u64,
destination_buffer: RenderResource,
destination_offset: u64,
size: u64,
) {
self.push(Command::CopyBufferToBuffer {
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
});
}
pub fn execute(&mut self, render_context: &mut dyn RenderContext) {
for command in self.queue.lock().unwrap().drain(..) {
match command {
Command::CopyBufferToBuffer {
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
} => render_context.copy_buffer_to_buffer(
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
),
}
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct NodeId(Uuid);
impl NodeId {
fn new() -> Self {
NodeId(Uuid::new_v4())
}
}
pub struct ResourceSlot {
name: Option<String>,
resource_type: ResourceInfo,
}
pub struct ResourceSlotBinding {
resource: RenderResource,
}
pub struct NodeDescriptor {
pub inputs: Vec<ResourceSlot>,
pub outputs: Vec<ResourceSlot>,
}
pub trait Node: Send + Sync + 'static {
fn descriptor(&self) -> &NodeDescriptor;
fn update(
&mut self,
world: &World,
resources: &Resources,
render_context: &mut dyn RenderContext,
);
}
pub trait SystemNode: Node {
fn get_system(&self, resources: &mut Resources) -> Box<dyn Schedulable>;
}
#[derive(Default)]
pub struct RenderGraph2 {
nodes: HashMap<NodeId, Box<dyn Node>>,
new_systems: Vec<Box<dyn Schedulable>>,
system_executor: Option<Executor>,
}
impl RenderGraph2 {
pub fn add_node<T>(&mut self, node: T) -> NodeId
where
T: Node + 'static,
{
let id = NodeId::new();
self.nodes.insert(id, Box::new(node));
id
}
pub fn add_system_node<T>(&mut self, node: T, resources: &mut Resources) -> NodeId
where
T: SystemNode + 'static,
{
let id = NodeId::new();
self.new_systems.push(node.get_system(resources));
self.nodes.insert(id, Box::new(node));
id
}
pub fn get_schedule(&mut self) -> impl Iterator<Item = &mut Box<dyn Node>> {
self.nodes.values_mut()
}
pub fn take_executor(&mut self) -> Option<Executor> {
// rebuild executor if there are new systems
if self.new_systems.len() > 0 {
let mut systems = self
.system_executor
.take()
.map(|executor| executor.into_vec())
.unwrap_or_else(|| Vec::new());
for system in self.new_systems.drain(..) {
systems.push(system);
}
self.system_executor = Some(Executor::new(systems));
}
self.system_executor.take()
}
pub fn set_executor(&mut self, executor: Executor) {
self.system_executor = Some(executor);
}
}

View file

@ -1,162 +1,107 @@
use bevy_window::WindowResized;
use crate::{
render_resource::{
resource_name, BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments,
ResourceProvider,
},
render_graph_2::{CommandQueue, Node, NodeDescriptor, SystemNode},
render_resource::{resource_name, BufferInfo, BufferUsage, RenderResourceAssignments},
renderer_2::{GlobalRenderResourceContext, RenderContext},
ActiveCamera, Camera,
};
use bevy_app::{EventReader, Events, GetEventReader};
use bevy_app::{Events, GetEventReader};
use bevy_transform::prelude::*;
use legion::prelude::*;
use once_cell::sync::Lazy;
use zerocopy::AsBytes;
pub fn camera_resource_provider_system(resources: &mut Resources) -> Box<dyn Schedulable> {
let mut camera_buffer = None;
let mut tmp_buffer = None;
let mut window_resized_event_reader = resources.get_event_reader::<WindowResized>();
SystemBuilder::new("camera_resource_provider")
.read_resource::<GlobalRenderResourceContext>()
// TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same
.write_resource::<RenderResourceAssignments>()
.read_resource::<Events<WindowResized>>()
.with_query(<(Read<Camera>, Read<LocalToWorld>, Read<ActiveCamera>)>::query())
.build(
move |_,
world,
(
render_resource_context,
ref mut render_resource_assignments,
window_resized_events,
),
query| {
let render_resources = &render_resource_context.context;
if camera_buffer.is_none() {
let buffer = render_resources.create_buffer(BufferInfo {
size: std::mem::size_of::<[[f32; 4]; 4]>(),
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
..Default::default()
});
render_resource_assignments.set(resource_name::uniform::CAMERA, buffer);
camera_buffer = Some(buffer);
}
let primary_window_resized_event = window_resized_events
.find_latest(&mut window_resized_event_reader, |event| event.is_primary);
if let Some(_) = primary_window_resized_event {
let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
for (camera, local_to_world, _) in query.iter(world) {
let camera_matrix: [[f32; 4]; 4] =
(camera.view_matrix * local_to_world.0).to_cols_array_2d();
if let Some(old_tmp_buffer) = tmp_buffer {
render_resources.remove_buffer(old_tmp_buffer);
}
tmp_buffer = Some(render_resources.create_buffer_mapped(
BufferInfo {
size: matrix_size,
buffer_usage: BufferUsage::COPY_SRC,
..Default::default()
},
&mut |data, _renderer| {
data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes());
},
));
// render_resources.copy_buffer_to_buffer(
// tmp_buffer.unwrap(),
// 0,
// camera_buffer.unwrap(),
// 0,
// matrix_size as u64,
// );
}
}
},
)
#[derive(Default)]
pub struct CameraNode {
command_queue: CommandQueue,
}
pub struct CameraResourceProvider {
pub camera_buffer: Option<RenderResource>,
pub tmp_buffer: Option<RenderResource>,
pub window_resized_event_reader: EventReader<WindowResized>,
}
impl CameraResourceProvider {
pub fn new(window_resized_event_reader: EventReader<WindowResized>) -> Self {
CameraResourceProvider {
camera_buffer: None,
tmp_buffer: None,
window_resized_event_reader,
}
}
}
impl ResourceProvider for CameraResourceProvider {
fn initialize(
&mut self,
render_context: &mut dyn RenderContext,
_world: &mut World,
resources: &Resources,
) {
let buffer = render_context.resources_mut().create_buffer(BufferInfo {
size: std::mem::size_of::<[[f32; 4]; 4]>(),
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
..Default::default()
impl Node for CameraNode {
fn descriptor(&self) -> &NodeDescriptor {
static DESCRIPTOR: Lazy<NodeDescriptor> = Lazy::new(|| NodeDescriptor {
inputs: Vec::new(),
outputs: Vec::new(),
});
let mut render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
render_resource_assignments.set(resource_name::uniform::CAMERA, buffer);
self.camera_buffer = Some(buffer);
&DESCRIPTOR
}
fn update(
&mut self,
_world: &World,
_resources: &Resources,
render_context: &mut dyn RenderContext,
world: &World,
resources: &Resources,
) {
let window_resized_events = resources.get::<Events<WindowResized>>().unwrap();
let primary_window_resized_event = window_resized_events
.find_latest(&mut self.window_resized_event_reader, |event| {
event.is_primary
});
if let Some(_) = primary_window_resized_event {
let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
for (camera, local_to_world, _) in
<(Read<Camera>, Read<LocalToWorld>, Read<ActiveCamera>)>::query().iter(world)
{
let camera_matrix: [[f32; 4]; 4] =
(camera.view_matrix * local_to_world.0).to_cols_array_2d();
if let Some(old_tmp_buffer) = self.tmp_buffer {
render_context.resources_mut().remove_buffer(old_tmp_buffer);
}
self.tmp_buffer = Some(render_context.resources_mut().create_buffer_mapped(
BufferInfo {
size: matrix_size,
buffer_usage: BufferUsage::COPY_SRC,
..Default::default()
},
&mut |data, _renderer| {
data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes());
},
));
render_context.copy_buffer_to_buffer(
self.tmp_buffer.unwrap(),
0,
self.camera_buffer.unwrap(),
0,
matrix_size as u64,
);
}
}
self.command_queue.execute(render_context);
}
}
impl SystemNode for CameraNode {
fn get_system(&self, resources: &mut Resources) -> Box<dyn Schedulable> {
let mut camera_buffer = None;
let mut tmp_buffer = None;
let mut window_resized_event_reader = resources.get_event_reader::<WindowResized>();
let mut command_queue = self.command_queue.clone();
SystemBuilder::new("camera_resource_provider")
.read_resource::<GlobalRenderResourceContext>()
// TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same
.write_resource::<RenderResourceAssignments>()
.read_resource::<Events<WindowResized>>()
.with_query(<(Read<Camera>, Read<LocalToWorld>, Read<ActiveCamera>)>::query())
.build(
move |_,
world,
(
render_resource_context,
ref mut render_resource_assignments,
window_resized_events,
),
query| {
let render_resources = &render_resource_context.context;
if camera_buffer.is_none() {
let buffer = render_resources.create_buffer(BufferInfo {
size: std::mem::size_of::<[[f32; 4]; 4]>(),
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
..Default::default()
});
render_resource_assignments.set(resource_name::uniform::CAMERA, buffer);
camera_buffer = Some(buffer);
}
let primary_window_resized_event = window_resized_events
.find_latest(&mut window_resized_event_reader, |event| event.is_primary);
if let Some(_) = primary_window_resized_event {
let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
for (camera, local_to_world, _) in query.iter(world) {
let camera_matrix: [[f32; 4]; 4] =
(camera.view_matrix * local_to_world.0).to_cols_array_2d();
if let Some(old_tmp_buffer) = tmp_buffer {
render_resources.remove_buffer(old_tmp_buffer);
}
tmp_buffer = Some(render_resources.create_buffer_mapped(
BufferInfo {
size: matrix_size,
buffer_usage: BufferUsage::COPY_SRC,
..Default::default()
},
&mut |data, _renderer| {
data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes());
},
));
command_queue.copy_buffer_to_buffer(
tmp_buffer.unwrap(),
0,
camera_buffer.unwrap(),
0,
matrix_size as u64,
);
}
}
},
)
}
}

View file

@ -6,6 +6,7 @@ use bevy_asset::AssetStorage;
use bevy_render::{
pipeline::{update_shader_assignments, PipelineCompiler, PipelineDescriptor},
render_graph::RenderGraph,
render_graph_2::RenderGraph2,
render_resource::RenderResourceAssignments,
renderer_2::{GlobalRenderResourceContext, RenderContext, RenderResourceContext},
};
@ -225,7 +226,39 @@ impl WgpuRenderer {
}
}
pub fn run_graph(&mut self, world: &mut World, resources: &mut Resources) {
let mut executor = {
let mut render_graph = resources.get_mut::<RenderGraph2>().unwrap();
render_graph.take_executor()
};
if let Some(executor) = executor.as_mut() {
executor.execute(world, resources);
}
let mut render_graph = resources.get_mut::<RenderGraph2>().unwrap();
if let Some(executor) = executor.take() {
render_graph.set_executor(executor);
}
let mut global_context = resources.get_mut::<GlobalRenderResourceContext>().unwrap();
let render_resource_context = global_context
.context
.downcast_mut::<WgpuRenderResourceContext>()
.unwrap();
let mut render_context =
WgpuRenderContext::new(self.device.clone(), render_resource_context.clone());
for node in render_graph.get_schedule() {
node.update(world, resources, &mut render_context);
}
let command_buffer = render_context.finish();
if let Some(command_buffer) = command_buffer {
self.queue.submit(&[command_buffer]);
}
}
pub fn update(&mut self, world: &mut World, resources: &mut Resources) {
self.run_graph(world, resources);
let mut encoder = {
let mut global_context = resources.get_mut::<GlobalRenderResourceContext>().unwrap();
let render_resource_context = global_context

View file

@ -19,7 +19,7 @@ pub struct WgpuBindGroupInfo {
}
/// Grabs a read lock on all wgpu resources. When paired with WgpuResourceRefs, this allows
/// us to pass in wgpu resources to wgpu::RenderPass<'a> with the appropriate lifetime. This is accomplished by
/// you to pass in wgpu resources to wgpu::RenderPass<'a> with the appropriate lifetime. This is accomplished by
/// grabbing a WgpuResourcesReadLock _before_ creating a wgpu::RenderPass, getting a WgpuResourcesRefs, and storing that
/// in the pass.
///
@ -88,7 +88,7 @@ pub struct WgpuResources {
}
impl WgpuResources {
pub fn read<'a>(&'a self) -> WgpuResourcesReadLock<'a> {
pub fn read(&self) -> WgpuResourcesReadLock {
WgpuResourcesReadLock {
buffers: self.buffers.read().unwrap(),
textures: self.textures.read().unwrap(),