Skip to content

Commit

Permalink
refactor: simplify render by culling and profiling in extract phase
Browse files Browse the repository at this point in the history
  • Loading branch information
simbleau committed Feb 17, 2025
1 parent 3a7ed45 commit b9a37a4
Show file tree
Hide file tree
Showing 10 changed files with 388 additions and 275 deletions.
7 changes: 5 additions & 2 deletions src/integrations/lottie/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::render::VelatoRenderer;
use crate::render::{extract::VelloExtractStep, VelatoRenderer};

use super::{
asset::VelloLottieHandle, asset_loader::VelloLottieLoader, render, systems, VelloLottie,
Expand Down Expand Up @@ -34,7 +34,10 @@ impl Plugin for LottieIntegrationPlugin {

render_app
.init_resource::<VelatoRenderer>()
.add_systems(ExtractSchedule, render::extract_lottie_assets)
.add_systems(
ExtractSchedule,
render::extract_lottie_assets.in_set(VelloExtractStep::ExtractAssets),
)
.add_systems(
Render,
(render::prepare_asset_affines).in_set(RenderSet::Prepare),
Expand Down
65 changes: 40 additions & 25 deletions src/integrations/lottie/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use super::{
Playhead, Theme, VelloLottieAnchor,
};
use crate::{
render::prepare::{PrepareRenderInstance, PreparedAffine, PreparedTransform},
render::{
prepare::{PrepareRenderInstance, PreparedAffine, PreparedTransform},
VelloFrameData, VelloView,
},
CoordinateSpace, SkipEncoding,
};
use bevy::{
Expand All @@ -24,14 +27,17 @@ pub struct ExtractedLottieAsset {
pub transform: GlobalTransform,
pub render_mode: CoordinateSpace,
pub ui_node: Option<ComputedNode>,
pub render_layers: Option<RenderLayers>,
pub alpha: f32,
pub theme: Option<Theme>,
pub playhead: f64,
}

pub fn extract_lottie_assets(
mut commands: Commands,
query_views: Query<
(&ExtractedCamera, Option<&RenderLayers>),
(With<Camera2d>, With<VelloView>),
>,
query_vectors: Extract<
Query<
(
Expand All @@ -50,7 +56,15 @@ pub fn extract_lottie_assets(
>,
>,
assets: Extract<Res<Assets<VelloLottie>>>,
mut frame_data: ResMut<VelloFrameData>,
) {
let mut n_lotties = 0;

// Respect camera ordering
let mut views: Vec<(&ExtractedCamera, Option<&RenderLayers>)> =
query_views.into_iter().collect();
views.sort_by(|(camera_a, _), (camera_b, _)| camera_a.order.cmp(&camera_b.order));

for (
asset,
asset_anchor,
Expand All @@ -66,40 +80,41 @@ pub fn extract_lottie_assets(
{
if let Some(asset) = assets.get(asset.id()) {
if view_visibility.get() && inherited_visibility.get() {
let playhead = playhead.frame();
commands
.spawn(ExtractedLottieAsset {
asset: asset.to_owned(),
transform: *transform,
asset_anchor: *asset_anchor,
theme: theme.cloned(),
render_mode: *coord_space,
playhead,
alpha: asset.alpha,
ui_node: ui_node.cloned(),
render_layers: render_layers.cloned(),
})
.insert(TemporaryRenderEntity);
let svg_render_layers = render_layers.unwrap_or_default();
for (_, camera_render_layers) in views.iter() {
if svg_render_layers.intersects(camera_render_layers.unwrap_or_default()) {
let playhead = playhead.frame();
commands
.spawn(ExtractedLottieAsset {
asset: asset.to_owned(),
transform: *transform,
asset_anchor: *asset_anchor,
theme: theme.cloned(),
render_mode: *coord_space,
playhead,
alpha: asset.alpha,
ui_node: ui_node.cloned(),
})
.insert(TemporaryRenderEntity);
n_lotties += 1;
break;
}
}
}
}
}

frame_data.n_lotties = n_lotties;
}

pub fn prepare_asset_affines(
mut commands: Commands,
views: Query<(&ExtractedCamera, &ExtractedView, Option<&RenderLayers>), With<Camera2d>>,
views: Query<(&ExtractedCamera, &ExtractedView), With<Camera2d>>,
mut render_entities: Query<(Entity, &ExtractedLottieAsset)>,
) {
for (camera, view, maybe_camera_layers) in views.iter() {
let camera_render_layers = maybe_camera_layers.unwrap_or_default();
for (camera, view) in views.iter() {
let viewport_size: UVec2 = camera.physical_viewport_size.unwrap();
for (entity, render_entity) in render_entities.iter_mut() {
let maybe_entity_layers = render_entity.render_layers.clone();
let entity_render_layers = maybe_entity_layers.unwrap_or_default();
if !camera_render_layers.intersects(&entity_render_layers) {
continue;
}

// Prepare render data needed for the subsequent render system
let final_transform = render_entity.final_transform();
let affine = render_entity.scene_affine(view, *final_transform, viewport_size);
Expand Down
6 changes: 5 additions & 1 deletion src/integrations/svg/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{asset::VelloSvgHandle, asset_loader::VelloSvgLoader, render, VelloSvg};
use crate::render::extract::VelloExtractStep;
use bevy::{
prelude::*,
render::{
Expand All @@ -23,7 +24,10 @@ impl Plugin for SvgIntegrationPlugin {
};

render_app
.add_systems(ExtractSchedule, render::extract_svg_assets)
.add_systems(
ExtractSchedule,
render::extract_svg_assets.in_set(VelloExtractStep::ExtractAssets),
)
.add_systems(
Render,
(render::prepare_asset_affines,).in_set(RenderSet::Prepare),
Expand Down
66 changes: 40 additions & 26 deletions src/integrations/svg/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use super::{
};
use crate::{
prelude::*,
render::prepare::{PrepareRenderInstance, PreparedAffine, PreparedTransform},
render::{
prepare::{PrepareRenderInstance, PreparedAffine, PreparedTransform},
VelloFrameData,
},
};
use bevy::{
prelude::*,
Expand All @@ -18,18 +21,21 @@ use bevy::{
use kurbo::Affine;

#[derive(Component, Clone)]
pub struct ExtractedSvgAsset {
pub struct ExtractedVelloSvg {
pub asset: VelloSvg,
pub asset_anchor: VelloSvgAnchor,
pub transform: GlobalTransform,
pub render_mode: CoordinateSpace,
pub ui_node: Option<ComputedNode>,
pub render_layers: Option<RenderLayers>,
pub alpha: f32,
}

pub fn extract_svg_assets(
mut commands: Commands,
query_views: Query<
(&ExtractedCamera, Option<&RenderLayers>),
(With<Camera2d>, With<VelloView>),
>,
query_vectors: Extract<
Query<
(
Expand All @@ -46,7 +52,15 @@ pub fn extract_svg_assets(
>,
>,
assets: Extract<Res<Assets<VelloSvg>>>,
mut frame_data: ResMut<VelloFrameData>,
) {
let mut n_svgs = 0;

// Respect camera ordering
let mut views: Vec<(&ExtractedCamera, Option<&RenderLayers>)> =
query_views.into_iter().collect();
views.sort_by(|(camera_a, _), (camera_b, _)| camera_a.order.cmp(&camera_b.order));

for (
asset,
asset_anchor,
Expand All @@ -60,47 +74,47 @@ pub fn extract_svg_assets(
{
if let Some(asset) = assets.get(asset.id()) {
if view_visibility.get() && inherited_visibility.get() {
commands
.spawn(ExtractedSvgAsset {
asset: asset.to_owned(),
transform: *transform,
asset_anchor: *asset_anchor,
render_mode: *coord_space,
ui_node: ui_node.cloned(),
render_layers: render_layers.cloned(),
alpha: asset.alpha,
})
.insert(TemporaryRenderEntity);
let svg_render_layers = render_layers.unwrap_or_default();
for (_, camera_render_layers) in views.iter() {
if svg_render_layers.intersects(camera_render_layers.unwrap_or_default()) {
commands
.spawn(ExtractedVelloSvg {
asset: asset.to_owned(),
transform: *transform,
asset_anchor: *asset_anchor,
render_mode: *coord_space,
ui_node: ui_node.cloned(),
alpha: asset.alpha,
})
.insert(TemporaryRenderEntity);
n_svgs += 1;
break;
}
}
}
}
}

frame_data.n_svgs = n_svgs;
}

pub fn prepare_asset_affines(
mut commands: Commands,
views: Query<(&ExtractedCamera, &ExtractedView, Option<&RenderLayers>), With<Camera2d>>,
mut render_entities: Query<(Entity, &ExtractedSvgAsset)>,
views: Query<(&ExtractedCamera, &ExtractedView), With<Camera2d>>,
mut render_entities: Query<(Entity, &ExtractedVelloSvg)>,
) {
for (camera, view, maybe_camera_layers) in views.iter() {
let camera_render_layers = maybe_camera_layers.unwrap_or_default();
for (camera, view) in views.iter() {
let viewport_size: UVec2 = camera.physical_viewport_size.unwrap();
for (entity, render_entity) in render_entities.iter_mut() {
let maybe_entity_layers = render_entity.render_layers.clone();
let entity_render_layers = maybe_entity_layers.unwrap_or_default();
if !camera_render_layers.intersects(&entity_render_layers) {
continue;
}

// Prepare render data needed for the subsequent render system
let final_transform = render_entity.final_transform();
let affine = render_entity.scene_affine(view, *final_transform, viewport_size);

commands.entity(entity).insert((affine, final_transform));
}
}
}

impl PrepareRenderInstance for ExtractedSvgAsset {
impl PrepareRenderInstance for ExtractedVelloSvg {
fn final_transform(&self) -> PreparedTransform {
PreparedTransform(self.asset_anchor.compute(
self.asset.width,
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub struct VelloTextBundle {
/// A simple newtype component wrapper for [`vello::Scene`] for rendering.
#[derive(Component, Default, Clone, Deref, DerefMut)]
#[require(CoordinateSpace, Transform, Visibility)]
pub struct VelloScene(vello::Scene);
pub struct VelloScene(Box<vello::Scene>);

impl VelloScene {
pub fn new() -> Self {
Expand All @@ -89,6 +89,6 @@ impl VelloScene {

impl From<vello::Scene> for VelloScene {
fn from(scene: vello::Scene) -> Self {
Self(scene)
Self(Box::new(scene))
}
}
Loading

0 comments on commit b9a37a4

Please sign in to comment.