diff --git a/bevy_derive/src/lib.rs b/bevy_derive/src/lib.rs index 1847e73aed..25687d8c3d 100644 --- a/bevy_derive/src/lib.rs +++ b/bevy_derive/src/lib.rs @@ -20,7 +20,7 @@ pub fn derive_entity_archetype(input: TokenStream) -> TokenStream { fn insert(self, world: &mut World) -> Entity { *world.insert((), vec![( #(self.#field_name),* - )]).first().unwrap() + ,)]).first().unwrap() } } }) diff --git a/examples/ui_new.rs b/examples/ui_new.rs new file mode 100644 index 0000000000..a69216a85f --- /dev/null +++ b/examples/ui_new.rs @@ -0,0 +1,173 @@ +use bevy::prelude::*; +use bevy::render::render_graph_2::{StandardMaterial, ShaderUniforms}; + +fn main() { + AppBuilder::new().add_defaults().setup_world(setup).run(); +} + +fn setup(world: &mut World) { + let cube = Mesh::load(MeshType::Cube); + let cube_handle = { + let mut mesh_storage = world.resources.get_mut::>().unwrap(); + mesh_storage.add(cube) + }; + + world.build() + // cube + .add_archetype(NewMeshEntity { + mesh: cube_handle.clone(), + material: StandardMaterial { + albedo: math::vec4(0.5, 0.3, 0.3, 1.0), + }, + shader_uniforms: ShaderUniforms::new(), + local_to_world: LocalToWorld::identity(), + translation: Translation::new(0.0, 0.0, 1.0), + }) + // light + .add_archetype(LightEntity { + light: Light { + color: wgpu::Color { + r: 0.8, + g: 0.8, + b: 0.5, + a: 1.0, + }, + fov: f32::to_radians(60.0), + depth: 0.1..50.0, + target_view: None, + }, + local_to_world: LocalToWorld::identity(), + translation: Translation::new(4.0, -4.0, 5.0), + rotation: Rotation::from_euler_angles(0.0, 0.0, 0.0), + }) + // 3d camera + .add_archetype(CameraEntity { + camera: Camera::new(CameraType::Projection { + fov: std::f32::consts::PI / 4.0, + near: 1.0, + far: 1000.0, + aspect_ratio: 1.0, + }), + active_camera: ActiveCamera, + local_to_world: LocalToWorld(Mat4::look_at_rh( + Vec3::new(3.0, 8.0, 5.0), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, 0.0, 1.0), + )), + }) + // 2d camera + .add_archetype(Camera2dEntity { + camera: Camera::new(CameraType::Orthographic { + left: 0.0, + right: 0.0, + bottom: 0.0, + top: 0.0, + near: 0.0, + far: 1.0, + }), + active_camera_2d: ActiveCamera2d, + }) + .build(); + + + // bottom left anchor with vertical fill + world.insert( + (), + vec![(Node::new( + math::vec2(0.0, 0.0), + Anchors::new(0.0, 0.0, 0.0, 1.0), + Margins::new(10.0, 200.0, 10.0, 10.0), + math::vec4(0.1, 0.1, 0.1, 1.0), + ),)], + ); + + // top right anchor with vertical fill + world.insert( + (), + vec![(Node::new( + math::vec2(0.0, 0.0), + Anchors::new(1.0, 1.0, 0.0, 1.0), + Margins::new(10.0, 100.0, 50.0, 100.0), + math::vec4(0.1, 0.1, 0.1, 1.0), + ),)], + ); + + // render order test: reddest in the back, whitest in the front + world.insert( + (), + vec![(Node::new( + math::vec2(75.0, 75.0), + Anchors::new(0.5, 0.5, 0.5, 0.5), + Margins::new(0.0, 100.0, 0.0, 100.0), + math::vec4(1.0, 0.1, 0.1, 1.0), + ),)], + ); + + world.insert( + (), + vec![(Node::new( + math::vec2(50.0, 50.0), + Anchors::new(0.5, 0.5, 0.5, 0.5), + Margins::new(0.0, 100.0, 0.0, 100.0), + math::vec4(1.0, 0.3, 0.3, 1.0), + ),)], + ); + + world.insert( + (), + vec![(Node::new( + math::vec2(100.0, 100.0), + Anchors::new(0.5, 0.5, 0.5, 0.5), + Margins::new(0.0, 100.0, 0.0, 100.0), + math::vec4(1.0, 0.5, 0.5, 1.0), + ),)], + ); + + world.insert( + (), + vec![(Node::new( + math::vec2(150.0, 150.0), + Anchors::new(0.5, 0.5, 0.5, 0.5), + Margins::new(0.0, 100.0, 0.0, 100.0), + math::vec4(1.0, 0.7, 0.7, 1.0), + ),)], + ); + + // parenting + let parent = *world + .insert( + (), + vec![(Node::new( + math::vec2(300.0, 300.0), + Anchors::new(0.0, 0.0, 0.0, 0.0), + Margins::new(0.0, 200.0, 0.0, 200.0), + math::vec4(0.1, 0.1, 1.0, 1.0), + ),)], + ) + .first() + .unwrap(); + + world.insert( + (), + vec![( + Node::new( + math::vec2(0.0, 0.0), + Anchors::new(0.0, 1.0, 0.0, 1.0), + Margins::new(20.0, 20.0, 20.0, 20.0), + math::vec4(0.6, 0.6, 1.0, 1.0), + ), + Parent(parent), + )], + ); + + // alpha test + world.insert( + (), + vec![(Node::new( + math::vec2(200.0, 200.0), + Anchors::new(0.5, 0.5, 0.5, 0.5), + Margins::new(0.0, 100.0, 0.0, 100.0), + math::vec4(1.0, 0.9, 0.9, 0.4), + ),)], + ); +} diff --git a/src/app/app_builder.rs b/src/app/app_builder.rs index 74e202a479..54894b54d4 100644 --- a/src/app/app_builder.rs +++ b/src/app/app_builder.rs @@ -4,7 +4,7 @@ use crate::{ core::Time, legion::prelude::{Runnable, Schedulable, Schedule, Universe, World}, render::render_graph_2, - render::render_graph_2::{pipelines::*, passes::*, wgpu_renderer::WgpuRenderer, resource_provider::{CameraResourceProvider, LightResourceProvider}, UniformResourceProvider, StandardMaterial}, + render::render_graph_2::{pipelines::*, passes::*, wgpu_renderer::WgpuRenderer, resource_providers::*, UniformResourceProvider, StandardMaterial}, render::{passes::*, *}, plugin::load_plugin, ui, @@ -175,11 +175,14 @@ impl AppBuilder { self.render_graph_builder = self .render_graph_builder .add_resource_provider(Box::new(CameraResourceProvider)) + .add_resource_provider(Box::new(Camera2dResourceProvider)) .add_resource_provider(Box::new(LightResourceProvider::new(10))) + .add_resource_provider(Box::new(UiResourceProvider::new())) .add_resource_provider(Box::new(UniformResourceProvider::::new())) .add_resource_provider(Box::new(UniformResourceProvider::::new())) .add_forward_pass() - .add_forward_pipeline(); + .add_forward_pipeline() + .add_ui_pipeline(); self } diff --git a/src/ecs/default_archetypes.rs b/src/ecs/default_archetypes.rs index d68535a9f7..7d8999ff07 100644 --- a/src/ecs/default_archetypes.rs +++ b/src/ecs/default_archetypes.rs @@ -32,4 +32,15 @@ pub struct CameraEntity { pub camera: Camera, pub active_camera: ActiveCamera, pub local_to_world: LocalToWorld, +} + +#[derive(EntityArchetype)] +pub struct Camera2dEntity { + pub camera: Camera, + pub active_camera_2d: ActiveCamera2d, +} + +#[derive(EntityArchetype)] +pub struct UiEntity { + pub node: Node, } \ No newline at end of file diff --git a/src/render/passes/ui/mod.rs b/src/render/passes/ui/mod.rs index f619ecacdc..31cf00aac0 100644 --- a/src/render/passes/ui/mod.rs +++ b/src/render/passes/ui/mod.rs @@ -177,54 +177,55 @@ impl Pipeline for UiPipeline { let vs_module = render_graph.device.create_shader_module(&vs_bytes); let fs_module = render_graph.device.create_shader_module(&fs_bytes); - self.pipeline = Some(render_graph.device.create_render_pipeline( - &wgpu::RenderPipelineDescriptor { - layout: &pipeline_layout, - vertex_stage: wgpu::ProgrammableStageDescriptor { - module: &vs_module, - entry_point: "main", - }, - fragment_stage: Some(wgpu::ProgrammableStageDescriptor { - module: &fs_module, - entry_point: "main", - }), - rasterization_state: Some(wgpu::RasterizationStateDescriptor { - front_face: wgpu::FrontFace::Ccw, - cull_mode: wgpu::CullMode::None, - depth_bias: 0, - depth_bias_slope_scale: 0.0, - depth_bias_clamp: 0.0, - }), - primitive_topology: wgpu::PrimitiveTopology::TriangleList, - color_states: &[wgpu::ColorStateDescriptor { - format: wgpu::TextureFormat::Bgra8UnormSrgb, - color_blend: wgpu::BlendDescriptor { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha_blend: wgpu::BlendDescriptor { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - write_mask: wgpu::ColorWrite::ALL, - }], - depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { - format: self.depth_format, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Always, - stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, - stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, - stencil_read_mask: 0, - stencil_write_mask: 0, - }), - index_format: wgpu::IndexFormat::Uint16, - vertex_buffers: &[vertex_buffer_descriptor, instance_buffer_descriptor], - sample_count: self.msaa_samples as u32, - sample_mask: !0, - alpha_to_coverage_enabled: false, + let pipedesc = wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::None, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[wgpu::ColorStateDescriptor { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + color_blend: wgpu::BlendDescriptor { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha_blend: wgpu::BlendDescriptor { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Add, + }, + write_mask: wgpu::ColorWrite::ALL, + }], + depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { + format: self.depth_format, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Always, + stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_read_mask: 0, + stencil_write_mask: 0, + }), + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[vertex_buffer_descriptor, instance_buffer_descriptor], + sample_count: self.msaa_samples as u32, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }; + + self.pipeline = Some(render_graph.device.create_render_pipeline(&pipedesc )); } diff --git a/src/render/render_graph_2/draw_target.rs b/src/render/render_graph_2/draw_target.rs index 65cf1abade..7a1a03b679 100644 --- a/src/render/render_graph_2/draw_target.rs +++ b/src/render/render_graph_2/draw_target.rs @@ -7,6 +7,8 @@ use crate::{ }, }; +use super::resource_name; +use super::ResourceInfo; use zerocopy::AsBytes; // A set of draw calls. ex: get + draw meshes, get + draw instanced meshes, draw ui meshes, etc @@ -60,3 +62,46 @@ pub fn mesh_draw_target(world: &World, render_pass: &mut dyn RenderPass) { renderer.remove_buffer(MESH_VERTEX_BUFFER_NAME); renderer.remove_buffer(MESH_INDEX_BUFFER_NAME); } + +pub fn ui_draw_target(world: &World, render_pass: &mut dyn RenderPass) { + let mut mesh_storage = world.resources.get_mut::>().unwrap(); + // NOTE: this is ugly and borrowing is stupid + let result = { + let renderer = render_pass.get_renderer(); + let result = if let Some(ResourceInfo::InstanceBuffer { count, mesh_id, .. }) = + renderer.get_resource_info(resource_name::buffer::UI_INSTANCES) + { + Some((*count, *mesh_id)) + } else { + None + }; + + if let Some((instance_count, mesh_id)) = result { + if let Some(mesh_asset) = mesh_storage.get(mesh_id) { + renderer.create_buffer_with_data( + MESH_VERTEX_BUFFER_NAME, + mesh_asset.vertices.as_bytes(), + wgpu::BufferUsage::VERTEX, + ); + renderer.create_buffer_with_data( + MESH_INDEX_BUFFER_NAME, + mesh_asset.indices.as_bytes(), + wgpu::BufferUsage::INDEX, + ); + Some((instance_count, mesh_asset.indices.len())) + } else { + None + } + } else { + None + } + + }; + if let Some((instance_count, indices_length)) = result { + render_pass.setup_bind_groups(None); + render_pass.set_index_buffer(MESH_INDEX_BUFFER_NAME, 0); + render_pass.set_vertex_buffer(0, MESH_VERTEX_BUFFER_NAME, 0); + render_pass.set_vertex_buffer(1, resource_name::buffer::UI_INSTANCES, 0); + render_pass.draw_indexed(0..indices_length as u32, 0, 0..(instance_count as u32)); + } +} diff --git a/src/render/render_graph_2/mod.rs b/src/render/render_graph_2/mod.rs index d7c689bc87..6d034a2c97 100644 --- a/src/render/render_graph_2/mod.rs +++ b/src/render/render_graph_2/mod.rs @@ -2,6 +2,7 @@ pub mod pipelines; pub mod resource_name; pub mod wgpu_renderer; pub mod resource_provider; +pub mod resource_providers; pub mod passes; mod resource; mod pipeline; diff --git a/src/render/render_graph_2/passes/forward.rs b/src/render/render_graph_2/passes/forward.rs index b57991ba95..b661551843 100644 --- a/src/render/render_graph_2/passes/forward.rs +++ b/src/render/render_graph_2/passes/forward.rs @@ -1,7 +1,7 @@ use crate::render::render_graph_2::{ resource_name, PassDescriptor, RenderGraphBuilder, RenderPassColorAttachmentDescriptor, RenderPassDepthStencilAttachmentDescriptor, TextureDescriptor, TextureDimension, - FrameTexture, + resource_providers::FrameTextureResourceProvider, }; pub trait ForwardPassBuilder { @@ -10,7 +10,7 @@ pub trait ForwardPassBuilder { impl ForwardPassBuilder for RenderGraphBuilder { fn add_forward_pass(self) -> Self { - self.add_resource_provider(Box::new(FrameTexture::new(resource_name::texture::DEPTH, TextureDescriptor { + self.add_resource_provider(Box::new(FrameTextureResourceProvider::new(resource_name::texture::DEPTH, TextureDescriptor { size: wgpu::Extent3d { depth: 1, width: 1, diff --git a/src/render/render_graph_2/pipeline.rs b/src/render/render_graph_2/pipeline.rs index 085328db36..bc1f040ffd 100644 --- a/src/render/render_graph_2/pipeline.rs +++ b/src/render/render_graph_2/pipeline.rs @@ -87,14 +87,12 @@ impl PipelineDescriptor { pub struct PipelineBuilder { pipeline: PipelineDescriptor, - vertex_buffer_descriptor_offset: u64, } impl PipelineBuilder { pub fn new(vertex_shader: Shader) -> Self { PipelineBuilder { pipeline: PipelineDescriptor::new(vertex_shader), - vertex_buffer_descriptor_offset: 0, } } @@ -130,16 +128,8 @@ impl PipelineBuilder { pub fn add_vertex_buffer_descriptor( mut self, - mut vertex_buffer_descriptor: VertexBufferDescriptor, + vertex_buffer_descriptor: VertexBufferDescriptor, ) -> Self { - let mut offset = 0; - for attribute in vertex_buffer_descriptor.attributes.iter_mut() { - offset += attribute.offset; - attribute.offset += self.vertex_buffer_descriptor_offset; - } - - self.vertex_buffer_descriptor_offset += offset; - self.pipeline .vertex_buffer_descriptors .push(vertex_buffer_descriptor); diff --git a/src/render/render_graph_2/pipelines/mod.rs b/src/render/render_graph_2/pipelines/mod.rs index 906ec8e291..42a234af01 100644 --- a/src/render/render_graph_2/pipelines/mod.rs +++ b/src/render/render_graph_2/pipelines/mod.rs @@ -1,5 +1,7 @@ mod forward; mod forward_flat; +mod ui; pub use forward::*; -pub use forward_flat::*; \ No newline at end of file +pub use forward_flat::*; +pub use ui::*; \ No newline at end of file diff --git a/src/render/render_graph_2/pipelines/ui/mod.rs b/src/render/render_graph_2/pipelines/ui/mod.rs new file mode 100644 index 0000000000..d1688424bc --- /dev/null +++ b/src/render/render_graph_2/pipelines/ui/mod.rs @@ -0,0 +1,108 @@ +use crate::render::{ + Vertex, + { + render_graph_2::{ + ui_draw_target, pipeline_layout::*, PipelineDescriptor, + RenderGraphBuilder, + }, + shader::{Shader, ShaderStage}, + }, +}; +use crate::render::render_graph_2::VertexBufferDescriptor; +use crate::render::render_graph_2::resource_providers::RectData; +pub trait UiPipelineBuilder { + fn add_ui_pipeline(self) -> Self; +} + +impl UiPipelineBuilder for RenderGraphBuilder { + fn add_ui_pipeline(self) -> Self { + self.add_pipeline( + "ui", + PipelineDescriptor::build(Shader::from_glsl( + include_str!("ui.vert"), + ShaderStage::Vertex, + )) + .with_fragment_shader(Shader::from_glsl( + include_str!("ui.frag"), + ShaderStage::Fragment, + )) + .add_bind_group(BindGroup::new( + vec![ + Binding { + name: "Camera2d".to_string(), + bind_type: BindType::Uniform { + dynamic: false, + properties: vec![ + UniformProperty { + name: "ViewProj".to_string(), + property_type: UniformPropertyType::Mat4, + }, + ] + } + }, + ] + )) + .with_rasterization_state(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::None, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }) + .with_depth_stencil_state(wgpu::DepthStencilStateDescriptor { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Always, + stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_read_mask: 0, + stencil_write_mask: 0, + }) + .add_color_state(wgpu::ColorStateDescriptor { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + color_blend: wgpu::BlendDescriptor { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha_blend: wgpu::BlendDescriptor { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Add, + }, + write_mask: wgpu::ColorWrite::ALL, + }) + .add_vertex_buffer_descriptor(Vertex::get_vertex_buffer_descriptor()) + .add_vertex_buffer_descriptor( + VertexBufferDescriptor { + stride: std::mem::size_of::() as u64, + step_mode: wgpu::InputStepMode::Instance, + attributes: vec![ + wgpu::VertexAttributeDescriptor { + format: wgpu::VertexFormat::Float2, + offset: 0, + shader_location: 3, + }, + wgpu::VertexAttributeDescriptor { + format: wgpu::VertexFormat::Float2, + offset: 2 * 4, + shader_location: 4, + }, + wgpu::VertexAttributeDescriptor { + format: wgpu::VertexFormat::Float4, + offset: 4 * 4, + shader_location: 5, + }, + wgpu::VertexAttributeDescriptor { + format: wgpu::VertexFormat::Float, + offset: 8 * 4, + shader_location: 6, + }, + ], + } + ) + .add_draw_target(ui_draw_target) + .build(), + ) + } +} \ No newline at end of file diff --git a/src/render/render_graph_2/pipelines/ui/ui.frag b/src/render/render_graph_2/pipelines/ui/ui.frag new file mode 100644 index 0000000000..50ec9a991b --- /dev/null +++ b/src/render/render_graph_2/pipelines/ui/ui.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec4 v_Color; + +layout(location = 0) out vec4 o_Target; + +void main() { + o_Target = v_Color; +} diff --git a/src/render/render_graph_2/pipelines/ui/ui.vert b/src/render/render_graph_2/pipelines/ui/ui.vert new file mode 100644 index 0000000000..9932a68378 --- /dev/null +++ b/src/render/render_graph_2/pipelines/ui/ui.vert @@ -0,0 +1,25 @@ +#version 450 + +// vertex attributes +layout(location = 0) in vec4 a_Pos; +layout(location = 1) in vec4 a_Normal; +layout(location = 2) in vec2 a_Uv; + +// instanced attributes (RectData) +layout (location = 3) in vec2 a_RectPosition; +layout (location = 4) in vec2 a_RectSize; +layout (location = 5) in vec4 a_RectColor; +layout (location = 6) in float a_RectZIndex; + +layout(location = 0) out vec4 v_Color; + +layout(set = 0, binding = 0) uniform Camera2d { + mat4 ViewProj; +}; + +void main() { + v_Color = a_RectColor; + vec4 position = a_Pos * vec4(a_RectSize, 0.0, 1.0); + position = position + vec4(a_RectPosition + a_RectSize / 2.0, -a_RectZIndex, 0.0); + gl_Position = ViewProj * position; +} diff --git a/src/render/render_graph_2/renderer.rs b/src/render/render_graph_2/renderer.rs index 9825681b3d..28f7b480f0 100644 --- a/src/render/render_graph_2/renderer.rs +++ b/src/render/render_graph_2/renderer.rs @@ -27,6 +27,8 @@ pub trait Renderer { ) -> Option<&mut DynamicUniformBufferInfo>; fn add_dynamic_uniform_buffer_info(&mut self, name: &str, info: DynamicUniformBufferInfo); fn create_buffer(&mut self, name: &str, size: u64, buffer_usage: wgpu::BufferUsage); + fn create_instance_buffer(&mut self, name: &str, mesh_id: usize, size: usize, count: usize, buffer_usage: wgpu::BufferUsage); + fn create_instance_buffer_with_data(&mut self, name: &str, mesh_id: usize, data: &[u8], size: usize, count: usize, buffer_usage: wgpu::BufferUsage); fn create_buffer_mapped( &mut self, name: &str, diff --git a/src/render/render_graph_2/resource.rs b/src/render/render_graph_2/resource.rs index cae4574e23..2bf29d673e 100644 --- a/src/render/render_graph_2/resource.rs +++ b/src/render/render_graph_2/resource.rs @@ -8,4 +8,11 @@ pub enum ResourceInfo { buffer_usage: wgpu::BufferUsage, // pub layout: Option< }, + InstanceBuffer { + size: usize, + count: usize, + buffer_usage: wgpu::BufferUsage, + mesh_id: usize, + // pub layout: Option< + }, } \ No newline at end of file diff --git a/src/render/render_graph_2/resource_name.rs b/src/render/render_graph_2/resource_name.rs index b8abfc042d..1750a99f2e 100644 --- a/src/render/render_graph_2/resource_name.rs +++ b/src/render/render_graph_2/resource_name.rs @@ -5,5 +5,10 @@ pub mod texture { pub mod uniform { pub const CAMERA: &str = "Camera"; + pub const CAMERA2D: &str = "Camera2d"; pub const LIGHTS: &str = "Lights"; +} + +pub mod buffer { + pub const UI_INSTANCES: &str = "UiInstances"; } \ No newline at end of file diff --git a/src/render/render_graph_2/resource_provider.rs b/src/render/render_graph_2/resource_provider.rs index 0b5d3e4f8b..428d36c791 100644 --- a/src/render/render_graph_2/resource_provider.rs +++ b/src/render/render_graph_2/resource_provider.rs @@ -1,171 +1,8 @@ -use crate::{render::{ - render_graph_2::{resource_name, Renderer, TextureDescriptor}, - ActiveCamera, Camera, Light, LightRaw, -}, transform::prelude::Translation}; -use bevy_transform::prelude::LocalToWorld; +use crate::render::render_graph_2::Renderer; use legion::prelude::*; -use zerocopy::AsBytes; pub trait ResourceProvider { fn initialize(&mut self, renderer: &mut dyn Renderer, world: &mut World); fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World); fn resize(&mut self, renderer: &mut dyn Renderer, world: &mut World, width: u32, height: u32); -} - -pub struct CameraResourceProvider; - -impl ResourceProvider for CameraResourceProvider { - fn initialize(&mut self, renderer: &mut dyn Renderer, _world: &mut World) { - renderer.create_buffer( - resource_name::uniform::CAMERA, - std::mem::size_of::<[[f32; 4]; 4]>() as u64, - wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, - ); - } - - fn update(&mut self, _renderer: &mut dyn Renderer, _world: &mut World) {} - fn resize(&mut self, renderer: &mut dyn Renderer, world: &mut World, width: u32, height: u32) { - let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>(); - for (mut camera, local_to_world, _) in - <(Write, Read, Read)>::query().iter_mut(world) - { - camera.update(width, height); - let camera_matrix: [[f32; 4]; 4] = - (camera.view_matrix * local_to_world.0).to_cols_array_2d(); - - renderer.create_buffer_mapped( - "camera_tmp", - matrix_size, - wgpu::BufferUsage::COPY_SRC, - &mut |data| { - data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes()); - }, - ); - - renderer.copy_buffer_to_buffer( - "camera_tmp", - 0, - resource_name::uniform::CAMERA, - 0, - matrix_size as u64, - ); - } - } -} - -pub struct LightResourceProvider { - pub lights_are_dirty: bool, - pub max_lights: usize, -} - -#[repr(C)] -#[derive(Clone, Copy, AsBytes)] -pub struct LightCount { - pub num_lights: [u32; 4], -} - -impl LightResourceProvider { - pub fn new(max_lights: usize) -> Self { - LightResourceProvider { - lights_are_dirty: true, - max_lights: max_lights, - } - } -} - -impl ResourceProvider for LightResourceProvider { - fn initialize(&mut self, renderer: &mut dyn Renderer, _world: &mut World) { - let light_uniform_size = - (std::mem::size_of::() + self.max_lights * std::mem::size_of::()) as wgpu::BufferAddress; - - renderer.create_buffer( - resource_name::uniform::LIGHTS, - light_uniform_size, - wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::COPY_DST, - ); - } - - fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World) { - if self.lights_are_dirty { - let light_query = <(Read, Read, Read)>::query(); - let light_count = light_query.iter(world).count(); - - if light_count == 0 { - return; - } - - self.lights_are_dirty = false; - let size = std::mem::size_of::(); - let total_size = size * light_count; - let light_count_size = std::mem::size_of::(); - renderer - .create_buffer_mapped("LIGHT_TMP", total_size, wgpu::BufferUsage::COPY_SRC, &mut |data| { - for ((light, local_to_world, translation), slot) in light_query - .iter(world) - .zip(data.chunks_exact_mut(size)) - { - slot.copy_from_slice( - LightRaw::from(&light, &local_to_world.0, &translation).as_bytes(), - ); - } - }); - renderer - .create_buffer_mapped("LIGHT_COUNT_TMP", light_count_size, wgpu::BufferUsage::COPY_SRC, &mut |data| { - data.copy_from_slice([light_count as u32, 0, 0, 0].as_bytes()); - }); - - renderer.copy_buffer_to_buffer( - "LIGHT_COUNT_TMP", - 0, - resource_name::uniform::LIGHTS, - 0, - light_count_size as wgpu::BufferAddress, - ); - - renderer.copy_buffer_to_buffer( - "LIGHT_TMP", - 0, - resource_name::uniform::LIGHTS, - light_count_size as u64, - total_size as wgpu::BufferAddress, - ); - } - } - fn resize(&mut self, renderer: &mut dyn Renderer, world: &mut World, width: u32, height: u32) {} -} - -pub struct FrameTexture { - pub name: String, - pub descriptor: TextureDescriptor, -} - -impl FrameTexture { - pub fn new(name: &str, descriptor: TextureDescriptor) -> Self { - FrameTexture { - name: name.to_string(), - descriptor, - } - } - - pub fn update(&mut self, renderer: &mut dyn Renderer, world: &World) { - let window = world.resources.get::().unwrap(); - let window_size = window.inner_size(); - self.descriptor.size.width = window_size.width; - self.descriptor.size.height = window_size.height; - renderer.create_texture(&self.name, &self.descriptor); - } -} - -impl ResourceProvider for FrameTexture { - fn initialize(&mut self, renderer: &mut dyn Renderer, world: &mut World) { - self.update(renderer, world); - } - - fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World) { - - } - - fn resize(&mut self, renderer: &mut dyn Renderer, world: &mut World, width: u32, height: u32) { - self.update(renderer, world); - } } \ No newline at end of file diff --git a/src/render/render_graph_2/resource_providers/camera2d_resource_provider.rs b/src/render/render_graph_2/resource_providers/camera2d_resource_provider.rs new file mode 100644 index 0000000000..4be4dd6235 --- /dev/null +++ b/src/render/render_graph_2/resource_providers/camera2d_resource_provider.rs @@ -0,0 +1,48 @@ +use crate::{render::{ + render_graph_2::{resource_name, Renderer}, + ActiveCamera2d, Camera, +}}; +use legion::prelude::*; +use zerocopy::AsBytes; +use crate::render::render_graph_2::ResourceProvider; + +pub struct Camera2dResourceProvider; + +impl ResourceProvider for Camera2dResourceProvider { + fn initialize(&mut self, renderer: &mut dyn Renderer, _world: &mut World) { + renderer.create_buffer( + resource_name::uniform::CAMERA2D, + std::mem::size_of::<[[f32; 4]; 4]>() as u64, + wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, + ); + } + + fn update(&mut self, _renderer: &mut dyn Renderer, _world: &mut World) {} + fn resize(&mut self, renderer: &mut dyn Renderer, world: &mut World, width: u32, height: u32) { + let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>(); + for (mut camera, _) in + <(Write, Read)>::query().iter_mut(world) + { + camera.update(width, height); + let camera_matrix: [[f32; 4]; 4] = + camera.view_matrix.to_cols_array_2d(); + + renderer.create_buffer_mapped( + "camera2d_tmp", + matrix_size, + wgpu::BufferUsage::COPY_SRC, + &mut |data| { + data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes()); + }, + ); + + renderer.copy_buffer_to_buffer( + "camera2d_tmp", + 0, + resource_name::uniform::CAMERA2D, + 0, + matrix_size as u64, + ); + } + } +} \ No newline at end of file diff --git a/src/render/render_graph_2/resource_providers/camera_resource_provider.rs b/src/render/render_graph_2/resource_providers/camera_resource_provider.rs new file mode 100644 index 0000000000..be4e10a7fc --- /dev/null +++ b/src/render/render_graph_2/resource_providers/camera_resource_provider.rs @@ -0,0 +1,49 @@ +use crate::{render::{ + render_graph_2::{resource_name, Renderer}, + ActiveCamera, Camera, +}}; +use bevy_transform::prelude::LocalToWorld; +use legion::prelude::*; +use zerocopy::AsBytes; +use crate::render::render_graph_2::ResourceProvider; + +pub struct CameraResourceProvider; + +impl ResourceProvider for CameraResourceProvider { + fn initialize(&mut self, renderer: &mut dyn Renderer, _world: &mut World) { + renderer.create_buffer( + resource_name::uniform::CAMERA, + std::mem::size_of::<[[f32; 4]; 4]>() as u64, + wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, + ); + } + + fn update(&mut self, _renderer: &mut dyn Renderer, _world: &mut World) {} + fn resize(&mut self, renderer: &mut dyn Renderer, world: &mut World, width: u32, height: u32) { + let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>(); + for (mut camera, local_to_world, _) in + <(Write, Read, Read)>::query().iter_mut(world) + { + camera.update(width, height); + let camera_matrix: [[f32; 4]; 4] = + (camera.view_matrix * local_to_world.0).to_cols_array_2d(); + + renderer.create_buffer_mapped( + "camera_tmp", + matrix_size, + wgpu::BufferUsage::COPY_SRC, + &mut |data| { + data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes()); + }, + ); + + renderer.copy_buffer_to_buffer( + "camera_tmp", + 0, + resource_name::uniform::CAMERA, + 0, + matrix_size as u64, + ); + } + } +} \ No newline at end of file diff --git a/src/render/render_graph_2/resource_providers/frame_texture_resource_provider.rs b/src/render/render_graph_2/resource_providers/frame_texture_resource_provider.rs new file mode 100644 index 0000000000..dd5a9b7654 --- /dev/null +++ b/src/render/render_graph_2/resource_providers/frame_texture_resource_provider.rs @@ -0,0 +1,38 @@ +use crate::prelude::World; +use crate::render::render_graph_2::Renderer; +use crate::render::render_graph_2::ResourceProvider; +use crate::render::render_graph_2::TextureDescriptor; + +pub struct FrameTextureResourceProvider { + pub name: String, + pub descriptor: TextureDescriptor, +} + +impl FrameTextureResourceProvider { + pub fn new(name: &str, descriptor: TextureDescriptor) -> Self { + FrameTextureResourceProvider { + name: name.to_string(), + descriptor, + } + } + + pub fn update(&mut self, renderer: &mut dyn Renderer, world: &World) { + let window = world.resources.get::().unwrap(); + let window_size = window.inner_size(); + self.descriptor.size.width = window_size.width; + self.descriptor.size.height = window_size.height; + renderer.create_texture(&self.name, &self.descriptor); + } +} + +impl ResourceProvider for FrameTextureResourceProvider { + fn initialize(&mut self, renderer: &mut dyn Renderer, world: &mut World) { + self.update(renderer, world); + } + + fn update(&mut self, _renderer: &mut dyn Renderer, _world: &mut World) {} + + fn resize(&mut self, renderer: &mut dyn Renderer, world: &mut World, _width: u32, _height: u32) { + self.update(renderer, world); + } +} diff --git a/src/render/render_graph_2/resource_providers/light_resource_provider.rs b/src/render/render_graph_2/resource_providers/light_resource_provider.rs new file mode 100644 index 0000000000..7b6f1c8ad9 --- /dev/null +++ b/src/render/render_graph_2/resource_providers/light_resource_provider.rs @@ -0,0 +1,89 @@ +use zerocopy::AsBytes; +use legion::prelude::*; +use crate::render::render_graph_2::ResourceProvider; +use crate::render::render_graph_2::resource_name; +use crate::render::render_graph_2::Renderer; +use crate::render::{Light, LightRaw}; +use bevy_transform::prelude::{LocalToWorld, Translation}; + +pub struct LightResourceProvider { + pub lights_are_dirty: bool, + pub max_lights: usize, +} + +#[repr(C)] +#[derive(Clone, Copy, AsBytes)] +pub struct LightCount { + pub num_lights: [u32; 4], +} + +impl LightResourceProvider { + pub fn new(max_lights: usize) -> Self { + LightResourceProvider { + lights_are_dirty: true, + max_lights: max_lights, + } + } +} + +impl ResourceProvider for LightResourceProvider { + fn initialize(&mut self, renderer: &mut dyn Renderer, _world: &mut World) { + let light_uniform_size = + (std::mem::size_of::() + self.max_lights * std::mem::size_of::()) as wgpu::BufferAddress; + + renderer.create_buffer( + resource_name::uniform::LIGHTS, + light_uniform_size, + wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::COPY_DST, + ); + } + + fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World) { + if self.lights_are_dirty { + let light_query = <(Read, Read, Read)>::query(); + let light_count = light_query.iter(world).count(); + + if light_count == 0 { + return; + } + + self.lights_are_dirty = false; + let size = std::mem::size_of::(); + let total_size = size * light_count; + let light_count_size = std::mem::size_of::(); + renderer + .create_buffer_mapped("LIGHT_TMP", total_size, wgpu::BufferUsage::COPY_SRC, &mut |data| { + for ((light, local_to_world, translation), slot) in light_query + .iter(world) + .zip(data.chunks_exact_mut(size)) + { + slot.copy_from_slice( + LightRaw::from(&light, &local_to_world.0, &translation).as_bytes(), + ); + } + }); + renderer + .create_buffer_mapped("LIGHT_COUNT_TMP", light_count_size, wgpu::BufferUsage::COPY_SRC, &mut |data| { + data.copy_from_slice([light_count as u32, 0, 0, 0].as_bytes()); + }); + + renderer.copy_buffer_to_buffer( + "LIGHT_COUNT_TMP", + 0, + resource_name::uniform::LIGHTS, + 0, + light_count_size as wgpu::BufferAddress, + ); + + renderer.copy_buffer_to_buffer( + "LIGHT_TMP", + 0, + resource_name::uniform::LIGHTS, + light_count_size as u64, + total_size as wgpu::BufferAddress, + ); + } + } + + fn resize(&mut self, _renderer: &mut dyn Renderer, _world: &mut World, _width: u32, _height: u32) {} +} \ No newline at end of file diff --git a/src/render/render_graph_2/resource_providers/mod.rs b/src/render/render_graph_2/resource_providers/mod.rs new file mode 100644 index 0000000000..2d26cae466 --- /dev/null +++ b/src/render/render_graph_2/resource_providers/mod.rs @@ -0,0 +1,11 @@ +mod ui_resource_provider; +mod frame_texture_resource_provider; +mod light_resource_provider; +mod camera_resource_provider; +mod camera2d_resource_provider; + +pub use ui_resource_provider::*; +pub use frame_texture_resource_provider::*; +pub use light_resource_provider::*; +pub use camera_resource_provider::*; +pub use camera2d_resource_provider::*; \ No newline at end of file diff --git a/src/render/render_graph_2/resource_providers/ui_resource_provider.rs b/src/render/render_graph_2/resource_providers/ui_resource_provider.rs new file mode 100644 index 0000000000..fc66cceb0c --- /dev/null +++ b/src/render/render_graph_2/resource_providers/ui_resource_provider.rs @@ -0,0 +1,100 @@ +use crate::prelude::Node; +use crate::render::render_graph_2::resource_name; +use crate::render::render_graph_2::ResourceProvider; +use crate::{ecs, render::render_graph_2::Renderer, asset::{Handle, Mesh, Asset, AssetStorage}, math}; +use bevy_transform::prelude::Parent; +use legion::prelude::*; +use zerocopy::{AsBytes, FromBytes}; +use crate::asset::MeshType; + +#[repr(C)] +#[derive(Clone, Copy, Debug, AsBytes, FromBytes)] +pub struct RectData { + pub position: [f32; 2], + pub size: [f32; 2], + pub color: [f32; 4], + pub z_index: f32, +} + +pub struct UiResourceProvider { + pub quad: Option>, +} + +impl UiResourceProvider { + pub fn new() -> Self { + UiResourceProvider { + quad: None, + } + } + + pub fn update(&mut self, renderer: &mut dyn Renderer, world: &World) { + let node_query = >::query().filter(!component::()); + + let mut data = Vec::new(); + if node_query.iter(world).count() > 0 { + // TODO: this probably isn't the best way to handle z-ordering + let mut z = 0.9999; + { + let mut add_data: Box Option<()>> = + Box::new(|world, entity, _| { + let node = world.get_component::(entity).unwrap(); + data.push(RectData { + position: node.global_position.into(), + size: node.size.into(), + color: node.color.into(), + z_index: z, + }); + + z -= 0.0001; + Some(()) + }); + + for entity in node_query + .iter_entities(world) + .map(|(entity, _)| entity) + .collect::>() + { + ecs::run_on_hierarchy(world, entity, (), &mut add_data); + } + } + } + + if data.len() == 0 { + return + } + + let size = std::mem::size_of::(); + + let mesh_id = self.quad.as_ref().unwrap().id; + + renderer.create_instance_buffer_with_data( + resource_name::buffer::UI_INSTANCES, + mesh_id, + data.as_bytes(), + size, + data.len(), + wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::VERTEX, + ); + } +} + +impl ResourceProvider for UiResourceProvider { + fn initialize(&mut self, _renderer: &mut dyn Renderer, world: &mut World) { + // self.update(renderer, world); + let mut mesh_storage = world.resources.get_mut::>().unwrap(); + let quad = Mesh::load(MeshType::Quad { + north_west: math::vec2(-0.5, 0.5), + north_east: math::vec2(0.5, 0.5), + south_west: math::vec2(-0.5, -0.5), + south_east: math::vec2(0.5, -0.5), + }); + self.quad = Some(mesh_storage.add(quad)); + } + + fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World) { + self.update(renderer, world); + } + + fn resize(&mut self, _renderer: &mut dyn Renderer, _world: &mut World, _width: u32, _height: u32) { + } +} diff --git a/src/render/render_graph_2/shader.rs b/src/render/render_graph_2/shader.rs index bc7e55bc68..4dc892dec0 100644 --- a/src/render/render_graph_2/shader.rs +++ b/src/render/render_graph_2/shader.rs @@ -260,7 +260,7 @@ impl ResourceProvider for UniformResourceProvider where T: AsUniforms + Send + Sync + 'static, { - fn initialize(&mut self, renderer: &mut dyn super::Renderer, world: &mut World) {} + fn initialize(&mut self, _renderer: &mut dyn super::Renderer, _world: &mut World) {} fn update(&mut self, renderer: &mut dyn super::Renderer, world: &mut World) { let query = >::query(); @@ -381,7 +381,7 @@ where fn resize( &mut self, - renderer: &mut dyn super::Renderer, + _renderer: &mut dyn super::Renderer, _world: &mut World, _width: u32, _height: u32, diff --git a/src/render/render_graph_2/wgpu_renderer.rs b/src/render/render_graph_2/wgpu_renderer.rs index 3388a43283..d7eac23f3b 100644 --- a/src/render/render_graph_2/wgpu_renderer.rs +++ b/src/render/render_graph_2/wgpu_renderer.rs @@ -255,6 +255,7 @@ impl WgpuRenderer { // if a uniform resource buffer doesn't exist, create a new empty one for binding in bind_group.bindings.iter() { if let None = self.resource_info.get(&binding.name) { + println!("Warning: creating new empty buffer for binding {}", binding.name); unset_uniforms.push(binding.name.to_string()); if let BindType::Uniform { .. } = &binding.bind_type { let size = binding.bind_type.get_uniform_size().unwrap(); @@ -584,6 +585,41 @@ impl Renderer for WgpuRenderer { self.buffers.insert(name.to_string(), buffer); } + fn create_instance_buffer(&mut self, name: &str, mesh_id: usize, size: usize, count: usize, buffer_usage: wgpu::BufferUsage) { + let buffer = self.device.create_buffer(&wgpu::BufferDescriptor { + size: (size * count) as u64, + usage: buffer_usage, + }); + + self.add_resource_info( + name, + ResourceInfo::InstanceBuffer { + buffer_usage, + size, + count, + mesh_id, + }, + ); + + self.buffers.insert(name.to_string(), buffer); + } + + fn create_instance_buffer_with_data(&mut self, name: &str, mesh_id: usize, data: &[u8], size: usize, count: usize, buffer_usage: wgpu::BufferUsage) { + let buffer = self.device.create_buffer_with_data(data, buffer_usage); + + self.add_resource_info( + name, + ResourceInfo::InstanceBuffer { + buffer_usage, + size, + count, + mesh_id, + }, + ); + + self.buffers.insert(name.to_string(), buffer); + } + fn get_resource_info(&self, name: &str) -> Option<&ResourceInfo> { self.resource_info.get(name) } @@ -719,6 +755,7 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> { } } + // TODO: check to see if bind group is already set self.render_pass.set_bind_group( i as u32, &bind_group_info.bind_group,