Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ExtractedSprites slice buffer #17041

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 148 additions & 71 deletions crates/bevy_sprite/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use bevy_ecs::{
};
use bevy_image::{BevyDefault, Image, ImageSampler, TextureAtlasLayout, TextureFormatPixelInfo};
use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4};
use bevy_render::sync_world::MainEntity;
use bevy_render::sync_world::MainEntityHashMap;
use bevy_render::view::RenderVisibleEntities;
use bevy_render::{
render_asset::RenderAssets,
Expand All @@ -30,7 +30,7 @@ use bevy_render::{
*,
},
renderer::{RenderDevice, RenderQueue},
sync_world::{RenderEntity, TemporaryRenderEntity},
sync_world::RenderEntity,
texture::{DefaultImageSampler, FallbackImage, GpuImage},
view::{
ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms,
Expand Down Expand Up @@ -323,6 +323,12 @@ impl SpecializedRenderPipeline for SpritePipeline {
}
}

pub struct ExtractedSlice {
pub offset: Vec2,
pub rect: Rect,
pub size: Vec2,
}

pub struct ExtractedSprite {
pub transform: GlobalTransform,
pub color: LinearRgba,
Expand All @@ -339,11 +345,14 @@ pub struct ExtractedSprite {
/// For cases where additional [`ExtractedSprites`] are created during extraction, this stores the
/// entity that caused that creation for use in determining visibility.
pub original_entity: Option<Entity>,
pub render_entity: Entity,
pub slice_indices: Range<usize>,
}

#[derive(Resource, Default)]
pub struct ExtractedSprites {
pub sprites: HashMap<(Entity, MainEntity), ExtractedSprite>,
pub sprites: MainEntityHashMap<ExtractedSprite>,
pub slices: Vec<ExtractedSlice>,
}

#[derive(Resource, Default)]
Expand All @@ -364,7 +373,6 @@ pub fn extract_sprite_events(
}

pub fn extract_sprites(
mut commands: Commands,
mut extracted_sprites: ResMut<ExtractedSprites>,
texture_atlases: Extract<Res<Assets<TextureAtlasLayout>>>,
sprite_query: Extract<
Expand All @@ -379,25 +387,37 @@ pub fn extract_sprites(
>,
) {
extracted_sprites.sprites.clear();
for (original_entity, entity, view_visibility, sprite, transform, slices) in sprite_query.iter()
extracted_sprites.slices.clear();
for (original_entity, render_entity, view_visibility, sprite, transform, slices) in
sprite_query.iter()
{
if !view_visibility.get() {
continue;
}

if let Some(slices) = slices {
extracted_sprites.sprites.extend(
slices
.extract_sprites(transform, original_entity, sprite)
.map(|e| {
(
(
commands.spawn(TemporaryRenderEntity).id(),
original_entity.into(),
),
e,
)
}),
let start = extracted_sprites.slices.len();

extracted_sprites
.slices
.extend(slices.extract_slices(sprite));
let end = extracted_sprites.slices.len();
extracted_sprites.sprites.insert(
original_entity.into(),
ExtractedSprite {
color: sprite.color.into(),
transform: *transform,
rect: None,
// Pass the custom size
custom_size: sprite.custom_size,
flip_x: sprite.flip_x,
flip_y: sprite.flip_y,
image_handle_id: sprite.image.id(),
anchor: sprite.anchor.as_vec(),
original_entity: Some(original_entity),
render_entity,
slice_indices: start..end,
},
);
} else {
let atlas_rect = sprite
Expand All @@ -418,7 +438,7 @@ pub fn extract_sprites(

// PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive
extracted_sprites.sprites.insert(
(entity, original_entity.into()),
original_entity.into(),
ExtractedSprite {
color: sprite.color.into(),
transform: *transform,
Expand All @@ -430,6 +450,8 @@ pub fn extract_sprites(
image_handle_id: sprite.image.id(),
anchor: sprite.anchor.as_vec(),
original_entity: Some(original_entity),
render_entity,
slice_indices: 0..0,
},
);
}
Expand Down Expand Up @@ -555,8 +577,11 @@ pub fn queue_sprites(
.items
.reserve(extracted_sprites.sprites.len());

for ((entity, main_entity), extracted_sprite) in extracted_sprites.sprites.iter() {
let index = extracted_sprite.original_entity.unwrap_or(*entity).index();
for (main_entity, extracted_sprite) in extracted_sprites.sprites.iter() {
let index = extracted_sprite
.original_entity
.unwrap_or(extracted_sprite.render_entity)
.index();

if !view_entities.contains(index as usize) {
continue;
Expand All @@ -569,7 +594,7 @@ pub fn queue_sprites(
transparent_phase.add(Transparent2d {
draw_function: draw_sprite_function,
pipeline,
entity: (*entity, *main_entity),
entity: (extracted_sprite.render_entity, *main_entity),
sort_key,
// batch_range and dynamic_offset will be calculated in prepare_sprites
batch_range: 0..0,
Expand Down Expand Up @@ -657,7 +682,8 @@ pub fn prepare_sprite_image_bind_groups(
// Compatible items share the same entity.
for item_index in 0..transparent_phase.items.len() {
let item = &transparent_phase.items[item_index];
let Some(extracted_sprite) = extracted_sprites.sprites.get(&item.entity) else {
let item_entity = item.entity;
let Some(extracted_sprite) = extracted_sprites.sprites.get(&item_entity.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
Expand Down Expand Up @@ -687,69 +713,120 @@ pub fn prepare_sprite_image_bind_groups(
});

batch_item_index = item_index;

batches.push((
item.entity(),
item_entity.0,
SpriteBatch {
image_handle_id: batch_image_handle,
range: index..index,
},
));
}

// 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.slice_indices.is_empty() {
// 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;
}
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,
));
// 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,
));

batches.last_mut().unwrap().1.range.end += 1;
index += 1;
} else {
for i in extracted_sprite.slice_indices.clone() {
let sprite = &extracted_sprites.slices[i];
let rect = sprite.rect;

// 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

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,
);

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;
}

let transform = extracted_sprite.transform.affine()
* Affine3A::from_scale_rotation_translation(
sprite.size.extend(1.0),
Quat::IDENTITY,
(sprite.size * (-extracted_sprite.anchor - Vec2::splat(0.5))
+ sprite.offset)
.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,
));

batches.last_mut().unwrap().1.range.end += 1;
index += 1;
}
}
transparent_phase.items[batch_item_index]
.batch_range_mut()
.end += 1;
batches.last_mut().unwrap().1.range.end += 1;
index += 1;
}
}
sprite_meta
Expand Down
Loading
Loading