bevy/crates/bevy_wgpu/src/renderer/wgpu_render_context.rs
2020-05-05 13:34:05 -07:00

261 lines
8.8 KiB
Rust

use super::WgpuRenderResourceContext;
use crate::{wgpu_type_converter::WgpuInto, WgpuRenderPass, WgpuResourceRefs};
use bevy_render::{
pass::{
PassDescriptor, RenderPass, RenderPassColorAttachmentDescriptor,
RenderPassDepthStencilAttachmentDescriptor, TextureAttachment,
},
render_resource::{RenderResource, RenderResourceAssignments},
renderer::{RenderContext, RenderResourceContext},
texture::{Extent3d, TextureDescriptor},
};
use std::{collections::HashMap, sync::Arc};
#[derive(Default)]
pub 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 => {
self.create(device);
self.command_encoder.as_mut().unwrap()
}
}
}
pub fn is_some(&self) -> bool {
self.command_encoder.is_some()
}
pub fn create(&mut self, device: &wgpu::Device) {
let command_encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
self.command_encoder = Some(command_encoder);
}
pub fn take(&mut self) -> Option<wgpu::CommandEncoder> {
self.command_encoder.take()
}
pub fn set(&mut self, command_encoder: wgpu::CommandEncoder) {
self.command_encoder = Some(command_encoder);
}
}
pub struct WgpuRenderContext {
pub device: Arc<wgpu::Device>,
pub command_encoder: LazyCommandEncoder,
pub render_resources: WgpuRenderResourceContext,
}
impl WgpuRenderContext {
pub fn new(device: Arc<wgpu::Device>, resources: WgpuRenderResourceContext) -> Self {
WgpuRenderContext {
device,
render_resources: resources,
command_encoder: LazyCommandEncoder::default(),
}
}
/// Consume this context, finalize the current CommandEncoder (if it exists), and take the current WgpuResources.
/// This is intended to be called from a worker thread right before synchronizing with the main thread.
pub fn finish(&mut self) -> Option<wgpu::CommandBuffer> {
self.command_encoder.take().map(|encoder| encoder.finish())
}
}
impl RenderContext for WgpuRenderContext {
fn create_texture_with_data(
&mut self,
texture_descriptor: &TextureDescriptor,
bytes: &[u8],
) -> RenderResource {
self.render_resources.create_texture_with_data(
self.command_encoder.get_or_create(&self.device),
texture_descriptor,
bytes,
)
}
fn copy_buffer_to_buffer(
&mut self,
source_buffer: RenderResource,
source_offset: u64,
destination_buffer: RenderResource,
destination_offset: u64,
size: u64,
) {
self.render_resources.copy_buffer_to_buffer(
self.command_encoder.get_or_create(&self.device),
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
);
}
fn copy_buffer_to_texture(
&mut self,
source_buffer: RenderResource,
source_offset: u64,
source_bytes_per_row: u32,
destination_texture: RenderResource,
destination_origin: [u32; 3],
destination_mip_level: u32,
destination_array_layer: u32,
size: Extent3d,
) {
self.render_resources.copy_buffer_to_texture(
self.command_encoder.get_or_create(&self.device),
source_buffer,
source_offset,
source_bytes_per_row,
destination_texture,
destination_origin,
destination_mip_level,
destination_array_layer,
size,
)
}
fn resources(&self) -> &dyn RenderResourceContext {
&self.render_resources
}
fn resources_mut(&mut self) -> &mut dyn RenderResourceContext {
&mut self.render_resources
}
fn begin_pass(
&mut self,
pass_descriptor: &PassDescriptor,
render_resource_assignments: &RenderResourceAssignments,
run_pass: &mut dyn Fn(&mut dyn RenderPass),
) {
if !self.command_encoder.is_some() {
self.command_encoder.create(&self.device);
}
let resource_lock = self.render_resources.resources.read();
let refs = resource_lock.refs();
let mut encoder = self.command_encoder.take().unwrap();
{
let render_pass = create_render_pass(
pass_descriptor,
render_resource_assignments,
&refs,
&mut encoder,
);
let mut wgpu_render_pass = WgpuRenderPass {
render_context: self,
render_pass,
render_resources: refs,
bound_bind_groups: HashMap::default(),
};
run_pass(&mut wgpu_render_pass);
}
self.command_encoder.set(encoder);
}
}
pub fn create_render_pass<'a, 'b>(
pass_descriptor: &PassDescriptor,
global_render_resource_assignments: &'b RenderResourceAssignments,
refs: &WgpuResourceRefs<'a>,
encoder: &'a mut wgpu::CommandEncoder,
) -> wgpu::RenderPass<'a> {
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &pass_descriptor
.color_attachments
.iter()
.map(|c| {
create_wgpu_color_attachment_descriptor(global_render_resource_assignments, refs, c)
})
.collect::<Vec<wgpu::RenderPassColorAttachmentDescriptor>>(),
depth_stencil_attachment: pass_descriptor.depth_stencil_attachment.as_ref().map(|d| {
create_wgpu_depth_stencil_attachment_descriptor(
global_render_resource_assignments,
refs,
d,
)
}),
})
}
fn get_texture_view<'a>(
global_render_resource_assignments: &RenderResourceAssignments,
refs: &WgpuResourceRefs<'a>,
attachment: &TextureAttachment,
) -> &'a wgpu::TextureView {
match attachment {
TextureAttachment::Name(name) => match global_render_resource_assignments.get(&name) {
Some(resource) => refs.textures.get(&resource).unwrap(),
None => {
panic!("Color attachment {} does not exist", name);
}
},
TextureAttachment::RenderResource(render_resource) => refs.textures.get(&render_resource).unwrap_or_else(|| &refs.swap_chain_outputs.get(&render_resource).unwrap().view),
TextureAttachment::Input(_) => panic!("Encountered unset TextureAttachment::Input. The RenderGraph executor should always set TextureAttachment::Inputs to TextureAttachment::RenderResource before running. This is a bug"),
}
}
fn create_wgpu_color_attachment_descriptor<'a>(
global_render_resource_assignments: &RenderResourceAssignments,
refs: &WgpuResourceRefs<'a>,
color_attachment_descriptor: &RenderPassColorAttachmentDescriptor,
) -> wgpu::RenderPassColorAttachmentDescriptor<'a> {
let attachment = get_texture_view(
global_render_resource_assignments,
refs,
&color_attachment_descriptor.attachment,
);
let resolve_target = color_attachment_descriptor
.resolve_target
.as_ref()
.map(|target| get_texture_view(global_render_resource_assignments, refs, &target));
wgpu::RenderPassColorAttachmentDescriptor {
store_op: color_attachment_descriptor.store_op.wgpu_into(),
load_op: color_attachment_descriptor.load_op.wgpu_into(),
clear_color: color_attachment_descriptor.clear_color.wgpu_into(),
attachment,
resolve_target,
}
}
fn create_wgpu_depth_stencil_attachment_descriptor<'a>(
global_render_resource_assignments: &RenderResourceAssignments,
refs: &WgpuResourceRefs<'a>,
depth_stencil_attachment_descriptor: &RenderPassDepthStencilAttachmentDescriptor,
) -> wgpu::RenderPassDepthStencilAttachmentDescriptor<'a> {
let attachment = get_texture_view(
global_render_resource_assignments,
refs,
&depth_stencil_attachment_descriptor.attachment,
);
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment,
clear_depth: depth_stencil_attachment_descriptor.clear_depth,
clear_stencil: depth_stencil_attachment_descriptor.clear_stencil,
depth_load_op: depth_stencil_attachment_descriptor
.depth_load_op
.wgpu_into(),
depth_store_op: depth_stencil_attachment_descriptor
.depth_store_op
.wgpu_into(),
stencil_load_op: depth_stencil_attachment_descriptor
.stencil_load_op
.wgpu_into(),
stencil_store_op: depth_stencil_attachment_descriptor
.stencil_store_op
.wgpu_into(),
}
}