initial multi-threaded resource creation using WgpuRenderContext

This commit is contained in:
Carter Anderson 2020-04-09 15:03:54 -07:00
parent d8c748644c
commit a7605b2d7a
11 changed files with 312 additions and 31 deletions

View File

@ -3,6 +3,8 @@ mod camera;
pub mod entity;
pub mod mesh;
pub mod render_graph;
pub mod render_graph_2;
pub mod renderer_2;
pub mod shader;
mod color;

View File

View File

@ -1,9 +1,17 @@
use crate::{mesh::Mesh, texture::Texture};
use bevy_asset::Handle;
use std::collections::HashMap;
use uuid::Uuid;
// TODO: Rename to RenderResourceId
#[derive(Copy, Clone, Hash, Debug, Eq, PartialEq)]
pub struct RenderResource(pub u64);
pub struct RenderResource(Uuid);
impl RenderResource {
pub fn new() -> Self {
RenderResource(Uuid::new_v4())
}
}
// TODO: consider scoping breaking these mappings up by type: Texture, Sampler, etc
// the overlap could cause accidents.
@ -13,7 +21,6 @@ pub struct RenderResources {
pub texture_to_sampler_resource: HashMap<Handle<Texture>, RenderResource>,
pub mesh_to_vertices_resource: HashMap<Handle<Mesh>, RenderResource>,
pub mesh_to_indices_resource: HashMap<Handle<Mesh>, RenderResource>,
pub resource_index: u64,
}
impl RenderResources {
@ -52,11 +59,4 @@ impl RenderResources {
pub fn get_texture_sampler_resource(&self, texture: Handle<Texture>) -> Option<RenderResource> {
self.texture_to_sampler_resource.get(&texture).cloned()
}
pub fn get_next_resource(&mut self) -> RenderResource {
let resource = self.resource_index;
self.resource_index += 1;
RenderResource(resource)
}
}

View File

@ -0,0 +1,3 @@
mod render_context;
pub use render_context::*;

View File

@ -0,0 +1,61 @@
use crate::{
render_resource::{
BufferInfo, RenderResource, RenderResources, ResourceInfo,
},
texture::{SamplerDescriptor, TextureDescriptor},
};
pub trait RenderContext {
fn create_sampler(&mut self, sampler_descriptor: &SamplerDescriptor) -> RenderResource;
fn create_texture(
&mut self,
texture_descriptor: &TextureDescriptor,
) -> RenderResource;
fn create_buffer(&mut self, buffer_info: BufferInfo) -> RenderResource;
fn create_buffer_mapped(
&mut self,
buffer_info: BufferInfo,
setup_data: &mut dyn FnMut(&mut [u8], &mut dyn RenderContext),
) -> RenderResource;
fn create_buffer_with_data(&mut self, buffer_info: BufferInfo, data: &[u8]) -> RenderResource;
fn remove_buffer(&mut self, resource: RenderResource);
fn remove_texture(&mut self, resource: RenderResource);
fn remove_sampler(&mut self, resource: RenderResource);
fn get_resource_info(&self, resource: RenderResource) -> Option<&ResourceInfo>;
fn get_resource_info_mut(&mut self, resource: RenderResource) -> Option<&mut ResourceInfo>;
fn render_resources(&self) -> &RenderResources;
fn render_resources_mut(&mut self) -> &mut RenderResources;
// fn setup_render_pipeline(
// &mut self,
// pipeline_handle: Handle<PipelineDescriptor>,
// pipeline_descriptor: &mut PipelineDescriptor,
// shader_storage: &AssetStorage<Shader>,
// );
// fn setup_bind_groups(
// &mut self,
// render_resource_assignments: &mut RenderResourceAssignments,
// pipeline_descriptor: &PipelineDescriptor,
// );
fn create_texture_with_data(
&mut self,
texture_descriptor: &TextureDescriptor,
bytes: Option<&[u8]>,
) -> RenderResource;
fn copy_buffer_to_buffer(
&mut self,
source_buffer: RenderResource,
source_offset: u64,
destination_buffer: RenderResource,
destination_offset: u64,
size: u64,
);
// fn copy_buffer_to_texture(
// &mut self,
// source_buffer: RenderResource,
// source_offset: u64,
// destination_buffer: RenderResource,
// destination_offset: u64,
// size: u64,
// );
}

View File

@ -22,4 +22,5 @@ legion = { path = "../bevy_legion" }
wgpu = { version = "0.5.0" }
futures = "0.3"
log = { version = "0.4", features = ["release_max_level_info"] }
log = { version = "0.4", features = ["release_max_level_info"] }
crossbeam-channel = "0.4.2"

View File

@ -2,6 +2,7 @@ mod wgpu_render_pass;
mod wgpu_renderer;
mod wgpu_resources;
mod wgpu_type_converter;
pub mod renderer_2;
pub use wgpu_render_pass::*;
pub use wgpu_renderer::*;

View File

@ -0,0 +1,3 @@
mod wgpu_render_context;
pub use wgpu_render_context::*;

View File

@ -0,0 +1,133 @@
use crate::WgpuResources;
use bevy_render::{
render_resource::{BufferInfo, RenderResource, RenderResources, ResourceInfo},
renderer_2::RenderContext,
texture::{SamplerDescriptor, TextureDescriptor},
};
use std::sync::Arc;
#[derive(Default)]
struct LazyCommandEncoder {
command_encoder: Option<wgpu::CommandEncoder>,
}
impl LazyCommandEncoder {
pub fn get_or_create(&mut self, device: &wgpu::Device) -> &mut wgpu::CommandEncoder {
match self.command_encoder {
Some(ref mut command_encoder) => command_encoder,
None => {
let command_encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
self.command_encoder = Some(command_encoder);
self.command_encoder.as_mut().unwrap()
}
}
}
pub fn take(&mut self) -> Option<wgpu::CommandEncoder> {
self.command_encoder.take()
}
}
pub struct WgpuRenderContext {
pub device: Arc<wgpu::Device>,
command_encoder: LazyCommandEncoder,
wgpu_resources: WgpuResources,
}
impl WgpuRenderContext {
pub fn new(device: Arc<wgpu::Device>) -> Self {
WgpuRenderContext {
device,
command_encoder: LazyCommandEncoder::default(),
wgpu_resources: WgpuResources::default(),
}
}
pub fn finish(&mut self) -> Option<wgpu::CommandBuffer> {
self.command_encoder.take().map(|encoder| encoder.finish())
}
}
impl RenderContext for WgpuRenderContext {
fn create_sampler(&mut self, sampler_descriptor: &SamplerDescriptor) -> RenderResource {
self.wgpu_resources
.create_sampler(&self.device, sampler_descriptor)
}
fn create_texture(&mut self, texture_descriptor: &TextureDescriptor) -> RenderResource {
self.wgpu_resources
.create_texture(&self.device, texture_descriptor)
}
fn create_buffer(&mut self, buffer_info: BufferInfo) -> RenderResource {
self.wgpu_resources.create_buffer(&self.device, buffer_info)
}
// TODO: clean this up
fn create_buffer_mapped(
&mut self,
buffer_info: BufferInfo,
setup_data: &mut dyn FnMut(&mut [u8], &mut dyn RenderContext),
) -> RenderResource {
let buffer = WgpuResources::begin_create_buffer_mapped_render_context(
&buffer_info,
self,
setup_data,
);
self.wgpu_resources.assign_buffer(buffer, buffer_info)
}
fn create_texture_with_data(
&mut self,
texture_descriptor: &TextureDescriptor,
bytes: Option<&[u8]>,
) -> RenderResource {
self.wgpu_resources.create_texture_with_data(
&self.device,
self.command_encoder.get_or_create(&self.device),
texture_descriptor,
bytes,
)
}
fn remove_buffer(&mut self, resource: RenderResource) {
self.wgpu_resources.remove_buffer(resource);
}
fn remove_texture(&mut self, resource: RenderResource) {
self.wgpu_resources.remove_texture(resource);
}
fn remove_sampler(&mut self, resource: RenderResource) {
self.wgpu_resources.remove_sampler(resource);
}
fn get_resource_info(&self, resource: RenderResource) -> Option<&ResourceInfo> {
self.wgpu_resources.resource_info.get(&resource)
}
fn get_resource_info_mut(&mut self, resource: RenderResource) -> Option<&mut ResourceInfo> {
self.wgpu_resources.resource_info.get_mut(&resource)
}
fn render_resources(&self) -> &RenderResources {
&self.wgpu_resources.render_resources
}
fn render_resources_mut(&mut self) -> &mut RenderResources {
&mut self.wgpu_resources.render_resources
}
fn copy_buffer_to_buffer(
&mut self,
source_buffer: RenderResource,
source_offset: u64,
destination_buffer: RenderResource,
destination_offset: u64,
size: u64,
) {
self.wgpu_resources.copy_buffer_to_buffer(
self.command_encoder.get_or_create(&self.device),
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
);
}
fn create_buffer_with_data(&mut self, buffer_info: BufferInfo, data: &[u8]) -> RenderResource {
self.wgpu_resources
.create_buffer_with_data(&self.device, buffer_info, data)
}
}

View File

@ -2,6 +2,7 @@ use super::{
wgpu_type_converter::{OwnedWgpuVertexBufferDescriptor, WgpuInto},
WgpuRenderPass, WgpuResources,
};
use crate::renderer_2::WgpuRenderContext;
use bevy_app::{EventReader, Events};
use bevy_asset::{AssetStorage, Handle};
use bevy_render::{
@ -12,8 +13,8 @@ use bevy_render::{
pipeline::{update_shader_assignments, PipelineCompiler, PipelineDescriptor},
render_graph::RenderGraph,
render_resource::{
resource_name, BufferInfo, RenderResource, RenderResourceAssignments, RenderResources,
ResourceInfo,
resource_name, BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments,
RenderResources, ResourceInfo,
},
renderer::Renderer,
shader::Shader,
@ -359,6 +360,43 @@ impl Renderer for WgpuRenderer {
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }),
);
// use bevy_render::renderer_2::RenderContext;
// let thread_count = 5;
// let (sender, receiver) = crossbeam_channel::bounded(thread_count);
// for i in 0..thread_count {
// let device = self.device.clone();
// let sender = sender.clone();
// std::thread::spawn(move || {
// let mut context = WgpuRenderContext::new(device);
// let data: Vec::<u8> = vec![1, 2, 3,4 ];
// let data2: Vec::<u8> = vec![4, 2, 3,4 ];
// let buffer = context.create_buffer_with_data(BufferInfo {
// buffer_usage: BufferUsage::COPY_SRC,
// ..Default::default()
// }, &data);
// let buffer2 = context.create_buffer_with_data(BufferInfo {
// buffer_usage: BufferUsage::UNIFORM |BufferUsage::COPY_DST,
// ..Default::default()
// }, &data2);
// context.copy_buffer_to_buffer(buffer, 0, buffer2, 0, data.len() as u64);
// sender.send(context.finish()).unwrap();
// });
// }
// let mut command_buffers = Vec::new();
// for i in 0..thread_count {
// if let Some(command_buffer) = receiver.recv().unwrap() {
// command_buffers.push(command_buffer);
// }
// println!("got {}", i);
// }
// self.queue.submit(&command_buffers);
self.update_resource_providers(world, resources);
update_shader_assignments(world, resources, self);
self.create_queued_textures(resources);
@ -434,8 +472,7 @@ impl Renderer for WgpuRenderer {
}
fn create_buffer(&mut self, buffer_info: BufferInfo) -> RenderResource {
self.wgpu_resources
.create_buffer(&self.device, buffer_info)
self.wgpu_resources.create_buffer(&self.device, buffer_info)
}
fn get_resource_info(&self, resource: RenderResource) -> Option<&ResourceInfo> {
@ -487,7 +524,7 @@ impl Renderer for WgpuRenderer {
texture_descriptor: &TextureDescriptor,
bytes: Option<&[u8]>,
) -> RenderResource {
self.wgpu_resources.create_texture(
self.wgpu_resources.create_texture_with_data(
&self.device,
self.encoder.as_mut().unwrap(),
texture_descriptor,
@ -564,10 +601,11 @@ impl Renderer for WgpuRenderer {
})
.collect::<Vec<wgpu::BindGroupLayoutEntry>>();
let wgpu_bind_group_layout =
self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: bind_group_layout_binding.as_slice(),
label: None,
});
self.device
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: bind_group_layout_binding.as_slice(),
label: None,
});
self.wgpu_resources
.bind_group_layouts
@ -587,9 +625,11 @@ impl Renderer for WgpuRenderer {
})
.collect::<Vec<&wgpu::BindGroupLayout>>();
let pipeline_layout = self.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: bind_group_layouts.as_slice(),
});
let pipeline_layout = self
.device
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: bind_group_layouts.as_slice(),
});
let owned_vertex_buffer_descriptors = layout
.vertex_buffer_descriptors
@ -617,8 +657,11 @@ impl Renderer for WgpuRenderer {
if let Some(fragment_handle) = pipeline_descriptor.shader_stages.fragment {
if let None = self.wgpu_resources.shader_modules.get(&fragment_handle) {
self.wgpu_resources
.create_shader_module(&self.device, fragment_handle, shader_storage);
self.wgpu_resources.create_shader_module(
&self.device,
fragment_handle,
shader_storage,
);
}
};
@ -673,7 +716,9 @@ impl Renderer for WgpuRenderer {
alpha_to_coverage_enabled: pipeline_descriptor.alpha_to_coverage_enabled,
};
let render_pipeline = self.device.create_render_pipeline(&mut render_pipeline_descriptor);
let render_pipeline = self
.device
.create_render_pipeline(&mut render_pipeline_descriptor);
self.render_pipelines
.insert(pipeline_handle, render_pipeline);
}

