From be12323e0ad09d50afb56400c0f22ab0a2857187 Mon Sep 17 00:00:00 2001 From: James Bell Date: Tue, 19 Dec 2023 23:46:38 -0500 Subject: [PATCH] Follow bevy example --- crates/bevy_tiles/clippy.toml => clippy.toml | 0 crates/bevy_tiles_render/src/bindings.rs | 42 +++++---- crates/bevy_tiles_render/src/draw.rs | 23 +++-- crates/bevy_tiles_render/src/pipeline.rs | 85 +++++++++++-------- crates/bevy_tiles_render/src/prepare.rs | 37 +------- crates/bevy_tiles_render/src/queue.rs | 53 +++++++----- .../src/shaders/tiles_frag.wgsl | 14 +-- .../src/shaders/tiles_vert.wgsl | 38 +++++---- 8 files changed, 156 insertions(+), 136 deletions(-) rename crates/bevy_tiles/clippy.toml => clippy.toml (100%) diff --git a/crates/bevy_tiles/clippy.toml b/clippy.toml similarity index 100% rename from crates/bevy_tiles/clippy.toml rename to clippy.toml diff --git a/crates/bevy_tiles_render/src/bindings.rs b/crates/bevy_tiles_render/src/bindings.rs index 7920b5b..329eac8 100644 --- a/crates/bevy_tiles_render/src/bindings.rs +++ b/crates/bevy_tiles_render/src/bindings.rs @@ -4,10 +4,11 @@ use bevy::{ ecs::{system::Resource, world::FromWorld}, math::{Mat3A, Mat4, Vec2}, render::{ + mesh::{Mesh, MeshVertexAttribute}, render_resource::{ self, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, Buffer, - BufferInitDescriptor, BufferUsages, ShaderStages, VertexAttribute, VertexBufferLayout, - VertexFormat, + BufferInitDescriptor, BufferUsages, PrimitiveTopology, ShaderStages, VertexAttribute, + VertexBufferLayout, VertexFormat, }, renderer::RenderDevice, }, @@ -38,25 +39,36 @@ pub struct TriThingMeshBuffer(pub Buffer); impl FromWorld for TriThingMeshBuffer { fn from_world(world: &mut bevy::prelude::World) -> Self { - let device = world.get_resource_mut::().unwrap(); - let mesh: [[f32; 2]; 6] = [ + let device = world + .get_resource::() + .expect("No render device found!"); + + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + let v_pos = vec![ + [0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [1.0, 1.0, 0.0], + [0.0, 0.0, 0.0], + [1.0, 1.0, 0.0], + [0.0, 1.0, 0.0], + ]; + let v_uv = vec![ [0.0, 0.0], + [1.0, 0.0], [1.0, 1.0], - [0.0, 1.0], [0.0, 0.0], - [1.0, 0.0], [1.0, 1.0], + [0.0, 1.0], ]; - let mesh: Vec = mesh - .iter() - .flat_map(|coord| coord.iter().flat_map(|val| val.to_be_bytes())) - .collect(); - let mesh = device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("tri_thing_default_mesh"), + + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, v_pos); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, v_uv); + + Self(device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("Chunk Quad"), + contents: &mesh.get_vertex_buffer_data(), usage: BufferUsages::VERTEX, - contents: &mesh, - }); - Self(mesh) + })) } } diff --git a/crates/bevy_tiles_render/src/draw.rs b/crates/bevy_tiles_render/src/draw.rs index c3078f4..e91c54f 100644 --- a/crates/bevy_tiles_render/src/draw.rs +++ b/crates/bevy_tiles_render/src/draw.rs @@ -9,14 +9,24 @@ use bevy::{ }, log::info, render::{ - render_phase::{RenderCommand, RenderCommandResult, TrackedRenderPass}, + render_phase::{RenderCommand, RenderCommandResult, SetItemPipeline, TrackedRenderPass}, render_resource::PipelineCache, }, + sprite::{DrawMesh2d, SetMesh2dBindGroup, SetMesh2dViewBindGroup}, }; use crate::{bindings::TriThingMeshBuffer, tiles_render::TriThingBindGroups}; -pub type DrawTiles = (SetPipeline, SetTilesBuffers); +pub type DrawTiles = ( + // Set the pipeline + SetItemPipeline, + // Set the view uniform as bind group 0 + SetMesh2dViewBindGroup<0>, + // Set the mesh uniform as bind group 1 + SetMesh2dBindGroup<1>, + // Draw the mesh + DrawChunks, +); pub struct SetPipeline; @@ -47,24 +57,23 @@ impl RenderCommand for SetPipeline { } } -pub struct SetTilesBuffers; -impl RenderCommand for SetTilesBuffers { +pub struct DrawChunks; +impl RenderCommand for DrawChunks { type Param = SRes; type ViewWorldQuery = (); - type ItemWorldQuery = Read; + type ItemWorldQuery = (); #[inline] fn render<'w>( _item: &Transparent2d, _view: ROQueryItem<'w, Self::ViewWorldQuery>, - bind_groups: ROQueryItem<'w, Self::ItemWorldQuery>, + _: ROQueryItem<'w, Self::ItemWorldQuery>, mesh: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { info!("Drawing!"); - pass.set_bind_group(0, &bind_groups.transform_bind_group, &[]); pass.set_vertex_buffer(0, mesh.into_inner().0.slice(..)); pass.draw(0..6, 0..1); info!("Did draw call!"); diff --git a/crates/bevy_tiles_render/src/pipeline.rs b/crates/bevy_tiles_render/src/pipeline.rs index 27e2b3a..c2fc09a 100644 --- a/crates/bevy_tiles_render/src/pipeline.rs +++ b/crates/bevy_tiles_render/src/pipeline.rs @@ -1,89 +1,104 @@ use bevy::{ - ecs::{system::Resource, world::FromWorld}, + ecs::{ + system::Resource, + world::{FromWorld, World}, + }, render::{ render_resource::{ BindGroupLayout, BlendState, ColorTargetState, ColorWrites, Face, FragmentState, FrontFace, MultisampleState, PolygonMode, PrimitiveState, PrimitiveTopology, - RenderPipelineDescriptor, SpecializedRenderPipeline, TextureFormat, VertexState, + RenderPipelineDescriptor, SpecializedRenderPipeline, TextureFormat, VertexBufferLayout, + VertexFormat, VertexState, VertexStepMode, }, texture::BevyDefault, + view::ViewTarget, }, - sprite::Mesh2dPipeline, + sprite::{Mesh2dPipeline, Mesh2dPipelineKey}, }; -use crate::{ - bindings::{TilesBindGroupLayouts, TilesVertexBufferLayouts}, - TILES_FRAG, TILES_VERT, -}; +use crate::{TILES_FRAG, TILES_VERT}; #[derive(Resource)] pub struct TilesPipeline { - vertex_layouts: TilesVertexBufferLayouts, - tile_layouts: TilesBindGroupLayouts, - view_layout: BindGroupLayout, + mesh2d_pipeline: Mesh2dPipeline, } -#[derive(PartialEq, Eq, Hash, Clone)] -pub struct TilesPipelineKey; - impl FromWorld for TilesPipeline { - fn from_world(world: &mut bevy::prelude::World) -> Self { - let mesh_2d_pipeline = world.get_resource::().unwrap(); - let tile_layouts = world.get_resource::().unwrap(); + fn from_world(world: &mut World) -> Self { Self { - vertex_layouts: TilesVertexBufferLayouts::default(), - view_layout: mesh_2d_pipeline.view_layout.clone(), - tile_layouts: tile_layouts.clone(), + mesh2d_pipeline: Mesh2dPipeline::from_world(world), } } } impl SpecializedRenderPipeline for TilesPipeline { - type Key = TilesPipelineKey; + type Key = Mesh2dPipelineKey; fn specialize( &self, key: Self::Key, ) -> bevy::render::render_resource::RenderPipelineDescriptor { + // Customize how to store the meshes' vertex attributes in the vertex buffer + // Our meshes only have position and color + let formats = vec![ + // Position + VertexFormat::Float32x3, + // Color + VertexFormat::Float32x2, + ]; + + let vertex_layout = + VertexBufferLayout::from_vertex_formats(VertexStepMode::Vertex, formats); + + let format = match key.contains(Mesh2dPipelineKey::HDR) { + true => ViewTarget::TEXTURE_FORMAT_HDR, + false => TextureFormat::bevy_default(), + }; + RenderPipelineDescriptor { - label: Some("tiles_pipeline".into()), - layout: vec![ - self.tile_layouts.transform_bind_group.clone(), - //self.view_layout.clone(), - ], vertex: VertexState { + // Use our custom shader shader: TILES_VERT, - shader_defs: Vec::new(), entry_point: "vs_main".into(), - buffers: vec![self.vertex_layouts.vertex_layout.clone()], + shader_defs: vec![], + // Use our custom vertex buffer + buffers: vec![vertex_layout], }, fragment: Some(FragmentState { + // Use our custom shader shader: TILES_FRAG, - shader_defs: Vec::new(), + shader_defs: vec![], entry_point: "fs_main".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), - blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING), + format, + blend: Some(BlendState::ALPHA_BLENDING), write_mask: ColorWrites::ALL, })], }), + // Use the two standard uniforms for 2d meshes + layout: vec![ + // Bind group 0 is the view uniform + self.mesh2d_pipeline.view_layout.clone(), + // Bind group 1 is the mesh uniform + self.mesh2d_pipeline.mesh_layout.clone(), + ], + push_constant_ranges: Vec::new(), primitive: PrimitiveState { - topology: PrimitiveTopology::TriangleList, - strip_index_format: None, front_face: FrontFace::Ccw, cull_mode: Some(Face::Back), unclipped_depth: false, polygon_mode: PolygonMode::Fill, conservative: false, + topology: PrimitiveTopology::TriangleList, + strip_index_format: None, }, depth_stencil: None, multisample: MultisampleState { - // TODO: MAKE THIS SPECIALIZED - count: 4, + count: key.msaa_samples(), mask: !0, alpha_to_coverage_enabled: false, }, - push_constant_ranges: vec![], + label: Some("tiles_pipeline".into()), } } } diff --git a/crates/bevy_tiles_render/src/prepare.rs b/crates/bevy_tiles_render/src/prepare.rs index b3c01aa..b7d14d1 100644 --- a/crates/bevy_tiles_render/src/prepare.rs +++ b/crates/bevy_tiles_render/src/prepare.rs @@ -32,40 +32,5 @@ pub fn prepare_tri_things( bindgroup_layouts: Res, tri_things: Query<(Entity, &GlobalTransform, &TriThing)>, ) { - for (entity, tri_thing_transform, _) in tri_things.iter() { - info!("Preparing!"); - - let transform_array: Vec = tri_thing_transform - .affine() - .matrix3 - .to_cols_array_2d() - .iter() - .flat_map(|[x, y, z]| [*x, *y, *z, 0.0].into_iter()) - .flat_map(|element| element.to_be_bytes()) - .collect(); - - let transform_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some(format!("tri_thing_transform_buffer_{:?}", entity).as_str()), - usage: BufferUsages::UNIFORM, - contents: &transform_array, - }); - - let transform_bind_group = render_device.create_bind_group( - Some(format!("tri_thing_bindgroup_{:?}", entity).as_str()), - &bindgroup_layouts.transform_bind_group, - &[BindGroupEntry { - binding: 0, - resource: BindingResource::Buffer(BufferBinding { - buffer: &transform_buffer, - offset: 0, - size: NonZeroU64::new(48), - }), - }], - ); - info!("{}", transform_buffer.size()); - - commands.entity(entity).insert(TriThingBindGroups { - transform_bind_group, - }); - } + for (entity, tri_thing_transform, _) in tri_things.iter() {} } diff --git a/crates/bevy_tiles_render/src/queue.rs b/crates/bevy_tiles_render/src/queue.rs index d252bf3..3c8ed5c 100644 --- a/crates/bevy_tiles_render/src/queue.rs +++ b/crates/bevy_tiles_render/src/queue.rs @@ -2,43 +2,54 @@ use bevy::{ core_pipeline::core_2d::Transparent2d, ecs::{ entity::Entity, + query::With, system::{Query, Res, ResMut}, }, - log::info, + log::{debug, info}, render::{ + mesh::Mesh, + render_asset::RenderAssets, render_phase::{DrawFunctions, RenderPhase}, - render_resource::{PipelineCache, SpecializedRenderPipelines}, + render_resource::{PipelineCache, PrimitiveTopology, SpecializedRenderPipelines}, + view::{ExtractedView, Msaa, VisibleEntities}, }, + sprite::{DrawMesh2d, Mesh2dPipelineKey, RenderMesh2dInstances}, utils::FloatOrd, }; -use crate::{ - draw::DrawTiles, - pipeline::{TilesPipeline, TilesPipelineKey}, - tiles::TriThing, -}; +use crate::{draw::DrawTiles, pipeline::TilesPipeline, tiles::TriThing}; pub fn queue_tri_things( - mut sp_pipeline: ResMut>, - mut views_query: Query<(Entity, &mut RenderPhase)>, - draw_functions: Res>, + mut pipelines: ResMut>, + colored_mesh2d_pipeline: Res, + transparent_draw_functions: Res>, pipeline_cache: Res, - pipeline: Res, - tri_things: Query<(Entity, &TriThing)>, + msaa: Res, + mut views: Query<(&mut RenderPhase, &ExtractedView)>, + chunks: Query>, ) { - for (view_entity, mut transparent_phase) in views_query.iter_mut() { - info!("Queuing!"); - for (tri_entity, _) in tri_things.iter() { - let pipeline = sp_pipeline.specialize(&pipeline_cache, &pipeline, TilesPipelineKey); - let draw_function = draw_functions.read().get_id::().unwrap(); + for (mut transparent_phase, view) in &mut views { + let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples()) + | Mesh2dPipelineKey::from_hdr(view.hdr) + | Mesh2dPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList); + let pipeline_id = pipelines.specialize(&pipeline_cache, &colored_mesh2d_pipeline, mesh_key); + + let draw_tiles = transparent_draw_functions.read().id::(); + + // Queue all entities visible to that view + for chunk_id in chunks.iter() { + debug!("Drawing chunk: {:?}", chunk_id); transparent_phase.add(Transparent2d { + entity: chunk_id, + draw_function: draw_tiles, + pipeline: pipeline_id, + // The 2d render items are sorted according to their z value before rendering, + // in order to get correct transparency sort_key: FloatOrd(0.0), - entity: tri_entity, - pipeline, - draw_function, + // This material is not batched batch_range: 0..1, dynamic_offset: None, - }) + }); } } } diff --git a/crates/bevy_tiles_render/src/shaders/tiles_frag.wgsl b/crates/bevy_tiles_render/src/shaders/tiles_frag.wgsl index 76e5d87..7d38c3c 100644 --- a/crates/bevy_tiles_render/src/shaders/tiles_frag.wgsl +++ b/crates/bevy_tiles_render/src/shaders/tiles_frag.wgsl @@ -1,7 +1,11 @@ -#import bevy_tiles::vert::{VertOut} +// The input of the fragment shader must correspond to the output of the vertex shader for all `location`s +struct FragIn { + // The color is interpolated between vertices by default + @location(0) color: vec4, +}; +/// Entry point for the fragment shader @fragment -fn fs_main(in: VertOut) -> @location(0) vec4 { - return vec4(1.0, 0.2, 0.1, 1.0); -} - \ No newline at end of file +fn fragment(in: FragIn) -> @location(0) vec4 { + return in.color; +} \ No newline at end of file diff --git a/crates/bevy_tiles_render/src/shaders/tiles_vert.wgsl b/crates/bevy_tiles_render/src/shaders/tiles_vert.wgsl index 03b018b..0e2f718 100644 --- a/crates/bevy_tiles_render/src/shaders/tiles_vert.wgsl +++ b/crates/bevy_tiles_render/src/shaders/tiles_vert.wgsl @@ -1,25 +1,29 @@ #define_import_path bevy_tiles::vert -#import bevy_sprite::mesh2d_view_bindings::view +// Import the standard 2d mesh uniforms and set their bind groups +#import bevy_sprite::mesh2d_functions +// The structure of the vertex buffer is as specified in `specialize()` struct VertIn { - @builtin(vertex_index) index: u32, - @location(0) position: vec2, -} - -@group(0) @binding(0) -var transform: mat3x3; + @builtin(instance_index) instance_index: u32, + @location(0) position: vec3, + @location(1) uv: vec2, +}; struct VertOut { + // The vertex shader must set the on-screen position of the vertex @builtin(position) clip_position: vec4, -} + // We pass the vertex color to the fragment shader in location 0 + @location(0) color: vec4, +}; +/// Entry point for the vertex shader @vertex -fn vs_main( - input: VertIn -) -> VertOut{ - var output : VertOut; - let x = f32(i32(input.index) - 1); - let y = f32(i32(input.index & 1u) * 2 - 1); - output.clip_position = vec4(x, y, 0.0, 1.0); - return output; -} +fn vs_main(v: VertIn) -> VertOut { + var out: VertOut; + // Project the world position of the mesh into screen position + let model = mesh2d_functions::get_model_matrix(v.instance_index); + out.clip_position = mesh2d_functions::mesh2d_position_local_to_clip(model, vec4(v.position, 1.0)); + // Unpack the `u32` from the vertex buffer into the `vec4` used by the fragment shader + out.color = vec4(v.uv, 1.0, 1.0); + return out; +} \ No newline at end of file