From 8d3026899d13bad26f743f8148c9f0d7601b8a4b Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 1 Mar 2020 10:22:58 -0800 Subject: [PATCH] more texture work --- ROADMAP.md | 1 + .../draw_targets/ui_draw_target.rs | 2 +- src/render/render_graph/pipeline_layout.rs | 18 +- src/render/render_graph/render_resource.rs | 11 ++ src/render/render_graph/renderer.rs | 15 +- .../render_graph/renderers/wgpu_renderer.rs | 185 ++++++++++++++++-- .../camera2d_resource_provider.rs | 2 +- .../camera_resource_provider.rs | 2 +- .../frame_texture_resource_provider.rs | 4 +- .../light_resource_provider.rs | 2 +- .../ui_resource_provider.rs | 2 +- .../uniform_resource_provider.rs | 43 +++- 12 files changed, 243 insertions(+), 44 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index ae424e6a31..6a6abe2243 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -12,6 +12,7 @@ Here is the current list of planned features. All items are sorted in approximat * Macro to produce vertex buffer attributes (and maybe descriptors) from structs * Add runtime type safety to uniform bindings (and maybe compile time) * Inject layout set/bindings into shader source so they don't need to be defined in-shader. Specify set / binding indices in resource providers? + * Pull as much logic as possible from wgpu_renderer into a "render orchestrator" struct/trait * Error Handling * Custom error type? * Remove as many panics / unwraps as possible diff --git a/src/render/render_graph/draw_targets/ui_draw_target.rs b/src/render/render_graph/draw_targets/ui_draw_target.rs index f2dc242f03..5f1cde23dc 100644 --- a/src/render/render_graph/draw_targets/ui_draw_target.rs +++ b/src/render/render_graph/draw_targets/ui_draw_target.rs @@ -16,7 +16,7 @@ pub fn ui_draw_target( let mut current_mesh_index_buffer = None; let ui_instances_buffer = { let renderer = render_pass.get_renderer(); - match renderer.get_named_resource(resource_name::buffer::UI_INSTANCES) { + match renderer.get_render_resources().get_named_resource(resource_name::buffer::UI_INSTANCES) { Some(buffer) => buffer, None => return, } diff --git a/src/render/render_graph/pipeline_layout.rs b/src/render/render_graph/pipeline_layout.rs index 3804e6e1d2..232b3535ce 100644 --- a/src/render/render_graph/pipeline_layout.rs +++ b/src/render/render_graph/pipeline_layout.rs @@ -1,4 +1,4 @@ -use crate::render::shader_reflect::ShaderLayout; +use crate::{asset::Texture, render::shader_reflect::ShaderLayout}; use std::{ collections::{hash_map::DefaultHasher, BTreeSet, HashMap}, hash::{Hash, Hasher}, @@ -270,3 +270,19 @@ impl From for wgpu::SamplerDescriptor { } } } + +impl From<&Texture> for SamplerDescriptor { + fn from(_texture: &Texture) -> Self { + SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Nearest, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Nearest, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + } + } +} \ No newline at end of file diff --git a/src/render/render_graph/render_resource.rs b/src/render/render_graph/render_resource.rs index d234b66b5e..43daf2b248 100644 --- a/src/render/render_graph/render_resource.rs +++ b/src/render/render_graph/render_resource.rs @@ -4,10 +4,13 @@ use std::collections::HashMap; #[derive(Copy, Clone, Hash, Debug, Eq, PartialEq)] pub struct RenderResource(pub u64); +// TODO: consider scoping breaking these mappings up by type: Texture, Sampler, etc +// the overlap could cause accidents. #[derive(Default)] pub struct RenderResources { pub name_to_resource: HashMap, pub texture_to_resource: HashMap, RenderResource>, + pub texture_to_sampler_resource: HashMap, RenderResource>, pub resource_index: u64, } @@ -28,6 +31,14 @@ impl RenderResources { self.texture_to_resource.get(&texture).cloned() } + pub fn set_texture_sampler_resource(&mut self, texture: Handle, resource: RenderResource) { + self.texture_to_sampler_resource.insert(texture, resource); + } + + pub fn get_texture_sampler_resource(&self, texture: Handle) -> Option { + 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; diff --git a/src/render/render_graph/renderer.rs b/src/render/render_graph/renderer.rs index 2898d2b641..aa1ae133fa 100644 --- a/src/render/render_graph/renderer.rs +++ b/src/render/render_graph/renderer.rs @@ -1,9 +1,10 @@ use crate::{ legion::prelude::*, render::render_graph::{ - render_resource::RenderResource, DynamicUniformBufferInfo, PipelineDescriptor, RenderGraph, - ResourceInfo, TextureDescriptor, SamplerDescriptor - }, asset::{Handle, Texture}, + render_resource::{RenderResource, RenderResources}, + DynamicUniformBufferInfo, PipelineDescriptor, RenderGraph, ResourceInfo, SamplerDescriptor, + TextureDescriptor, + }, }; use std::ops::Range; @@ -77,10 +78,10 @@ pub trait Renderer { destination_offset: u64, size: u64, ); - fn get_named_resource(&self, name: &str) -> Option; - fn set_named_resource(&mut self, name: &str, resource: RenderResource); - fn get_texture_resource(&self, texture: Handle) -> Option; - fn set_texture_resource(&mut self, texture: Handle, resource: RenderResource); + fn get_render_resources(&self) -> &RenderResources; + fn get_render_resources_mut(&mut self) -> &mut RenderResources; + fn set_entity_uniform_resource(&mut self, entity: Entity, uniform_name: &str, resource: RenderResource); + fn get_entity_uniform_resource(&mut self, entity: Entity, uniform_name: &str) -> Option; } pub trait RenderPass { diff --git a/src/render/render_graph/renderers/wgpu_renderer.rs b/src/render/render_graph/renderers/wgpu_renderer.rs index 21e9687316..b79a0f27b1 100644 --- a/src/render/render_graph/renderers/wgpu_renderer.rs +++ b/src/render/render_graph/renderers/wgpu_renderer.rs @@ -1,5 +1,5 @@ use crate::{ - asset::{AssetStorage, Handle, Texture}, + asset::{AssetStorage, Handle}, legion::prelude::*, render::{ render_graph::{ @@ -12,7 +12,7 @@ use crate::{ Shader, }, }; -use std::{collections::HashMap, ops::Deref}; +use std::{collections::HashMap, ops::Deref, borrow::Cow}; pub struct WgpuRenderer { pub device: wgpu::Device, @@ -27,6 +27,8 @@ pub struct WgpuRenderer { pub resource_info: HashMap, pub bind_groups: HashMap, pub bind_group_layouts: HashMap, + pub entity_bind_groups: HashMap<(Entity, u64), BindGroupInfo>, + pub entity_uniform_resources: HashMap<(Cow<'static, Entity>, Cow<'static, str>), RenderResource>, pub dynamic_uniform_buffer_info: HashMap, pub render_resources: RenderResources, } @@ -70,6 +72,8 @@ impl WgpuRenderer { bind_groups: HashMap::new(), bind_group_layouts: HashMap::new(), dynamic_uniform_buffer_info: HashMap::new(), + entity_bind_groups: HashMap::new(), + entity_uniform_resources: HashMap::new(), render_resources: RenderResources::default(), } } @@ -300,7 +304,7 @@ impl WgpuRenderer { } // TODO: consider moving this to a resource provider - fn setup_bind_group(&mut self, bind_group: &BindGroup) -> u64 { + fn setup_bind_group(&mut self, bind_group: &BindGroup) { let bind_group_id = bind_group.get_hash().unwrap(); if let None = self.bind_groups.get(&bind_group_id) { @@ -331,7 +335,7 @@ impl WgpuRenderer { }, BindType::Sampler | BindType::SampledTexture { .. } => { // textures and samplers are handled per-entity - None + return; }, _ => panic!("unsupported bind type: {:?}", binding), } @@ -392,8 +396,6 @@ impl WgpuRenderer { }, ); } - - bind_group_id } pub fn create_shader_module( @@ -421,6 +423,139 @@ impl WgpuRenderer { let command_buffer = self.encoder.take().unwrap().finish(); self.queue.submit(&[command_buffer]); } + + pub fn get_entity_bind_group(&self, entity: Entity, bind_group_id: u64) -> Option<&BindGroupInfo> { + self.entity_bind_groups.get(&(entity, bind_group_id)) + } + + pub fn create_entity_bind_group(&mut self, bind_group: &BindGroup, entity: Entity) { + let bind_group_id = bind_group.get_hash().unwrap(); + let mut binding_resources = Vec::new(); + for binding in bind_group.bindings.iter() { + if let Some(resource) = self.get_entity_uniform_resource(entity, &binding.name) { + let resource_info = self.resource_info.get(&resource).unwrap(); + match binding.bind_type { + BindType::SampledTexture { .. } => { + }, + BindType::Sampler => { + + }, + _ => panic!("Attempted to create an unsupported BindType for BindGroup. Binding: {:?}, BindGroup: {:?}, Entity: {:?}", binding, bind_group, entity), + } + wgpu::Binding { + binding: binding.index, + resource: match &binding.bind_type { + BindType::SampledTexture { + } => { + if let ResourceInfo::Texture = resource_info { + let texture = self.textures.get(&resource).unwrap(); + wgpu::BindingResource::TextureView(texture) + } else { + panic!("expected a Texture resource"); + } + }, + _ => panic!("unsupported bind type"), + }, + } + } + else { + panic!("No resource assigned to uniform {} for entity {}", binding.name, entity), + } + } + } + fn setup_bind_group(&mut self, bind_group: &BindGroup) { + let bind_group_id = bind_group.get_hash().unwrap(); + + if let None = self.bind_groups.get(&bind_group_id) { + let mut unset_uniforms = Vec::new(); + + let mut binding_resources = Vec::new(); + // if a uniform resource buffer doesn't exist, create a new empty one + for binding in bind_group.bindings.iter() { + let resource = match self.render_resources.get_named_resource(&binding.name) { + resource @ Some(_) => resource, + None => { + println!( + "Warning: creating new empty buffer for binding {} {:?}", + binding.name, binding + ); + unset_uniforms.push(binding.name.to_string()); + match binding.bind_type { + BindType::Uniform { .. } => { + let size = binding.bind_type.get_uniform_size().unwrap(); + let resource = self.create_buffer( + size, + wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + ); + + self.render_resources + .set_named_resource(&binding.name, resource); + Some(resource) + }, + BindType::Sampler | BindType::SampledTexture { .. } => { + // textures and samplers are handled per-entity + return; + }, + _ => panic!("unsupported bind type: {:?}", binding), + } + } + }; + + if let Some(resource) = resource { + binding_resources.push(resource); + } + } + + // create wgpu Bindings + let bindings = bind_group + .bindings + .iter() + .zip(binding_resources) + .map(|(binding, resource)| { + let resource_info = self.resource_info.get(&resource).unwrap(); + wgpu::Binding { + binding: binding.index, + resource: match &binding.bind_type { + BindType::Uniform { + dynamic: _, + properties: _, + } => { + if let ResourceInfo::Buffer { + size, + buffer_usage: _, + } = resource_info + { + let buffer = self.buffers.get(&resource).unwrap(); + wgpu::BindingResource::Buffer { + buffer, + range: 0..*size, + } + } else { + panic!("expected a Buffer resource"); + } + } + _ => panic!("unsupported bind type"), + }, + } + }) + .collect::>(); + + let bind_group_layout = self.bind_group_layouts.get(&bind_group_id).unwrap(); + let bind_group_descriptor = wgpu::BindGroupDescriptor { + layout: bind_group_layout, + bindings: bindings.as_slice(), + }; + + let bind_group = self.device.create_bind_group(&bind_group_descriptor); + self.bind_groups.insert( + bind_group_id, + BindGroupInfo { + bind_group, + unset_uniforms, + }, + ); + } + } } impl Renderer for WgpuRenderer { @@ -762,14 +897,6 @@ impl Renderer for WgpuRenderer { resource } - fn get_named_resource(&self, name: &str) -> Option { - self.render_resources.get_named_resource(name) - } - - fn set_named_resource(&mut self, name: &str, resource: RenderResource) { - self.render_resources.set_named_resource(name, resource); - } - fn remove_texture(&mut self, resource: RenderResource) { self.textures.remove(&resource); self.resource_info.remove(&resource); @@ -780,12 +907,19 @@ impl Renderer for WgpuRenderer { self.resource_info.remove(&resource); } - fn get_texture_resource(&self, texture: Handle) -> Option { - self.render_resources.get_texture_resource(texture) + fn get_render_resources(&self) -> &RenderResources { + &self.render_resources } - fn set_texture_resource(&mut self, texture: Handle, resource: RenderResource) { - self.render_resources.set_texture_resource(texture, resource); + fn get_render_resources_mut(&mut self) -> &mut RenderResources { + &mut self.render_resources + } + + fn set_entity_uniform_resource(&mut self, entity: Entity, uniform_name: &str, resource: RenderResource) { + self.entity_uniform_resources.insert((Cow::Owned(entity), Cow::Owned(uniform_name.to_string())), resource); + } + fn get_entity_uniform_resource(&mut self, entity: Entity, uniform_name: &str) -> Option { + self.entity_uniform_resources.get(&(Cow::Owned(entity), Cow::Borrowed(uniform_name))).cloned() } } @@ -829,7 +963,20 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> { let pipeline_layout = self.pipeline_descriptor.get_layout().unwrap(); for bind_group in pipeline_layout.bind_groups.iter() { let bind_group_id = bind_group.get_hash().unwrap(); - let bind_group_info = self.renderer.bind_groups.get(&bind_group_id).unwrap(); + let bind_group_info = match self.renderer.bind_groups.get(&bind_group_id) { + Some(bind_group_info) => bind_group_info, + None => { + if let Some(entity) = entity { + if let None = self.renderer.get_entity_bind_group(*entity, bind_group_id) { + self.renderer.create_entity_bind_group(bind_group, *entity); + } + + self.renderer.get_entity_bind_group(*entity, bind_group_id).unwrap() + } else { + panic!("No bind group exists that matches: {:?}"); + } + } + }; let mut dynamic_uniform_indices = Vec::new(); for binding in bind_group.bindings.iter() { diff --git a/src/render/render_graph/resource_providers/camera2d_resource_provider.rs b/src/render/render_graph/resource_providers/camera2d_resource_provider.rs index 6578d57cd3..0001cbe4f0 100644 --- a/src/render/render_graph/resource_providers/camera2d_resource_provider.rs +++ b/src/render/render_graph/resource_providers/camera2d_resource_provider.rs @@ -18,7 +18,7 @@ impl ResourceProvider for Camera2dResourceProvider { wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, ); - renderer.set_named_resource(resource_name::uniform::CAMERA2D, buffer); + renderer.get_render_resources_mut().set_named_resource(resource_name::uniform::CAMERA2D, buffer); self.camera_buffer = Some(buffer); } diff --git a/src/render/render_graph/resource_providers/camera_resource_provider.rs b/src/render/render_graph/resource_providers/camera_resource_provider.rs index cea8cccf9d..67047bac01 100644 --- a/src/render/render_graph/resource_providers/camera_resource_provider.rs +++ b/src/render/render_graph/resource_providers/camera_resource_provider.rs @@ -19,7 +19,7 @@ impl ResourceProvider for CameraResourceProvider { wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, ); - renderer.set_named_resource(resource_name::uniform::CAMERA, buffer); + renderer.get_render_resources_mut().set_named_resource(resource_name::uniform::CAMERA, buffer); self.camera_buffer = Some(buffer); } diff --git a/src/render/render_graph/resource_providers/frame_texture_resource_provider.rs b/src/render/render_graph/resource_providers/frame_texture_resource_provider.rs index ada768e84c..1f0d5c4bf3 100644 --- a/src/render/render_graph/resource_providers/frame_texture_resource_provider.rs +++ b/src/render/render_graph/resource_providers/frame_texture_resource_provider.rs @@ -22,12 +22,12 @@ impl FrameTextureResourceProvider { self.descriptor.size.width = window_size.width; self.descriptor.size.height = window_size.height; - if let Some(old_resource) = renderer.get_named_resource(&self.name) { + if let Some(old_resource) = renderer.get_render_resources().get_named_resource(&self.name) { renderer.remove_texture(old_resource); } let texture_resource = renderer.create_texture(&self.descriptor, None); - renderer.set_named_resource(&self.name, texture_resource); + renderer.get_render_resources_mut().set_named_resource(&self.name, texture_resource); } } diff --git a/src/render/render_graph/resource_providers/light_resource_provider.rs b/src/render/render_graph/resource_providers/light_resource_provider.rs index 9d39c8c8a0..3e19feb37f 100644 --- a/src/render/render_graph/resource_providers/light_resource_provider.rs +++ b/src/render/render_graph/resource_providers/light_resource_provider.rs @@ -42,7 +42,7 @@ impl ResourceProvider for LightResourceProvider { light_uniform_size, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::COPY_DST, ); - renderer.set_named_resource(resource_name::uniform::LIGHTS, buffer); + renderer.get_render_resources_mut().set_named_resource(resource_name::uniform::LIGHTS, buffer); self.light_buffer = Some(buffer); } diff --git a/src/render/render_graph/resource_providers/ui_resource_provider.rs b/src/render/render_graph/resource_providers/ui_resource_provider.rs index 4569c8adc7..e81f7685ef 100644 --- a/src/render/render_graph/resource_providers/ui_resource_provider.rs +++ b/src/render/render_graph/resource_providers/ui_resource_provider.rs @@ -82,7 +82,7 @@ impl UiResourceProvider { wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::VERTEX, ); - renderer.set_named_resource(resource_name::buffer::UI_INSTANCES, buffer); + renderer.get_render_resources_mut().set_named_resource(resource_name::buffer::UI_INSTANCES, buffer); self.instance_buffer = Some(buffer); } } diff --git a/src/render/render_graph/resource_providers/uniform_resource_provider.rs b/src/render/render_graph/resource_providers/uniform_resource_provider.rs index 28887e4e4f..1b71c17f49 100644 --- a/src/render/render_graph/resource_providers/uniform_resource_provider.rs +++ b/src/render/render_graph/resource_providers/uniform_resource_provider.rs @@ -2,7 +2,7 @@ use crate::{ asset::{AssetStorage, Texture}, render::render_graph::{ render_resource::RenderResource, AsUniforms, BindType, DynamicUniformBufferInfo, - Renderable, Renderer, ResourceProvider, TextureDescriptor, UniformInfoIter, + Renderable, Renderer, ResourceProvider, TextureDescriptor, UniformInfoIter, SamplerDescriptor, }, }; use legion::prelude::*; @@ -51,7 +51,7 @@ where } let mut counts = Vec::new(); - for (uniforms, _renderable) in query.iter(world) { + for (entity, (uniforms, _renderable)) in query.iter_entities(world) { let mut uniform_index = 0; let field_uniform_names = uniforms.get_field_uniform_names(); for uniform_info in UniformInfoIter::new(field_uniform_names, uniforms.deref()) { @@ -75,14 +75,37 @@ where uniforms.get_uniform_texture(&uniform_info.name).unwrap(); let storage = world.resources.get::>().unwrap(); let texture = storage.get(&texture_handle).unwrap(); - if let None = renderer.get_texture_resource(texture_handle) { - let descriptor: TextureDescriptor = texture.into(); - let resource = - renderer.create_texture(&descriptor, Some(&texture.data)); - renderer.set_texture_resource(texture_handle, resource); - } + let resource = match renderer.get_render_resources().get_texture_resource(texture_handle) { + Some(resource) => resource, + None => { + let descriptor: TextureDescriptor = texture.into(); + let resource = + renderer.create_texture(&descriptor, Some(&texture.data)); + renderer.get_render_resources_mut().set_texture_resource(texture_handle, resource); + resource + } + }; + + renderer.assign_entity_uniform_resource(*entity, uniform_info.name, resource); + } + BindType::Sampler { .. } => { + let texture_handle = + uniforms.get_uniform_texture(&uniform_info.name).unwrap(); + let storage = world.resources.get::>().unwrap(); + let texture = storage.get(&texture_handle).unwrap(); + let resource = match renderer.get_render_resources().get_texture_sampler_resource(texture_handle) { + Some(resource) => resource, + None => { + let descriptor: SamplerDescriptor = texture.into(); + let resource = + renderer.create_sampler(&descriptor); + renderer.get_render_resources_mut().set_texture_sampler_resource(texture_handle, resource); + resource + } + }; + + renderer.assign_entity_uniform_resource(*entity, uniform_info.name, resource); } - BindType::Sampler { .. } => {} _ => panic!( "encountered unsupported bind_type {:?}", uniform_info.bind_type @@ -114,7 +137,7 @@ where info.capacity = capacity; renderer.add_dynamic_uniform_buffer_info(created_resource, info); *resource = Some(created_resource); - renderer.set_named_resource(name, created_resource); + renderer.get_render_resources_mut().set_named_resource(name, created_resource); } // copy entity uniform data to buffers