From 1a4bd984343917af32e793784088effa9ee8b217 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 23 Feb 2020 23:41:48 -0800 Subject: [PATCH] more texture work --- bevy_derive/src/lib.rs | 21 ++++++-- examples/texture.rs | 2 +- src/asset/texture.rs | 31 +++++++++-- src/render/render_graph/pipeline_layout.rs | 29 +++++++++++ src/render/render_graph/renderer.rs | 11 ++-- .../render_graph/renderers/wgpu_renderer.rs | 51 ++++++++++++++----- .../frame_texture_resource_provider.rs | 2 +- .../uniform_resource_provider.rs | 24 ++++++--- src/render/render_graph/uniform.rs | 37 +++++++++++--- .../render_graph/uniforms/local_to_world.rs | 5 +- 10 files changed, 171 insertions(+), 42 deletions(-) diff --git a/bevy_derive/src/lib.rs b/bevy_derive/src/lib.rs index a7c7354f58..879dcc90fc 100644 --- a/bevy_derive/src/lib.rs +++ b/bevy_derive/src/lib.rs @@ -119,13 +119,20 @@ pub fn derive_uniforms(input: TokenStream) -> TokenStream { }).collect::>(); let mut uniform_name_strings = Vec::new(); - let field_uniform_names = active_uniform_field_name_strings.iter().map(|f| { - let uniform = format!("{}_{}", struct_name, f); + let mut texture_and_sampler_name_strings = Vec::new(); + let mut texture_and_sampler_name_idents = Vec::new(); + let field_uniform_names = active_uniform_fields.iter().map(|f| { + let field_name = f.ident.as_ref().unwrap().to_string(); + let uniform = format!("{}_{}", struct_name, field_name); let texture = format!("{}_texture", uniform); let sampler = format!("{}_sampler", uniform); uniform_name_strings.push(uniform.clone()); + texture_and_sampler_name_strings.push(texture.clone()); + texture_and_sampler_name_strings.push(sampler.clone()); + texture_and_sampler_name_idents.push(f.ident.clone()); + texture_and_sampler_name_idents.push(f.ident.clone()); quote!(bevy::render::render_graph::FieldUniformName { - field: #f, + field: #field_name, uniform: #uniform, texture: #texture, sampler: #sampler, @@ -160,6 +167,14 @@ pub fn derive_uniforms(input: TokenStream) -> TokenStream { } } + fn get_uniform_texture(&self, name: &str) -> Option> { + use bevy::render::render_graph::GetTexture; + match name { + #(#texture_and_sampler_name_strings => self.#texture_and_sampler_name_idents.get_texture(),)* + _ => None, + } + } + // TODO: this will be very allocation heavy. find a way to either make this allocation free // or alternatively only run it when the shader_defs have changed fn get_shader_defs(&self) -> Option> { diff --git a/examples/texture.rs b/examples/texture.rs index 8adb5a7755..892f6e8654 100644 --- a/examples/texture.rs +++ b/examples/texture.rs @@ -12,7 +12,7 @@ fn setup(world: &mut World) { let texture_handle = { let mut texture_storage = world.resources.get_mut::>().unwrap(); - let texture = Texture::load(TextureType::Data(asset::create_texels(256))); + let texture = Texture::load(TextureType::Data(asset::create_texels(256), 256, 256)); texture_storage.add(texture) }; diff --git a/src/asset/texture.rs b/src/asset/texture.rs index 977fa9278f..d8c5866277 100644 --- a/src/asset/texture.rs +++ b/src/asset/texture.rs @@ -1,23 +1,44 @@ -use crate::asset::Asset; +use crate::{render::render_graph::{TextureDimension, TextureDescriptor}, asset::Asset}; pub enum TextureType { - Data(Vec), + Data(Vec, usize, usize), } pub struct Texture { pub data: Vec, + pub width: usize, + pub height: usize, } impl Asset for Texture { fn load(descriptor: TextureType) -> Self { - let data = match descriptor { - TextureType::Data(data) => data.clone(), + let (data, width, height) = match descriptor { + TextureType::Data(data, width, height) => (data.clone(), width, height), }; - Texture { data } + Texture { data, width, height } } } +impl From<&Texture> for TextureDescriptor { + fn from(texture: &Texture) -> Self { + TextureDescriptor { + size: wgpu::Extent3d { + height: texture.height as u32, + width: texture.width as u32, + depth: 1, + }, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, + } + } + +} + pub fn create_texels(size: usize) -> Vec { use std::iter; diff --git a/src/render/render_graph/pipeline_layout.rs b/src/render/render_graph/pipeline_layout.rs index ec54e73fb7..3804e6e1d2 100644 --- a/src/render/render_graph/pipeline_layout.rs +++ b/src/render/render_graph/pipeline_layout.rs @@ -241,3 +241,32 @@ impl From for wgpu::TextureDescriptor { } } } + +#[derive(Copy, Clone)] +pub struct SamplerDescriptor { + pub address_mode_u: wgpu::AddressMode, + pub address_mode_v: wgpu::AddressMode, + pub address_mode_w: wgpu::AddressMode, + pub mag_filter: wgpu::FilterMode, + pub min_filter: wgpu::FilterMode, + pub mipmap_filter: wgpu::FilterMode, + pub lod_min_clamp: f32, + pub lod_max_clamp: f32, + pub compare_function: wgpu::CompareFunction, +} + +impl From for wgpu::SamplerDescriptor { + fn from(sampler_descriptor: SamplerDescriptor) -> Self { + wgpu::SamplerDescriptor { + address_mode_u: sampler_descriptor.address_mode_u, + address_mode_v: sampler_descriptor.address_mode_v, + address_mode_w: sampler_descriptor.address_mode_w, + mag_filter: sampler_descriptor.mag_filter, + min_filter: sampler_descriptor.min_filter, + mipmap_filter: sampler_descriptor.mipmap_filter, + lod_min_clamp: sampler_descriptor.lod_min_clamp, + lod_max_clamp: sampler_descriptor.lod_max_clamp, + compare_function: sampler_descriptor.compare_function, + } + } +} diff --git a/src/render/render_graph/renderer.rs b/src/render/render_graph/renderer.rs index a50f2c604d..2898d2b641 100644 --- a/src/render/render_graph/renderer.rs +++ b/src/render/render_graph/renderer.rs @@ -2,8 +2,8 @@ use crate::{ legion::prelude::*, render::render_graph::{ render_resource::RenderResource, DynamicUniformBufferInfo, PipelineDescriptor, RenderGraph, - ResourceInfo, TextureDescriptor, - }, + ResourceInfo, TextureDescriptor, SamplerDescriptor + }, asset::{Handle, Texture}, }; use std::ops::Range; @@ -23,8 +23,8 @@ pub trait Renderer { data: &[u8], buffer_usage: wgpu::BufferUsage, ) -> RenderResource; - fn create_texture(&mut self, texture_descriptor: &TextureDescriptor) -> RenderResource; - fn create_texture_with_data( + fn create_sampler(&mut self, sampler_descriptor: &SamplerDescriptor) -> RenderResource; + fn create_texture( &mut self, texture_descriptor: &TextureDescriptor, bytes: Option<&[u8]>, @@ -67,6 +67,7 @@ pub trait Renderer { ) -> 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 copy_buffer_to_buffer( &mut self, @@ -78,6 +79,8 @@ pub trait Renderer { ); 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); } pub trait RenderPass { diff --git a/src/render/render_graph/renderers/wgpu_renderer.rs b/src/render/render_graph/renderers/wgpu_renderer.rs index 23f2e9b0b3..cb1e9fed5f 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}, + asset::{AssetStorage, Handle, Texture}, legion::prelude::*, render::{ render_graph::{ @@ -7,7 +7,7 @@ use crate::{ DynamicUniformBufferInfo, PassDescriptor, PipelineDescriptor, PipelineLayout, PipelineLayoutType, RenderGraph, RenderPass, RenderPassColorAttachmentDescriptor, RenderPassDepthStencilAttachmentDescriptor, RenderResource, RenderResources, Renderer, - ResourceInfo, TextureDescriptor, + ResourceInfo, SamplerDescriptor, TextureDescriptor, }, Shader, }, @@ -23,6 +23,7 @@ pub struct WgpuRenderer { pub render_pipelines: HashMap, wgpu::RenderPipeline>, pub buffers: HashMap, pub textures: HashMap, + pub samplers: HashMap, pub resource_info: HashMap, pub bind_groups: HashMap, pub bind_group_layouts: HashMap, @@ -64,6 +65,7 @@ impl WgpuRenderer { render_pipelines: HashMap::new(), buffers: HashMap::new(), textures: HashMap::new(), + samplers: HashMap::new(), resource_info: HashMap::new(), bind_groups: HashMap::new(), bind_group_layouts: HashMap::new(), @@ -304,11 +306,11 @@ impl WgpuRenderer { if let None = self.bind_groups.get(&bind_group_id) { let mut unset_uniforms = Vec::new(); - let mut binding_resources = Vec::with_capacity(bind_group.bindings.len()); + 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) { - Some(resource) => resource, + resource @ Some(_) => resource, None => { println!( "Warning: creating new empty buffer for binding {} {:?}", @@ -325,14 +327,20 @@ impl WgpuRenderer { self.render_resources .set_named_resource(&binding.name, resource); - resource - } + Some(resource) + }, + BindType::Sampler | BindType::SampledTexture { .. } => { + // textures and samplers are handled per-entity + None + }, _ => panic!("unsupported bind type: {:?}", binding), } } }; - binding_resources.push(resource); + if let Some(resource) = resource { + binding_resources.push(resource); + } } // create wgpu Bindings @@ -474,7 +482,7 @@ impl Renderer for WgpuRenderer { update_shader_assignments(world, render_graph); for (name, texture_descriptor) in render_graph.queued_textures.drain(..) { - let resource = self.create_texture(&texture_descriptor); + let resource = self.create_texture(&texture_descriptor, None); self.render_resources.set_named_resource(&name, resource); } @@ -709,16 +717,17 @@ impl Renderer for WgpuRenderer { self.dynamic_uniform_buffer_info.insert(resource, info); } - fn create_texture(&mut self, texture_descriptor: &TextureDescriptor) -> RenderResource { - let descriptor: wgpu::TextureDescriptor = (*texture_descriptor).into(); - let texture = self.device.create_texture(&descriptor); + fn create_sampler(&mut self, sampler_descriptor: &SamplerDescriptor) -> RenderResource { + let descriptor: wgpu::SamplerDescriptor = (*sampler_descriptor).into(); + let sampler = self.device.create_sampler(&descriptor); let resource = self.render_resources.get_next_resource(); - self.textures - .insert(resource, texture.create_default_view()); + self.samplers.insert(resource, sampler); + self.add_resource_info(resource, ResourceInfo::Texture); resource } - fn create_texture_with_data( + + fn create_texture( &mut self, texture_descriptor: &TextureDescriptor, bytes: Option<&[u8]>, @@ -749,6 +758,7 @@ impl Renderer for WgpuRenderer { let resource = self.render_resources.get_next_resource(); self.add_resource_info(resource, ResourceInfo::Texture); + self.textures.insert(resource, texture_view); resource } @@ -764,6 +774,19 @@ impl Renderer for WgpuRenderer { self.textures.remove(&resource); self.resource_info.remove(&resource); } + + fn remove_sampler(&mut self, resource: RenderResource) { + self.samplers.remove(&resource); + self.resource_info.remove(&resource); + } + + fn get_texture_resource(&self, texture: Handle) -> Option { + self.render_resources.get_texture_resource(texture) + } + + fn set_texture_resource(&mut self, texture: Handle, resource: RenderResource) { + self.render_resources.set_texture_resource(texture, resource); + } } pub struct WgpuRenderPass<'a, 'b, 'c, 'd> { 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 bc377c3814..ada768e84c 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 @@ -26,7 +26,7 @@ impl FrameTextureResourceProvider { renderer.remove_texture(old_resource); } - let texture_resource = renderer.create_texture(&self.descriptor); + let texture_resource = renderer.create_texture(&self.descriptor, None); renderer.set_named_resource(&self.name, texture_resource); } } 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 fd42710877..1944e0c512 100644 --- a/src/render/render_graph/resource_providers/uniform_resource_provider.rs +++ b/src/render/render_graph/resource_providers/uniform_resource_provider.rs @@ -1,6 +1,9 @@ -use crate::render::render_graph::{ - render_resource::RenderResource, AsUniforms, BindType, DynamicUniformBufferInfo, Renderable, - Renderer, ResourceProvider, UniformInfoIter, +use crate::{ + asset::{AssetStorage, Texture}, + render::render_graph::{ + render_resource::RenderResource, AsUniforms, BindType, DynamicUniformBufferInfo, + Renderable, Renderer, ResourceProvider, TextureDescriptor, UniformInfoIter, + }, }; use legion::prelude::*; use std::{marker::PhantomData, ops::Deref}; @@ -68,11 +71,18 @@ where uniform_index += 1; } BindType::SampledTexture { .. } => { - // TODO: look up Handle and load - } - BindType::Sampler { .. } => { - // TODO: look up Handle and load + let texture_handle = + 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.clone()) { + let descriptor: TextureDescriptor = texture.into(); + let resource = + renderer.create_texture(&descriptor, Some(&texture.data)); + renderer.set_texture_resource(texture_handle, resource); + } } + BindType::Sampler { .. } => {} _ => panic!( "encountered unsupported bind_type {:?}", uniform_info.bind_type diff --git a/src/render/render_graph/uniform.rs b/src/render/render_graph/uniform.rs index 39e3565043..30234122c2 100644 --- a/src/render/render_graph/uniform.rs +++ b/src/render/render_graph/uniform.rs @@ -1,6 +1,6 @@ use crate::{ + asset::{Handle, Texture}, core::GetBytes, - math::Vec4, render::{ color::ColorSource, render_graph::{BindType, TextureViewDimension}, @@ -13,6 +13,7 @@ use std::collections::HashMap; pub trait AsUniforms { fn get_field_uniform_names(&self) -> &[FieldUniformName]; fn get_uniform_bytes(&self, name: &str) -> Option>; + fn get_uniform_texture(&self, name: &str) -> Option>; fn get_shader_defs(&self) -> Option>; fn get_field_bind_type(&self, name: &str) -> Option; // TODO: support zero-copy uniforms @@ -125,18 +126,42 @@ impl AsFieldBindType for ColorSource { } } -default impl AsFieldBindType for T +impl AsFieldBindType for T where T: GetBytes, { - fn get_field_bind_type(&self) -> FieldBindType { + default fn get_field_bind_type(&self) -> FieldBindType { FieldBindType::Uniform } } -impl AsFieldBindType for Vec4 { - fn get_field_bind_type(&self) -> FieldBindType { - FieldBindType::Uniform +pub trait GetTexture { + fn get_texture(&self) -> Option> { + None + } +} + +impl GetTexture for T +where + T: GetBytes, +{ + default fn get_texture(&self) -> Option> { + None + } +} + +impl GetTexture for Handle { + fn get_texture(&self) -> Option> { + Some(self.clone()) + } +} + +impl GetTexture for ColorSource { + fn get_texture(&self) -> Option> { + match self { + ColorSource::Color(_) => None, + ColorSource::Texture(texture) => Some(texture.clone()), + } } } diff --git a/src/render/render_graph/uniforms/local_to_world.rs b/src/render/render_graph/uniforms/local_to_world.rs index a27462c569..c062454bc4 100644 --- a/src/render/render_graph/uniforms/local_to_world.rs +++ b/src/render/render_graph/uniforms/local_to_world.rs @@ -1,4 +1,4 @@ -use crate::render::render_graph::{uniform::AsUniforms, FieldBindType, FieldUniformName}; +use crate::{asset::{Handle, Texture}, render::render_graph::{uniform::AsUniforms, FieldBindType, FieldUniformName}}; use zerocopy::AsBytes; @@ -30,4 +30,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld { _ => None, } } + fn get_uniform_texture(&self, _name: &str) -> Option> { + None + } }