From fb0f05e36201d6546aa4b5f80094792c0558588d Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 5 Sep 2023 21:50:16 +0100 Subject: [PATCH 1/6] Almost working but corrupted text with multiple fonts --- crates/bevy_sprite/src/lib.rs | 1 + crates/bevy_sprite/src/render/mod.rs | 335 ++++++++++++++++++++------- crates/bevy_text/src/text2d.rs | 68 ++++-- 3 files changed, 298 insertions(+), 106 deletions(-) diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index b337df92d9687..0ec4ac681d0e8 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -80,6 +80,7 @@ impl Plugin for SpritePlugin { .init_resource::>() .init_resource::() .init_resource::() + .init_resource::() .init_resource::() .add_render_command::() .add_systems( diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 3949fbe10d733..d9d7b0c7c7663 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -328,6 +328,29 @@ pub struct ExtractedSprite { pub anchor: Vec2, } +pub struct ExtractedGlyphText { + pub sections: Vec, +} + +pub struct ExtractedGlyphSection { + pub transform: GlobalTransform, + pub color: Color, + pub atlas_id: HandleId, + pub range: Range, +} + +pub struct ExtractedGlyph { + pub rect: Rect, + pub position: Vec2, +} + +#[derive(Resource, Default)] +pub struct ExtractedGlyphs { + pub glyphs: Vec, + pub texts: SparseSet, + pub sections: SparseSet, +} + #[derive(Resource, Default)] pub struct ExtractedSprites { pub sprites: SparseSet, @@ -505,6 +528,7 @@ pub fn queue_sprites( pipeline_cache: Res, msaa: Res, extracted_sprites: Res, + extracted_glyphs: Res, mut views: Query<( &mut RenderPhase, &VisibleEntities, @@ -559,11 +583,11 @@ pub fn queue_sprites( transparent_phase .items - .reserve(extracted_sprites.sprites.len()); + .reserve(extracted_sprites.sprites.len() + extracted_glyphs.sections.len()); for (entity, extracted_sprite) in extracted_sprites.sprites.iter() { if !view_entities.contains(entity.index() as usize) { - continue; + // continue; } // These items will be sorted by depth with other phase items @@ -590,6 +614,43 @@ pub fn queue_sprites( }); } } + + for (entity, texts) in extracted_glyphs.texts.iter() { + if !view_entities.contains(entity.index() as usize) { + println!("not visible"); + // continue; + } + + // These items will be sorted by depth with other phase items + let mut z = 0.1; + for section_entity in texts.sections.iter() { + println!("queue section entity: {section_entity:?}"); + let section = extracted_glyphs.sections.get(*section_entity).unwrap(); + println!("\tglyph range: {:?}", section.range); + let sort_key = FloatOrd(section.transform.translation().z + z); + // Add the item to the render phase + if section.color != Color::WHITE { + transparent_phase.add(Transparent2d { + draw_function: draw_sprite_function, + pipeline: colored_pipeline, + entity: *section_entity, + sort_key, + // batch_size will be calculated in prepare_glyphs + batch_size: 0, + }); + } else { + transparent_phase.add(Transparent2d { + draw_function: draw_sprite_function, + pipeline, + entity: *section_entity, + sort_key, + // batch_size will be calculated in prepare_glyphs + batch_size: 0, + }); + } + z += 0.1; + } + } } } @@ -605,9 +666,11 @@ pub fn prepare_sprites( mut image_bind_groups: ResMut, gpu_images: Res>, extracted_sprites: Res, + extracted_glyphs: Res, mut phases: Query<&mut RenderPhase>, events: Res, ) { + println!("\n**** PREPARE ****"); // If an image has changed, the GpuImage has (probably) changed for event in &events.images { match event { @@ -647,97 +710,190 @@ pub fn prepare_sprites( // Spawn an entity with a `SpriteBatch` component for each possible batch. // Compatible items share the same entity. for item_index in 0..transparent_phase.items.len() { + println!("transparent item index: {item_index}"); let item = &transparent_phase.items[item_index]; - let Some(extracted_sprite) = extracted_sprites.sprites.get(item.entity) else { + println!("item id: {:?}", item.entity); + let mut batch_image_changed = false; + let n = + if let Some(extracted_sprite) = extracted_sprites.sprites.get(item.entity) { + if batch_image_handle != extracted_sprite.image_handle_id { + batch_image_changed = true; + println!("image_changed"); + } + if batch_image_changed { + let Some(gpu_image) = + gpu_images.get(&Handle::weak(extracted_sprite.image_handle_id)) + else { + continue; + }; + + batch_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y); + batch_image_handle = extracted_sprite.image_handle_id; + image_bind_groups + .values + .entry(Handle::weak(batch_image_handle)) + .or_insert_with(|| { + render_device.create_bind_group(&BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView( + &gpu_image.texture_view, + ), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&gpu_image.sampler), + }, + ], + label: Some("sprite_material_bind_group"), + layout: &sprite_pipeline.material_layout, + }) + }); + } + + // By default, the size of the quad is the size of the texture + let mut quad_size = batch_image_size; + + // Calculate vertex data for this item + let mut uv_offset_scale: Vec4; + + // If a rect is specified, adjust UVs and the size of the quad + if let Some(rect) = extracted_sprite.rect { + let rect_size = rect.size(); + uv_offset_scale = Vec4::new( + rect.min.x / batch_image_size.x, + rect.max.y / batch_image_size.y, + rect_size.x / batch_image_size.x, + -rect_size.y / batch_image_size.y, + ); + quad_size = rect_size; + } else { + uv_offset_scale = Vec4::new(0.0, 1.0, 1.0, -1.0); + } + + if extracted_sprite.flip_x { + uv_offset_scale.x += uv_offset_scale.z; + uv_offset_scale.z *= -1.0; + } + if extracted_sprite.flip_y { + uv_offset_scale.y += uv_offset_scale.w; + uv_offset_scale.w *= -1.0; + } + + // Override the size if a custom one is specified + if let Some(custom_size) = extracted_sprite.custom_size { + quad_size = custom_size; + } + let transform = extracted_sprite.transform.affine() + * Affine3A::from_scale_rotation_translation( + quad_size.extend(1.0), + Quat::IDENTITY, + (quad_size * (-extracted_sprite.anchor - Vec2::splat(0.5))).extend(0.0), + ); + + // Store the vertex data and add the item to the render phase + sprite_meta + .sprite_instance_buffer + .push(SpriteInstance::from( + &transform, + &extracted_sprite.color, + &uv_offset_scale, + )); + + // if batch_image_changed { + // batch_item_index = item_index; + + // batches.push(( + // item.entity, + // SpriteBatch { + // image_handle_id: batch_image_handle, + // range: index..index, + // }, + // )); + // } + + // transparent_phase.items[batch_item_index].batch_size += 1; + // batches.last_mut().unwrap().1.range.end += 1; + // index += 1; + + 1 + } else if let Some(section) = extracted_glyphs.sections.get(item.entity) { + println!("glyph section"); + if batch_image_handle != section.atlas_id { + batch_image_changed = true; + println!("image_changed: {:?}", section.atlas_id); + println!("batch changed"); + let Some(gpu_image) = + gpu_images.get(&Handle::weak(section.atlas_id)) + else { + println!("no image"); + continue; + }; + + batch_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y); + batch_image_handle = section.atlas_id; + image_bind_groups + .values + .entry(Handle::weak(batch_image_handle)) + .or_insert_with(|| { + render_device.create_bind_group(&BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView( + &gpu_image.texture_view, + ), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&gpu_image.sampler), + }, + ], + label: Some("sprite_material_bind_group"), + layout: &sprite_pipeline.material_layout, + }) + }); + } + println!("glyph range: {:?}", section.range); + for glyph in &extracted_glyphs.glyphs[section.range.clone()] { + // If a rect is specified, adjust UVs and the size of the quad + let rect_size = glyph.rect.size(); + println!("batch_image_size: {}", batch_image_size); + println!("rect_size: {}", rect_size); + let uv_offset_scale = Vec4::new( + glyph.rect.min.x / batch_image_size.x, + glyph.rect.max.y / batch_image_size.y, + rect_size.x / batch_image_size.x, + -rect_size.y / batch_image_size.y, + ); + println!("uv offset scale: {}", uv_offset_scale); + let transform = section.transform.affine() + * Affine3A::from_scale_rotation_translation( + rect_size.extend(1.0), + Quat::IDENTITY, + (glyph.position - 0.5 * rect_size).extend(0.)); + println!("position: {}", (glyph.position - 0.5 * rect_size).extend(0.)); + println!("global: {}", transform.translation); + // Store the vertex data and add the item to the render phase + sprite_meta + .sprite_instance_buffer + .push(SpriteInstance::from( + &transform, + §ion.color, + &uv_offset_scale, + )); + } + section.range.len() as u32 + + } else { // If there is a phase item that is not a sprite, then we must start a new // batch to draw the other phase item(s) and to respect draw order. This can be // done by invalidating the batch_image_handle + println!("INVALIDATE!"); batch_image_handle = HandleId::Id(Uuid::nil(), u64::MAX); continue; }; - - let batch_image_changed = batch_image_handle != extracted_sprite.image_handle_id; - if batch_image_changed { - let Some(gpu_image) = - gpu_images.get(&Handle::weak(extracted_sprite.image_handle_id)) - else { - continue; - }; - - batch_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y); - batch_image_handle = extracted_sprite.image_handle_id; - image_bind_groups - .values - .entry(Handle::weak(batch_image_handle)) - .or_insert_with(|| { - render_device.create_bind_group(&BindGroupDescriptor { - entries: &[ - BindGroupEntry { - binding: 0, - resource: BindingResource::TextureView( - &gpu_image.texture_view, - ), - }, - BindGroupEntry { - binding: 1, - resource: BindingResource::Sampler(&gpu_image.sampler), - }, - ], - label: Some("sprite_material_bind_group"), - layout: &sprite_pipeline.material_layout, - }) - }); - } - - // By default, the size of the quad is the size of the texture - let mut quad_size = batch_image_size; - - // Calculate vertex data for this item - let mut uv_offset_scale: Vec4; - - // If a rect is specified, adjust UVs and the size of the quad - if let Some(rect) = extracted_sprite.rect { - let rect_size = rect.size(); - uv_offset_scale = Vec4::new( - rect.min.x / batch_image_size.x, - rect.max.y / batch_image_size.y, - rect_size.x / batch_image_size.x, - -rect_size.y / batch_image_size.y, - ); - quad_size = rect_size; - } else { - uv_offset_scale = Vec4::new(0.0, 1.0, 1.0, -1.0); - } - - if extracted_sprite.flip_x { - uv_offset_scale.x += uv_offset_scale.z; - uv_offset_scale.z *= -1.0; - } - if extracted_sprite.flip_y { - uv_offset_scale.y += uv_offset_scale.w; - uv_offset_scale.w *= -1.0; - } - - // Override the size if a custom one is specified - if let Some(custom_size) = extracted_sprite.custom_size { - quad_size = custom_size; - } - let transform = extracted_sprite.transform.affine() - * Affine3A::from_scale_rotation_translation( - quad_size.extend(1.0), - Quat::IDENTITY, - (quad_size * (-extracted_sprite.anchor - Vec2::splat(0.5))).extend(0.0), - ); - - // Store the vertex data and add the item to the render phase - sprite_meta - .sprite_instance_buffer - .push(SpriteInstance::from( - &transform, - &extracted_sprite.color, - &uv_offset_scale, - )); - if batch_image_changed { batch_item_index = item_index; @@ -751,8 +907,9 @@ pub fn prepare_sprites( } transparent_phase.items[batch_item_index].batch_size += 1; - batches.last_mut().unwrap().1.range.end += 1; + batches.last_mut().unwrap().1.range.end += n; index += 1; + } } sprite_meta @@ -782,10 +939,12 @@ pub fn prepare_sprites( .sprite_index_buffer .write_buffer(&render_device, &render_queue); } - + println!("batch count: {}", batches.len()); *previous_len = batches.len(); commands.insert_or_spawn_batch(batches); } + + println!("**** PREPARE END **** \n"); } pub type DrawSprite = ( diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 79fcc40094fc0..7fcd5f66c9f46 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -17,7 +17,10 @@ use bevy_render::{ view::{InheritedVisibility, ViewVisibility, Visibility}, Extract, }; -use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, TextureAtlas}; +use bevy_sprite::{ + Anchor, ExtractedGlyph, ExtractedGlyphSection, ExtractedGlyphText, ExtractedGlyphs, + TextureAtlas, +}; use bevy_transform::prelude::{GlobalTransform, Transform}; use bevy_utils::HashSet; use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged}; @@ -80,11 +83,12 @@ pub struct Text2dBundle { pub fn extract_text2d_sprite( mut commands: Commands, - mut extracted_sprites: ResMut, + mut extracted_glyphs: ResMut, texture_atlases: Extract>>, windows: Extract>>, text2d_query: Extract< Query<( + Entity, &ViewVisibility, &Text, &TextLayoutInfo, @@ -93,6 +97,9 @@ pub fn extract_text2d_sprite( )>, >, ) { + extracted_glyphs.glyphs.clear(); + extracted_glyphs.sections.clear(); + extracted_glyphs.texts.clear(); // TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() @@ -100,7 +107,10 @@ pub fn extract_text2d_sprite( .unwrap_or(1.0); let scaling = GlobalTransform::from_scale(Vec3::splat(scale_factor.recip())); - for (view_visibility, text, text_layout_info, anchor, global_transform) in text2d_query.iter() { + for (entity, view_visibility, text, text_layout_info, anchor, global_transform) in + text2d_query.iter() + { + println!("Extract Text entity: {entity:?}"); if !view_visibility.get() { continue; } @@ -110,8 +120,9 @@ pub fn extract_text2d_sprite( let transform = *global_transform * scaling * GlobalTransform::from_translation(alignment_translation.extend(0.)); - let mut color = Color::WHITE; let mut current_section = usize::MAX; + let mut section_ids = Vec::with_capacity(text.sections.len()); + let mut count = 0; for PositionedGlyph { position, atlas_info, @@ -120,25 +131,46 @@ pub fn extract_text2d_sprite( } in &text_layout_info.glyphs { if *section_index != current_section { - color = text.sections[*section_index].style.color.as_rgba_linear(); + println!("section: {section_index}, start: {count}"); + let section_id = commands.spawn_empty().id(); + section_ids.push(section_id); + let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); + let extracted_section = ExtractedGlyphSection { + transform, + color: text.sections[*section_index].style.color, + atlas_id: atlas.texture.id(), + range: extracted_glyphs.glyphs.len()..extracted_glyphs.glyphs.len(), + }; + + extracted_glyphs + .sections + .insert(section_id, extracted_section); + current_section = *section_index; } + let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); - extracted_sprites.sprites.insert( - commands.spawn_empty().id(), - ExtractedSprite { - transform: transform * GlobalTransform::from_translation(position.extend(0.)), - color, - rect: Some(atlas.textures[atlas_info.glyph_index]), - custom_size: None, - image_handle_id: atlas.texture.id(), - flip_x: false, - flip_y: false, - anchor: Anchor::Center.as_vec(), - }, - ); + extracted_glyphs.glyphs.push(ExtractedGlyph { + rect: atlas.textures[atlas_info.glyph_index], + position: *position, + }); + count +=1; + extracted_glyphs + .sections + .get_mut(*section_ids.last().unwrap()) + .unwrap() + .range + .end += 1; } + println!("total glyph count: {count}"); + println!("extracted glyphs len {}:", extracted_glyphs.glyphs.len()); + extracted_glyphs.texts.insert( + entity, + ExtractedGlyphText { + sections: section_ids, + }, + ); } } From edfdbaf2af3ce9a9e1f51b289917c4967794197a Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 6 Sep 2023 00:38:47 +0100 Subject: [PATCH 2/6] Adds `ExtractedSpriteBatches`. This allows adding sprites in batches that all share the same entity for visibility checks. Then `extracted_text2d_sprite` extracts into `ExtractedSpriteBatches` instead of `ExtractedSprites` and text drawn with `Text2d` is visible again. --- crates/bevy_sprite/src/lib.rs | 2 +- crates/bevy_sprite/src/render/mod.rs | 190 ++++++--------------------- crates/bevy_text/src/text2d.rs | 63 ++++----- 3 files changed, 60 insertions(+), 195 deletions(-) diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index 0ec4ac681d0e8..b43c3841a127a 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -80,7 +80,7 @@ impl Plugin for SpritePlugin { .init_resource::>() .init_resource::() .init_resource::() - .init_resource::() + .init_resource::() .init_resource::() .add_render_command::() .add_systems( diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index d9d7b0c7c7663..555721827594c 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -328,31 +328,14 @@ pub struct ExtractedSprite { pub anchor: Vec2, } -pub struct ExtractedGlyphText { - pub sections: Vec, -} - -pub struct ExtractedGlyphSection { - pub transform: GlobalTransform, - pub color: Color, - pub atlas_id: HandleId, - pub range: Range, -} - -pub struct ExtractedGlyph { - pub rect: Rect, - pub position: Vec2, -} - #[derive(Resource, Default)] -pub struct ExtractedGlyphs { - pub glyphs: Vec, - pub texts: SparseSet, - pub sections: SparseSet, +pub struct ExtractedSprites { + pub sprites: SparseSet, } #[derive(Resource, Default)] -pub struct ExtractedSprites { +pub struct ExtractedSpriteBatches { + pub batches: SparseSet>, pub sprites: SparseSet, } @@ -528,7 +511,7 @@ pub fn queue_sprites( pipeline_cache: Res, msaa: Res, extracted_sprites: Res, - extracted_glyphs: Res, + extracted_batches: Res, mut views: Query<( &mut RenderPhase, &VisibleEntities, @@ -583,11 +566,11 @@ pub fn queue_sprites( transparent_phase .items - .reserve(extracted_sprites.sprites.len() + extracted_glyphs.sections.len()); + .reserve(extracted_sprites.sprites.len() + extracted_batches.sprites.len()); for (entity, extracted_sprite) in extracted_sprites.sprites.iter() { if !view_entities.contains(entity.index() as usize) { - // continue; + continue; } // These items will be sorted by depth with other phase items @@ -615,25 +598,21 @@ pub fn queue_sprites( } } - for (entity, texts) in extracted_glyphs.texts.iter() { - if !view_entities.contains(entity.index() as usize) { - println!("not visible"); - // continue; + for (batch_entity, sprite_entities) in extracted_batches.batches.iter() { + if !view_entities.contains(batch_entity.index() as usize) { + continue; } + for sprite_entity in sprite_entities.iter() { + let extracted_sprite = extracted_batches.sprites.get(*sprite_entity).unwrap(); + // These items will be sorted by depth with other phase items + let sort_key = FloatOrd(extracted_sprite.transform.translation().z); - // These items will be sorted by depth with other phase items - let mut z = 0.1; - for section_entity in texts.sections.iter() { - println!("queue section entity: {section_entity:?}"); - let section = extracted_glyphs.sections.get(*section_entity).unwrap(); - println!("\tglyph range: {:?}", section.range); - let sort_key = FloatOrd(section.transform.translation().z + z); // Add the item to the render phase - if section.color != Color::WHITE { + if extracted_sprite.color != Color::WHITE { transparent_phase.add(Transparent2d { draw_function: draw_sprite_function, pipeline: colored_pipeline, - entity: *section_entity, + entity: *sprite_entity, sort_key, // batch_size will be calculated in prepare_glyphs batch_size: 0, @@ -642,13 +621,12 @@ pub fn queue_sprites( transparent_phase.add(Transparent2d { draw_function: draw_sprite_function, pipeline, - entity: *section_entity, + entity: *sprite_entity, sort_key, // batch_size will be calculated in prepare_glyphs batch_size: 0, }); } - z += 0.1; } } } @@ -666,11 +644,10 @@ pub fn prepare_sprites( mut image_bind_groups: ResMut, gpu_images: Res>, extracted_sprites: Res, - extracted_glyphs: Res, + extracted_batches: Res, mut phases: Query<&mut RenderPhase>, events: Res, ) { - println!("\n**** PREPARE ****"); // If an image has changed, the GpuImage has (probably) changed for event in &events.images { match event { @@ -710,15 +687,11 @@ pub fn prepare_sprites( // Spawn an entity with a `SpriteBatch` component for each possible batch. // Compatible items share the same entity. for item_index in 0..transparent_phase.items.len() { - println!("transparent item index: {item_index}"); - let item = &transparent_phase.items[item_index]; - println!("item id: {:?}", item.entity); + let item = transparent_phase.items[item_index].entity; let mut batch_image_changed = false; - let n = - if let Some(extracted_sprite) = extracted_sprites.sprites.get(item.entity) { + if let Some(extracted_sprite) = extracted_sprites.sprites.get(item).or_else(|| extracted_batches.sprites.get(item)) { if batch_image_handle != extracted_sprite.image_handle_id { batch_image_changed = true; - println!("image_changed"); } if batch_image_changed { let Some(gpu_image) = @@ -801,115 +774,28 @@ pub fn prepare_sprites( &uv_offset_scale, )); - // if batch_image_changed { - // batch_item_index = item_index; - - // batches.push(( - // item.entity, - // SpriteBatch { - // image_handle_id: batch_image_handle, - // range: index..index, - // }, - // )); - // } - - // transparent_phase.items[batch_item_index].batch_size += 1; - // batches.last_mut().unwrap().1.range.end += 1; - // index += 1; - - 1 - } else if let Some(section) = extracted_glyphs.sections.get(item.entity) { - println!("glyph section"); - if batch_image_handle != section.atlas_id { - batch_image_changed = true; - println!("image_changed: {:?}", section.atlas_id); - println!("batch changed"); - let Some(gpu_image) = - gpu_images.get(&Handle::weak(section.atlas_id)) - else { - println!("no image"); - continue; - }; - - batch_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y); - batch_image_handle = section.atlas_id; - image_bind_groups - .values - .entry(Handle::weak(batch_image_handle)) - .or_insert_with(|| { - render_device.create_bind_group(&BindGroupDescriptor { - entries: &[ - BindGroupEntry { - binding: 0, - resource: BindingResource::TextureView( - &gpu_image.texture_view, - ), - }, - BindGroupEntry { - binding: 1, - resource: BindingResource::Sampler(&gpu_image.sampler), - }, - ], - label: Some("sprite_material_bind_group"), - layout: &sprite_pipeline.material_layout, - }) - }); - } - println!("glyph range: {:?}", section.range); - for glyph in &extracted_glyphs.glyphs[section.range.clone()] { - // If a rect is specified, adjust UVs and the size of the quad - let rect_size = glyph.rect.size(); - println!("batch_image_size: {}", batch_image_size); - println!("rect_size: {}", rect_size); - let uv_offset_scale = Vec4::new( - glyph.rect.min.x / batch_image_size.x, - glyph.rect.max.y / batch_image_size.y, - rect_size.x / batch_image_size.x, - -rect_size.y / batch_image_size.y, - ); - println!("uv offset scale: {}", uv_offset_scale); - let transform = section.transform.affine() - * Affine3A::from_scale_rotation_translation( - rect_size.extend(1.0), - Quat::IDENTITY, - (glyph.position - 0.5 * rect_size).extend(0.)); - println!("position: {}", (glyph.position - 0.5 * rect_size).extend(0.)); - println!("global: {}", transform.translation); - // Store the vertex data and add the item to the render phase - sprite_meta - .sprite_instance_buffer - .push(SpriteInstance::from( - &transform, - §ion.color, - &uv_offset_scale, - )); + if batch_image_changed { + batch_item_index = item_index; + + batches.push(( + item, + SpriteBatch { + image_handle_id: batch_image_handle, + range: index..index, + }, + )); } - section.range.len() as u32 - + + transparent_phase.items[batch_item_index].batch_size += 1; + batches.last_mut().unwrap().1.range.end += 1; + index += 1; } else { // If there is a phase item that is not a sprite, then we must start a new // batch to draw the other phase item(s) and to respect draw order. This can be // done by invalidating the batch_image_handle - println!("INVALIDATE!"); batch_image_handle = HandleId::Id(Uuid::nil(), u64::MAX); continue; - }; - if batch_image_changed { - batch_item_index = item_index; - - batches.push(( - item.entity, - SpriteBatch { - image_handle_id: batch_image_handle, - range: index..index, - }, - )); - } - - transparent_phase.items[batch_item_index].batch_size += 1; - batches.last_mut().unwrap().1.range.end += n; - index += 1; - + }; } } sprite_meta @@ -939,12 +825,9 @@ pub fn prepare_sprites( .sprite_index_buffer .write_buffer(&render_device, &render_queue); } - println!("batch count: {}", batches.len()); *previous_len = batches.len(); commands.insert_or_spawn_batch(batches); } - - println!("**** PREPARE END **** \n"); } pub type DrawSprite = ( @@ -1032,4 +915,5 @@ impl RenderCommand

for DrawSpriteBatch { pass.draw_indexed(0..6, 0, batch.range.clone()); RenderCommandResult::Success } -} + +} \ No newline at end of file diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 7fcd5f66c9f46..bfc02a28249c1 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -18,8 +18,7 @@ use bevy_render::{ Extract, }; use bevy_sprite::{ - Anchor, ExtractedGlyph, ExtractedGlyphSection, ExtractedGlyphText, ExtractedGlyphs, - TextureAtlas, + Anchor, TextureAtlas, ExtractedSpriteBatches, ExtractedSprite, }; use bevy_transform::prelude::{GlobalTransform, Transform}; use bevy_utils::HashSet; @@ -83,7 +82,7 @@ pub struct Text2dBundle { pub fn extract_text2d_sprite( mut commands: Commands, - mut extracted_glyphs: ResMut, + mut extracted_batches: ResMut, texture_atlases: Extract>>, windows: Extract>>, text2d_query: Extract< @@ -97,9 +96,8 @@ pub fn extract_text2d_sprite( )>, >, ) { - extracted_glyphs.glyphs.clear(); - extracted_glyphs.sections.clear(); - extracted_glyphs.texts.clear(); + extracted_batches.batches.clear(); + extracted_batches.sprites.clear(); // TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() @@ -110,7 +108,6 @@ pub fn extract_text2d_sprite( for (entity, view_visibility, text, text_layout_info, anchor, global_transform) in text2d_query.iter() { - println!("Extract Text entity: {entity:?}"); if !view_visibility.get() { continue; } @@ -121,8 +118,8 @@ pub fn extract_text2d_sprite( * scaling * GlobalTransform::from_translation(alignment_translation.extend(0.)); let mut current_section = usize::MAX; - let mut section_ids = Vec::with_capacity(text.sections.len()); - let mut count = 0; + let mut glyph_ids = vec![]; + let mut color = Color::WHITE; for PositionedGlyph { position, atlas_info, @@ -131,45 +128,29 @@ pub fn extract_text2d_sprite( } in &text_layout_info.glyphs { if *section_index != current_section { - println!("section: {section_index}, start: {count}"); - let section_id = commands.spawn_empty().id(); - section_ids.push(section_id); - let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); - let extracted_section = ExtractedGlyphSection { - transform, - color: text.sections[*section_index].style.color, - atlas_id: atlas.texture.id(), - range: extracted_glyphs.glyphs.len()..extracted_glyphs.glyphs.len(), - }; - - extracted_glyphs - .sections - .insert(section_id, extracted_section); - + color = text.sections[*section_index].style.color.as_rgba_linear(); current_section = *section_index; } let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); - extracted_glyphs.glyphs.push(ExtractedGlyph { - rect: atlas.textures[atlas_info.glyph_index], - position: *position, - }); - count +=1; - extracted_glyphs - .sections - .get_mut(*section_ids.last().unwrap()) - .unwrap() - .range - .end += 1; + let sprite_id = commands.spawn_empty().id(); + let sprite = ExtractedSprite { + transform: transform * GlobalTransform::from_translation(position.extend(0.)), + color, + rect: Some(atlas.textures[atlas_info.glyph_index]), + custom_size: None, + image_handle_id: atlas.texture.id(), + flip_x: false, + flip_y: false, + anchor: Anchor::Center.as_vec(), + }; + glyph_ids.push(sprite_id); + extracted_batches.sprites.insert(sprite_id, sprite); } - println!("total glyph count: {count}"); - println!("extracted glyphs len {}:", extracted_glyphs.glyphs.len()); - extracted_glyphs.texts.insert( + extracted_batches.batches.insert( entity, - ExtractedGlyphText { - sections: section_ids, - }, + glyph_ids, ); } } From 068098f94371fd285a2ffcc46c9abb5154079ec3 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 6 Sep 2023 00:39:48 +0100 Subject: [PATCH 3/6] cargo fmt --all --- crates/bevy_sprite/src/render/mod.rs | 11 +++++++---- crates/bevy_text/src/text2d.rs | 9 ++------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 555721827594c..787ce63705513 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -689,7 +689,11 @@ pub fn prepare_sprites( for item_index in 0..transparent_phase.items.len() { let item = transparent_phase.items[item_index].entity; let mut batch_image_changed = false; - if let Some(extracted_sprite) = extracted_sprites.sprites.get(item).or_else(|| extracted_batches.sprites.get(item)) { + if let Some(extracted_sprite) = extracted_sprites + .sprites + .get(item) + .or_else(|| extracted_batches.sprites.get(item)) + { if batch_image_handle != extracted_sprite.image_handle_id { batch_image_changed = true; } @@ -795,7 +799,7 @@ pub fn prepare_sprites( // done by invalidating the batch_image_handle batch_image_handle = HandleId::Id(Uuid::nil(), u64::MAX); continue; - }; + }; } } sprite_meta @@ -915,5 +919,4 @@ impl RenderCommand

for DrawSpriteBatch { pass.draw_indexed(0..6, 0, batch.range.clone()); RenderCommandResult::Success } - -} \ No newline at end of file +} diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index bfc02a28249c1..6a476531acf8e 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -17,9 +17,7 @@ use bevy_render::{ view::{InheritedVisibility, ViewVisibility, Visibility}, Extract, }; -use bevy_sprite::{ - Anchor, TextureAtlas, ExtractedSpriteBatches, ExtractedSprite, -}; +use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSpriteBatches, TextureAtlas}; use bevy_transform::prelude::{GlobalTransform, Transform}; use bevy_utils::HashSet; use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged}; @@ -148,10 +146,7 @@ pub fn extract_text2d_sprite( glyph_ids.push(sprite_id); extracted_batches.sprites.insert(sprite_id, sprite); } - extracted_batches.batches.insert( - entity, - glyph_ids, - ); + extracted_batches.batches.insert(entity, glyph_ids); } } From 87c1b1e4b36c0cc350bf729b2ba6d67f1f011aa5 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 6 Sep 2023 01:24:08 +0100 Subject: [PATCH 4/6] cargo fmt, reduced changes --- crates/bevy_sprite/src/render/mod.rs | 210 +++++++++++++-------------- 1 file changed, 104 insertions(+), 106 deletions(-) diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 787ce63705513..d783a2a3771ac 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -687,119 +687,116 @@ pub fn prepare_sprites( // Spawn an entity with a `SpriteBatch` component for each possible batch. // Compatible items share the same entity. for item_index in 0..transparent_phase.items.len() { - let item = transparent_phase.items[item_index].entity; - let mut batch_image_changed = false; - if let Some(extracted_sprite) = extracted_sprites + let item = &transparent_phase.items[item_index]; + let Some(extracted_sprite) = extracted_sprites .sprites - .get(item) - .or_else(|| extracted_batches.sprites.get(item)) - { - if batch_image_handle != extracted_sprite.image_handle_id { - batch_image_changed = true; - } - if batch_image_changed { - let Some(gpu_image) = - gpu_images.get(&Handle::weak(extracted_sprite.image_handle_id)) - else { - continue; - }; - - batch_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y); - batch_image_handle = extracted_sprite.image_handle_id; - image_bind_groups - .values - .entry(Handle::weak(batch_image_handle)) - .or_insert_with(|| { - render_device.create_bind_group(&BindGroupDescriptor { - entries: &[ - BindGroupEntry { - binding: 0, - resource: BindingResource::TextureView( - &gpu_image.texture_view, - ), - }, - BindGroupEntry { - binding: 1, - resource: BindingResource::Sampler(&gpu_image.sampler), - }, - ], - label: Some("sprite_material_bind_group"), - layout: &sprite_pipeline.material_layout, - }) - }); - } - - // By default, the size of the quad is the size of the texture - let mut quad_size = batch_image_size; - - // Calculate vertex data for this item - let mut uv_offset_scale: Vec4; - - // If a rect is specified, adjust UVs and the size of the quad - if let Some(rect) = extracted_sprite.rect { - let rect_size = rect.size(); - uv_offset_scale = Vec4::new( - rect.min.x / batch_image_size.x, - rect.max.y / batch_image_size.y, - rect_size.x / batch_image_size.x, - -rect_size.y / batch_image_size.y, - ); - quad_size = rect_size; - } else { - uv_offset_scale = Vec4::new(0.0, 1.0, 1.0, -1.0); - } - - if extracted_sprite.flip_x { - uv_offset_scale.x += uv_offset_scale.z; - uv_offset_scale.z *= -1.0; - } - if extracted_sprite.flip_y { - uv_offset_scale.y += uv_offset_scale.w; - uv_offset_scale.w *= -1.0; - } - - // Override the size if a custom one is specified - if let Some(custom_size) = extracted_sprite.custom_size { - quad_size = custom_size; - } - let transform = extracted_sprite.transform.affine() - * Affine3A::from_scale_rotation_translation( - quad_size.extend(1.0), - Quat::IDENTITY, - (quad_size * (-extracted_sprite.anchor - Vec2::splat(0.5))).extend(0.0), - ); - - // Store the vertex data and add the item to the render phase - sprite_meta - .sprite_instance_buffer - .push(SpriteInstance::from( - &transform, - &extracted_sprite.color, - &uv_offset_scale, - )); - - if batch_image_changed { - batch_item_index = item_index; - - batches.push(( - item, - SpriteBatch { - image_handle_id: batch_image_handle, - range: index..index, - }, - )); - } - - transparent_phase.items[batch_item_index].batch_size += 1; - batches.last_mut().unwrap().1.range.end += 1; - index += 1; - } else { + .get(item.entity) + .or_else(|| extracted_batches.sprites.get(item.entity)) + else { // If there is a phase item that is not a sprite, then we must start a new // batch to draw the other phase item(s) and to respect draw order. This can be // done by invalidating the batch_image_handle batch_image_handle = HandleId::Id(Uuid::nil(), u64::MAX); continue; }; + + let batch_image_changed = batch_image_handle != extracted_sprite.image_handle_id; + if batch_image_changed { + let Some(gpu_image) = + gpu_images.get(&Handle::weak(extracted_sprite.image_handle_id)) + else { + continue; + }; + + batch_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y); + batch_image_handle = extracted_sprite.image_handle_id; + image_bind_groups + .values + .entry(Handle::weak(batch_image_handle)) + .or_insert_with(|| { + render_device.create_bind_group(&BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView( + &gpu_image.texture_view, + ), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&gpu_image.sampler), + }, + ], + label: Some("sprite_material_bind_group"), + layout: &sprite_pipeline.material_layout, + }) + }); + } + + // By default, the size of the quad is the size of the texture + let mut quad_size = batch_image_size; + + // Calculate vertex data for this item + let mut uv_offset_scale: Vec4; + + // If a rect is specified, adjust UVs and the size of the quad + if let Some(rect) = extracted_sprite.rect { + let rect_size = rect.size(); + uv_offset_scale = Vec4::new( + rect.min.x / batch_image_size.x, + rect.max.y / batch_image_size.y, + rect_size.x / batch_image_size.x, + -rect_size.y / batch_image_size.y, + ); + quad_size = rect_size; + } else { + uv_offset_scale = Vec4::new(0.0, 1.0, 1.0, -1.0); + } + + if extracted_sprite.flip_x { + uv_offset_scale.x += uv_offset_scale.z; + uv_offset_scale.z *= -1.0; + } + if extracted_sprite.flip_y { + uv_offset_scale.y += uv_offset_scale.w; + uv_offset_scale.w *= -1.0; + } + + // Override the size if a custom one is specified + if let Some(custom_size) = extracted_sprite.custom_size { + quad_size = custom_size; + } + let transform = extracted_sprite.transform.affine() + * Affine3A::from_scale_rotation_translation( + quad_size.extend(1.0), + Quat::IDENTITY, + (quad_size * (-extracted_sprite.anchor - Vec2::splat(0.5))).extend(0.0), + ); + + // Store the vertex data and add the item to the render phase + sprite_meta + .sprite_instance_buffer + .push(SpriteInstance::from( + &transform, + &extracted_sprite.color, + &uv_offset_scale, + )); + + if batch_image_changed { + batch_item_index = item_index; + + batches.push(( + item.entity, + SpriteBatch { + image_handle_id: batch_image_handle, + range: index..index, + }, + )); + } + + transparent_phase.items[batch_item_index].batch_size += 1; + batches.last_mut().unwrap().1.range.end += 1; + index += 1; } } sprite_meta @@ -829,6 +826,7 @@ pub fn prepare_sprites( .sprite_index_buffer .write_buffer(&render_device, &render_queue); } + *previous_len = batches.len(); commands.insert_or_spawn_batch(batches); } From 0b76079e4b92974cafd0c4a2e3f98b0f7ebf26cd Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 8 Sep 2023 13:21:24 +0100 Subject: [PATCH 5/6] Batches now hold a range into a vec of entities, instead of a separate vec for each batch. --- crates/bevy_sprite/src/render/mod.rs | 20 ++++++++++++++++++-- crates/bevy_text/src/text2d.rs | 11 +++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index d783a2a3771ac..64e7193182d8d 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -328,15 +328,31 @@ pub struct ExtractedSprite { pub anchor: Vec2, } + #[derive(Resource, Default)] pub struct ExtractedSprites { pub sprites: SparseSet, } +/// Allows extraction of multiply sprites for a single entity that all share that entity's id for visibility resolution. +/// Not designed for efficiency, extracting sprite entities individually should be more performant. #[derive(Resource, Default)] pub struct ExtractedSpriteBatches { - pub batches: SparseSet>, + /// maps the entity id for each batch to a range of indices into `sprite_ids` + pub batches: SparseSet>, + /// set of all the extracted sprites from every batch pub sprites: SparseSet, + /// ids of empty entities used to identify the individual sprites in each batch + pub sprite_ids: Vec, +} + +impl ExtractedSpriteBatches { + /// Clear all batches + pub fn clear(&mut self) { + self.batches.clear(); + self.sprites.clear(); + self.sprite_ids.clear(); + } } #[derive(Resource, Default)] @@ -602,7 +618,7 @@ pub fn queue_sprites( if !view_entities.contains(batch_entity.index() as usize) { continue; } - for sprite_entity in sprite_entities.iter() { + for sprite_entity in &extracted_batches.sprite_ids[sprite_entities.clone()] { let extracted_sprite = extracted_batches.sprites.get(*sprite_entity).unwrap(); // These items will be sorted by depth with other phase items let sort_key = FloatOrd(extracted_sprite.transform.translation().z); diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 6a476531acf8e..3d49da31d83d1 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -94,8 +94,7 @@ pub fn extract_text2d_sprite( )>, >, ) { - extracted_batches.batches.clear(); - extracted_batches.sprites.clear(); + extracted_batches.clear(); // TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() @@ -109,14 +108,13 @@ pub fn extract_text2d_sprite( if !view_visibility.get() { continue; } - + let start = extracted_batches.sprite_ids.len(); let text_anchor = -(anchor.as_vec() + 0.5); let alignment_translation = text_layout_info.size * text_anchor; let transform = *global_transform * scaling * GlobalTransform::from_translation(alignment_translation.extend(0.)); let mut current_section = usize::MAX; - let mut glyph_ids = vec![]; let mut color = Color::WHITE; for PositionedGlyph { position, @@ -143,10 +141,11 @@ pub fn extract_text2d_sprite( flip_y: false, anchor: Anchor::Center.as_vec(), }; - glyph_ids.push(sprite_id); + extracted_batches.sprite_ids.push(sprite_id); extracted_batches.sprites.insert(sprite_id, sprite); } - extracted_batches.batches.insert(entity, glyph_ids); + let indices = start..extracted_batches.sprite_ids.len(); + extracted_batches.batches.insert(entity, indices); } } From 033d1c5813f8864f92c5a1c0dea6c391741aca15 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 8 Sep 2023 13:24:09 +0100 Subject: [PATCH 6/6] cargo fmt --- crates/bevy_sprite/src/render/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 64e7193182d8d..a546d47a9e9e4 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -328,14 +328,13 @@ pub struct ExtractedSprite { pub anchor: Vec2, } - #[derive(Resource, Default)] pub struct ExtractedSprites { pub sprites: SparseSet, } /// Allows extraction of multiply sprites for a single entity that all share that entity's id for visibility resolution. -/// Not designed for efficiency, extracting sprite entities individually should be more performant. +/// Not designed for efficiency, extracting sprite entities individually should be more performant. #[derive(Resource, Default)] pub struct ExtractedSpriteBatches { /// maps the entity id for each batch to a range of indices into `sprite_ids`