View File

@ -1,5 +1,5 @@
use super::WgpuRenderer;
use crate::wgpu_type_converter::WgpuInto;
use crate::{renderer_2::WgpuRenderContext, wgpu_type_converter::WgpuInto};
use bevy_asset::{AssetStorage, Handle};
use bevy_render::{
pipeline::{BindGroupDescriptor, BindGroupDescriptorId, BindType},
@ -9,7 +9,7 @@ use bevy_render::{
},
renderer::Renderer,
shader::Shader,
texture::{SamplerDescriptor, TextureDescriptor},
texture::{SamplerDescriptor, TextureDescriptor}, renderer_2::RenderContext,
};
use bevy_window::WindowId;
use std::collections::HashMap;
@ -154,7 +154,7 @@ impl WgpuResources {
usage: buffer_info.buffer_usage.wgpu_into(),
});
let resource = self.render_resources.get_next_resource();
let resource = RenderResource::new();
self.add_resource_info(resource, ResourceInfo::Buffer(buffer_info));
self.buffers.insert(resource, buffer);
@ -186,7 +186,7 @@ impl WgpuResources {
buffer: wgpu::Buffer,
buffer_info: BufferInfo,
) -> RenderResource {
let resource = self.render_resources.get_next_resource();
let resource = RenderResource::new();
self.add_resource_info(resource, ResourceInfo::Buffer(buffer_info));
self.buffers.insert(resource, buffer);
resource
@ -209,6 +209,24 @@ impl WgpuResources {
mapped.finish()
}
// TODO: clean this up
pub fn begin_create_buffer_mapped_render_context(
buffer_info: &BufferInfo,
render_context: &mut WgpuRenderContext,
setup_data: &mut dyn FnMut(&mut [u8], &mut dyn RenderContext),
) -> wgpu::Buffer {
let device = render_context.device.clone();
let mut mapped = device.create_buffer_mapped(
&wgpu::BufferDescriptor {
size: buffer_info.size as u64,
usage: buffer_info.buffer_usage.wgpu_into(),
label: None,
}
);
setup_data(&mut mapped.data, render_context);
mapped.finish()
}
pub fn copy_buffer_to_buffer(
&mut self,
encoder: &mut wgpu::CommandEncoder,
@ -242,13 +260,27 @@ impl WgpuResources {
) -> RenderResource {
let descriptor: wgpu::SamplerDescriptor = (*sampler_descriptor).wgpu_into();
let sampler = device.create_sampler(&descriptor);
let resource = self.render_resources.get_next_resource();
let resource = RenderResource::new();
self.samplers.insert(resource, sampler);
self.add_resource_info(resource, ResourceInfo::Sampler);
resource
}
pub fn create_texture(
&mut self,
device: &wgpu::Device,
texture_descriptor: &TextureDescriptor,
) -> RenderResource {
let descriptor: wgpu::TextureDescriptor = (*texture_descriptor).wgpu_into();
let texture = device.create_texture(&descriptor);
let texture_view = texture.create_default_view();
let resource = RenderResource::new();
self.add_resource_info(resource, ResourceInfo::Texture);
self.textures.insert(resource, texture_view);
resource
}
pub fn create_texture_with_data(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
@ -277,7 +309,7 @@ impl WgpuResources {
);
}
let resource = self.render_resources.get_next_resource();
let resource = RenderResource::new();
self.add_resource_info(resource, ResourceInfo::Texture);
self.textures.insert(resource, texture_view);
resource