Skip to content

Commit

Permalink
cleaner code and improvement performances
Browse files Browse the repository at this point in the history
  • Loading branch information
ManevilleF committed Jul 9, 2022
1 parent 304798d commit 0a0d5c4
Showing 1 changed file with 124 additions and 111 deletions.
235 changes: 124 additions & 111 deletions crates/bevy_sprite/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,11 @@ impl SpecializedRenderPipeline for SpritePipeline {
}
}

/// Render world sprite data
pub struct ExtractedSprite {
/// Sprite global translation, rotation and scale
pub transform: GlobalTransform,
/// Sprite color tint
pub color: Color,
/// Select an area of the texture
pub rect: Option<Rect>,
Expand All @@ -181,14 +184,21 @@ pub struct ExtractedSprite {
/// Handle to the `Image` of this sprite
/// PERF: storing a `HandleId` instead of `Handle<Image>` enables some optimizations (`ExtractedSprite` becomes `Copy` and doesn't need to be dropped)
pub image_handle_id: HandleId,
/// Is the texture flipped horizontally
pub flip_x: bool,
/// Is the texture flipped vertically
pub flip_y: bool,
/// Sprite anchor offset
pub anchor: Vec2,
}

/// Container for all [`ExtractedSprite`]
#[derive(Default)]
pub struct ExtractedSprites {
/// White extracted sprites
pub sprites: Vec<ExtractedSprite>,
/// Color tinted extracted sprites
pub colored_sprites: Vec<ExtractedSprite>,
}

#[derive(Default)]
Expand Down Expand Up @@ -232,12 +242,14 @@ pub fn extract_sprites(
)>,
>,
) {
extracted_sprites.sprites.clear();
extracted_sprites.colored_sprites.clear();
for (visibility, sprite, transform, handle) in sprite_query.iter() {
if !visibility.is_visible {
continue;
}
// 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.alloc().init(ExtractedSprite {
let sprite = ExtractedSprite {
color: sprite.color,
transform: *transform,
// Use the full texture
Expand All @@ -248,15 +260,20 @@ pub fn extract_sprites(
flip_y: sprite.flip_y,
image_handle_id: handle.id,
anchor: sprite.anchor.as_vec(),
});
};
if sprite.color == Color::WHITE {
extracted_sprites.sprites.alloc().init(sprite);
} else {
extracted_sprites.colored_sprites.alloc().init(sprite);
}
}
for (visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
if !visibility.is_visible {
continue;
}
if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) {
let rect = Some(texture_atlas.textures[atlas_sprite.index as usize]);
extracted_sprites.sprites.alloc().init(ExtractedSprite {
let sprite = ExtractedSprite {
color: atlas_sprite.color,
transform: *transform,
// Select the area in the texture atlas
Expand All @@ -267,7 +284,12 @@ pub fn extract_sprites(
flip_y: atlas_sprite.flip_y,
image_handle_id: texture_atlas.texture.id,
anchor: atlas_sprite.anchor.as_vec(),
});
};
if sprite.color == Color::WHITE {
extracted_sprites.sprites.alloc().init(sprite);
} else {
extracted_sprites.colored_sprites.alloc().init(sprite);
}
}
}
}
Expand Down Expand Up @@ -370,123 +392,114 @@ pub fn prepare_sprites(
sprite_meta.colored_vertices.clear();

// Sort sprites by z for correct transparency and then by handle to improve batching
extracted_sprites.sprites.sort_unstable_by(|a, b| {
match a
.transform
.translation
.z
.partial_cmp(&b.transform.translation.z)
{
Some(Ordering::Equal) | None => a.image_handle_id.cmp(&b.image_handle_id),
Some(other) => other,
}
});

// Impossible starting values that will be replaced on the first iteration
let mut current_batch_handle = HandleId::Id(Uuid::nil(), u64::MAX);
let mut current_image_size = Vec2::ZERO;
let mut current_batch_colored = false;
let mut z_order = 0.0;

// Vertex buffer indices
let [mut white_start, mut white_end] = [0, 0];
let [mut colored_start, mut colored_end] = [0, 0];

for extracted_sprite in extracted_sprites.sprites.drain(..) {
let colored = extracted_sprite.color != Color::WHITE;
if extracted_sprite.image_handle_id != current_batch_handle
|| colored != current_batch_colored
{
if let Some(gpu_image) = gpu_images.get(&Handle::weak(extracted_sprite.image_handle_id))
{
current_image_size = gpu_image.size;
current_batch_handle = extracted_sprite.image_handle_id;
current_batch_colored = colored;
let [start, end] = match colored {
true => [&mut colored_start, &mut colored_end],
false => [&mut white_start, &mut white_end],
};
if *start != *end {
commands.spawn().insert(SpriteBatch {
range: *start..*end,
image_handle_id: current_batch_handle,
colored: current_batch_colored,
z_order,
});
*start = *end;
let sort = |a: &ExtractedSprite, b: &ExtractedSprite| match a
.transform
.translation
.z
.partial_cmp(&b.transform.translation.z)
{
Some(Ordering::Equal) | None => a.image_handle_id.cmp(&b.image_handle_id),
Some(other) => other,
};
extracted_sprites.sprites.sort_unstable_by(sort);
extracted_sprites.colored_sprites.sort_unstable_by(sort);

for (colored, sprites) in [
(false, &extracted_sprites.sprites),
(true, &extracted_sprites.colored_sprites),
] {
// Impossible starting values that will be replaced on the first iteration
let mut current_batch_handle = HandleId::Id(Uuid::nil(), u64::MAX);
let mut current_image_size = Vec2::ZERO;
let mut z_order = 0.0;

let mut start = 0;
let mut end = 0;

for extracted_sprite in sprites {
if extracted_sprite.image_handle_id != current_batch_handle {
if let Some(gpu_image) =
gpu_images.get(&Handle::weak(extracted_sprite.image_handle_id))
{
current_image_size = gpu_image.size;
current_batch_handle = extracted_sprite.image_handle_id;
if start != end {
commands.spawn().insert(SpriteBatch {
range: start..end,
image_handle_id: current_batch_handle,
colored,
z_order,
});
start = end;
}
} else {
// We skip loading images
continue;
}
} else {
// We skip loading images
continue;
}
}
// Calculate vertex data for this item
let mut uvs = QUAD_UVS;
if extracted_sprite.flip_x {
uvs = [uvs[1], uvs[0], uvs[3], uvs[2]];
}
if extracted_sprite.flip_y {
uvs = [uvs[3], uvs[2], uvs[1], uvs[0]];
}
// Calculate vertex data for this item
let mut uvs = QUAD_UVS;
if extracted_sprite.flip_x {
uvs = [uvs[1], uvs[0], uvs[3], uvs[2]];
}
if extracted_sprite.flip_y {
uvs = [uvs[3], uvs[2], uvs[1], uvs[0]];
}

// By default, the size of the quad is the size of the texture
let mut quad_size = current_image_size;
// By default, the size of the quad is the size of the texture
let mut quad_size = current_image_size;

// 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();
for uv in &mut uvs {
*uv = (rect.min + *uv * rect_size) / current_image_size;
// 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();
for uv in &mut uvs {
*uv = (rect.min + *uv * rect_size) / current_image_size;
}
quad_size = rect_size;
}
quad_size = rect_size;
}

// Override the size if a custom one is specified
if let Some(custom_size) = extracted_sprite.custom_size {
quad_size = custom_size;
}

// Apply size and global transform
let positions = QUAD_VERTEX_POSITIONS.map(|quad_pos| {
extracted_sprite
.transform
.mul_vec3(((quad_pos - extracted_sprite.anchor) * quad_size).extend(0.))
.into()
});
if colored {
for i in QUAD_INDICES {
sprite_meta.colored_vertices.push(ColoredSpriteVertex {
position: positions[i],
uv: uvs[i].into(),
color: extracted_sprite.color.as_linear_rgba_f32(),
});
// Override the size if a custom one is specified
if let Some(custom_size) = extracted_sprite.custom_size {
quad_size = custom_size;
}
colored_end += QUAD_INDICES.len() as u32;
} else {
for i in QUAD_INDICES {
sprite_meta.vertices.push(SpriteVertex {
position: positions[i],
uv: uvs[i].into(),
});

// Apply size and global transform
let positions = QUAD_VERTEX_POSITIONS.map(|quad_pos| {
extracted_sprite
.transform
.mul_vec3(((quad_pos - extracted_sprite.anchor) * quad_size).extend(0.))
.into()
});
if colored {
for i in QUAD_INDICES {
sprite_meta.colored_vertices.push(ColoredSpriteVertex {
position: positions[i],
uv: uvs[i].into(),
color: extracted_sprite.color.as_linear_rgba_f32(),
});
}
} else {
for i in QUAD_INDICES {
sprite_meta.vertices.push(SpriteVertex {
position: positions[i],
uv: uvs[i].into(),
});
}
}
white_end += QUAD_INDICES.len() as u32;
end += QUAD_INDICES.len() as u32;
z_order = extracted_sprite.transform.translation.z;
}
z_order = extracted_sprite.transform.translation.z;
}
// if start != end, there is one last batch to process
let [start, end] = match current_batch_colored {
true => [&mut colored_start, &mut colored_end],
false => [&mut white_start, &mut white_end],
};
if *start != *end {
commands.spawn().insert(SpriteBatch {
range: *start..*end,
image_handle_id: current_batch_handle,
colored: current_batch_colored,
z_order,
});
}

// if start != end, there is one last batch to process
if start != end {
commands.spawn().insert(SpriteBatch {
range: start..end,
image_handle_id: current_batch_handle,
colored,
z_order,
});
}
}
sprite_meta
.vertices
.write_buffer(&render_device, &render_queue);
Expand Down

0 comments on commit 0a0d5c4

Please sign in to comment.