diff --git a/crates/bevy_tiles_render/src/draw.rs b/crates/bevy_tiles_render/src/draw.rs index 6fe0637..13b7e73 100644 --- a/crates/bevy_tiles_render/src/draw.rs +++ b/crates/bevy_tiles_render/src/draw.rs @@ -3,7 +3,7 @@ use bevy::{ ecs::{ query::ROQueryItem, system::{ - lifetimeless::{Read, SRes}, + lifetimeless::{Read, SRes, SResMut}, SystemParamItem, }, }, @@ -12,14 +12,22 @@ use bevy::{ render_phase::{ PhaseItem, RenderCommand, RenderCommandResult, SetItemPipeline, TrackedRenderPass, }, - render_resource::PipelineCache, + render_resource::{BindGroupEntries, PipelineCache}, + renderer::{RenderDevice, RenderQueue}, }, sprite::{DrawMesh2d, SetMesh2dViewBindGroup}, + transform::components::GlobalTransform, }; -use crate::{prepare::TilesChunkMeshBindGroup, tiles_render::TriThingBindGroups}; +use crate::{ + bindings::TilesChunkModelUniform, gpu_storage_buffer::GpuStorageBuffer, + pipeline::TilesChunkPipeline, prepare::TilesChunkMeshBindGroup, + tiles_render::TriThingBindGroups, +}; + +pub type LoadChunk = AddTransform; -pub type DrawTiles = ( +pub type DrawChunks = ( // Set the pipeline SetItemPipeline, // Set the view uniform as bind group 0 @@ -27,9 +35,73 @@ pub type DrawTiles = ( // Set the mesh uniform as bind group 1 SetChunkBindGroup<1>, // Draw the mesh - DrawChunks, + DrawChunkBatch, ); +pub struct AddTransform; +impl RenderCommand for AddTransform { + type Param = SResMut>; + + type ViewWorldQuery = (); + + type ItemWorldQuery = Read; + + #[inline] + fn render<'w>( + item: &Transparent2d, + _view: (), + uniforms: ROQueryItem<'w, Self::ItemWorldQuery>, + mut chunk_uniforms: SystemParamItem<'w, '_, Self::Param>, + pass: &mut TrackedRenderPass<'w>, + ) -> RenderCommandResult { + chunk_uniforms.push(uniforms.into()); + RenderCommandResult::Success + } +} +pub struct CreateUniformBindGroups; +impl RenderCommand for CreateUniformBindGroups { + type Param = ( + SRes, + SRes, + SRes, + SRes>, + SResMut, + ); + + type ViewWorldQuery = (); + + type ItemWorldQuery = (); + + #[inline] + fn render<'w>( + item: &Transparent2d, + _view: (), + _item_query: (), + (device, queue, base_pipeline, mut model_buffer, mut bind_groups): SystemParamItem< + 'w, + '_, + Self::Param, + >, + pass: &mut TrackedRenderPass<'w>, + ) -> RenderCommandResult { + model_buffer.write_buffer(&device, &queue); + + let Some(binding) = model_buffer.binding() else { + return RenderCommandResult::Failure; + }; + + let bind_group = device.create_bind_group( + "tiles_bind_group", + &base_pipeline.tiles_bind_groups.mesh_layout, + &BindGroupEntries::single(binding), + ); + + bind_groups.value = Some(bind_group); + + RenderCommandResult::Success + } +} + pub struct SetChunkBindGroup; impl RenderCommand for SetChunkBindGroup { type Param = SRes; @@ -52,17 +124,18 @@ impl RenderCommand for SetChunkBindGroup { dynamic_offsets[offset_count] = dynamic_offset.get(); offset_count += 1; } - pass.set_bind_group( - I, - &mesh2d_bind_group.into_inner().value, - &dynamic_offsets[..offset_count], - ); + + let Some(model_bindgroup) = mesh2d_bind_group.value else { + return RenderCommandResult::Failure; + }; + + pass.set_bind_group(I, &model_bindgroup, &dynamic_offsets[..offset_count]); RenderCommandResult::Success } } -pub struct DrawChunks; -impl RenderCommand for DrawChunks { +pub struct DrawChunkBatch; +impl RenderCommand for DrawChunkBatch { type Param = (); type ViewWorldQuery = (); diff --git a/crates/bevy_tiles_render/src/extract.rs b/crates/bevy_tiles_render/src/extract.rs index cdb2c17..96c12dc 100644 --- a/crates/bevy_tiles_render/src/extract.rs +++ b/crates/bevy_tiles_render/src/extract.rs @@ -12,7 +12,7 @@ use bevy::{ use crate::tiles::TriThing; -pub fn extract_tri_things( +pub fn extract_chunks( mut commands: Commands, tri_things: Extract>, ) { diff --git a/crates/bevy_tiles_render/src/gpu_storage_buffer.rs b/crates/bevy_tiles_render/src/gpu_storage_buffer.rs index 499b02f..2c783b4 100644 --- a/crates/bevy_tiles_render/src/gpu_storage_buffer.rs +++ b/crates/bevy_tiles_render/src/gpu_storage_buffer.rs @@ -1,7 +1,7 @@ use std::{marker::PhantomData, mem}; use bevy::{ - ecs::system::Resource, + ecs::{system::Resource, world::FromWorld}, render::{ render_resource::{ BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, @@ -20,14 +20,16 @@ pub struct GpuStorageBuffer { buffer: Vec, } -impl GpuStorageBuffer { - pub fn new(device: &RenderDevice) -> Self { +impl Default for GpuStorageBuffer { + fn default() -> Self { Self { - gpu_buffer: StorageBuffer::default(), - buffer: Vec::new(), + gpu_buffer: Default::default(), + buffer: Default::default(), } } +} +impl GpuStorageBuffer { pub fn clear(&mut self) { self.buffer.clear() } diff --git a/crates/bevy_tiles_render/src/lib.rs b/crates/bevy_tiles_render/src/lib.rs index ae9ee22..52a60c3 100644 --- a/crates/bevy_tiles_render/src/lib.rs +++ b/crates/bevy_tiles_render/src/lib.rs @@ -10,14 +10,16 @@ use bevy::{ ExtractSchedule, Render, RenderApp, RenderSet, }, }; -use draw::DrawTiles; -use extract::extract_tri_things; -use prepare::prepare_chunk_bind_groups; -use queue::queue_tri_things; + +use extract::extract_chunks; +use queue::queue_chunks; use crate::{ - bindings::TilesChunkModelUniform, gpu_storage_buffer::GpuStorageBuffer, + bindings::TilesChunkModelUniform, + draw::{DrawChunkBatch, LoadChunk}, + gpu_storage_buffer::GpuStorageBuffer, pipeline::TilesChunkPipeline, + prepare::TilesChunkMeshBindGroup, }; mod bindings; @@ -27,6 +29,7 @@ mod gpu_storage_buffer; mod pipeline; mod prepare; mod queue; +pub mod settings; pub mod tiles; mod tiles_render; @@ -41,9 +44,8 @@ impl Plugin for TilesRenderPlugin { // Respawn chunks that we saved from the last frame // Copy over tile data - render_app.add_systems(ExtractSchedule, extract_tri_things); - render_app.add_systems(Render, prepare_chunk_bind_groups.in_set(RenderSet::Prepare)); - render_app.add_systems(Render, queue_tri_things.in_set(RenderSet::Queue)); + render_app.add_systems(ExtractSchedule, extract_chunks); + render_app.add_systems(Render, queue_chunks.in_set(RenderSet::Queue)); /* render_app.add_systems( ExtractSchedule, @@ -62,11 +64,11 @@ impl Plugin for TilesRenderPlugin { fn finish(&self, app: &mut bevy::prelude::App) { let render_app = app.get_sub_app_mut(RenderApp).expect("No RenderApp found!"); - render_app.add_render_command::(); + render_app.add_render_command::(); + render_app.add_render_command::(); render_app - .insert_resource(GpuStorageBuffer::::new( - render_app.world.resource::(), - )) + .init_resource::() + .init_resource::>() .init_resource::() .init_resource::>(); diff --git a/crates/bevy_tiles_render/src/prepare.rs b/crates/bevy_tiles_render/src/prepare.rs index 3129773..91f0978 100644 --- a/crates/bevy_tiles_render/src/prepare.rs +++ b/crates/bevy_tiles_render/src/prepare.rs @@ -20,31 +20,7 @@ use crate::{ pipeline::TilesChunkPipeline, tiles::TriThing, tiles_render::TriThingBindGroups, }; -#[derive(Resource)] +#[derive(Resource, Default)] pub struct TilesChunkMeshBindGroup { - pub value: BindGroup, -} - -pub fn prepare_chunk_bind_groups( - mut commands: Commands, - tiles_chunk_pipeline: Res, - render_device: Res, - render_queue: Res, - mut mesh_uniforms: ResMut>, - chunks: Query<&GlobalTransform, With>, -) { - mesh_uniforms.clear(); - for transform in chunks.iter() { - mesh_uniforms.push(transform.into()); - } - mesh_uniforms.write_buffer(&render_device, &render_queue); - if let Some(binding) = mesh_uniforms.binding() { - commands.insert_resource(TilesChunkMeshBindGroup { - value: render_device.create_bind_group( - "tiles_bind_group", - &tiles_chunk_pipeline.tiles_bind_groups.mesh_layout, - &BindGroupEntries::single(binding), - ), - }); - } + pub value: Option, } diff --git a/crates/bevy_tiles_render/src/queue.rs b/crates/bevy_tiles_render/src/queue.rs index 573ea39..9906b9d 100644 --- a/crates/bevy_tiles_render/src/queue.rs +++ b/crates/bevy_tiles_render/src/queue.rs @@ -17,39 +17,71 @@ use bevy::{ utils::FloatOrd, }; -use crate::{draw::DrawTiles, pipeline::TilesChunkPipeline, tiles::TriThing}; +use crate::{ + draw::{DrawChunkBatch, LoadChunk}, + pipeline::TilesChunkPipeline, + settings::ChunkRenderSettings, + tiles::TriThing, +}; -pub fn queue_tri_things( +pub fn queue_chunks( mut pipelines: ResMut>, colored_mesh2d_pipeline: Res, transparent_draw_functions: Res>, pipeline_cache: Res, msaa: Res, - mut views: Query<(&mut RenderPhase, &ExtractedView)>, + mut views: Query<(Entity, &mut RenderPhase, &ExtractedView)>, chunks: Query>, + chunk_render_settings: Res, ) { - for (mut transparent_phase, view) in &mut views { + for (view_id, 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::(); + let load_chunk = transparent_draw_functions.read().id::(); + let draw_chunks = transparent_draw_functions.read().id::(); + + let mut batch_size = 0; // Queue all entities visible to that view for chunk_id in chunks.iter() { - debug!("Drawing chunk: {:?}", chunk_id); + batch_size += 1; + transparent_phase.add(Transparent2d { entity: chunk_id, - draw_function: draw_tiles, + draw_function: load_chunk, 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(10.0), - // This material is not batched + sort_key: FloatOrd(0.0), batch_range: 0..1, dynamic_offset: None, }); + + if batch_size == chunk_render_settings.max_chunk_batch_size { + transparent_phase.add(Transparent2d { + entity: view_id, + draw_function: draw_chunks, + pipeline: pipeline_id, + sort_key: FloatOrd(0.0), + batch_range: 0..batch_size, + dynamic_offset: None, + }); + + batch_size = 0; + } + } + + // Add one more draw call at the end + if batch_size > 0 { + transparent_phase.add(Transparent2d { + entity: view_id, + draw_function: draw_chunks, + pipeline: pipeline_id, + sort_key: FloatOrd(0.0), + batch_range: 0..batch_size, + dynamic_offset: None, + }); } } } diff --git a/crates/bevy_tiles_render/src/settings.rs b/crates/bevy_tiles_render/src/settings.rs new file mode 100644 index 0000000..1b35ddd --- /dev/null +++ b/crates/bevy_tiles_render/src/settings.rs @@ -0,0 +1,14 @@ +use bevy::ecs::system::Resource; + +#[derive(Resource)] +pub struct ChunkRenderSettings { + pub max_chunk_batch_size: u32, +} + +impl Default for ChunkRenderSettings { + fn default() -> Self { + Self { + max_chunk_batch_size: 128, + } + } +}