Skip to content

Commit

Permalink
Retained Gizmos (#15473)
Browse files Browse the repository at this point in the history
# Objective
Add a way to use the gizmo API in a retained manner, for increased
performance.

## Solution
- Move gizmo API from `Gizmos` to `GizmoBuffer`, ~ab~using `Deref` to
keep usage the same as before.
- Merge non-strip and strip variant of `LineGizmo` into one, storing the
data in a `GizmoBuffer` to have the same API for retained `LineGizmo`s.

### Review guide
- The meat of the changes are in `lib.rs`, `retained.rs`, `gizmos.rs`,
`pipeline_3d.rs` and `pipeline_2d.rs`
- The other files contain almost exclusively the churn from moving the
gizmo API from `Gizmos` to `GizmoBuffer`

## Testing
### Performance

Performance compared to the immediate mode API is from 65 to 80 times
better for static lines.

```
7900 XTX, 3700X
1707.9k lines/ms: gizmos_retained (21.3ms)
3488.5k lines/ms: gizmos_retained_continuous_polyline (31.3ms)
   0.5k lines/ms: gizmos_retained_separate (97.7ms)

3054.9k lines/ms: bevy_polyline_retained_nan (16.8ms)
3596.3k lines/ms: bevy_polyline_retained_continuous_polyline (14.2ms)
   0.6k lines/ms: bevy_polyline_retained_separate (78.9ms)

  26.9k lines/ms: gizmos_immediate (14.9ms)
  43.8k lines/ms: gizmos_immediate_continuous_polyline (18.3ms)
```
Looks like performance is good enough, being close to par with
`bevy_polyline`.

Benchmarks can be found here: 
This branch:
https://github.com/tim-blackbird/line_racing/tree/retained-gizmos
Bevy 0.14: https://github.com/DGriffin91/line_racing

## Showcase
```rust 
fn setup(
    mut commands: Commands,
    mut gizmo_assets: ResMut<Assets<GizmoAsset>>
) {
    let mut gizmo = GizmoAsset::default();

    // A sphere made out of one million lines!
    gizmo
        .sphere(default(), 1., CRIMSON)
        .resolution(1_000_000 / 3);

    commands.spawn(Gizmo {
        handle: gizmo_assets.add(gizmo),
        ..default()
    });
}
```

## Follow-up work
- Port over to the retained rendering world proper
- Calculate visibility and cull `Gizmo`s
  • Loading branch information
tim-blackbird authored Dec 4, 2024
1 parent f59ae0f commit d2a07f9
Show file tree
Hide file tree
Showing 22 changed files with 840 additions and 462 deletions.
6 changes: 3 additions & 3 deletions crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ impl<'w, 's> InsetGizmo<'w, 's> {
Vec2::new(right, bottom),
Vec2::new(right, top),
Vec2::new(left, top),
];
self.draw
.linestrip_2d(strip.map(|v| self.relative(v)), color);
]
.map(|v| self.relative(v));
self.draw.linestrip_2d(strip, color);
}
}
}
2 changes: 1 addition & 1 deletion crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ fn outline_roots(
let line_width = outline
.gizmo_config
.get_config_dyn(&UiGizmosDebug.type_id())
.map_or(2., |(config, _)| config.line_width)
.map_or(2., |(config, _)| config.line.width)
/ window_scale;
let mut draw = InsetGizmo::new(draw, cam.debug_camera, line_width);
for (entity, trans, node, view_visibility, maybe_target_camera) in &roots {
Expand Down
49 changes: 23 additions & 26 deletions crates/bevy_gizmos/src/arcs.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
//! Additional [`Gizmos`] Functions -- Arcs
//! Additional [`GizmoBuffer`] Functions -- Arcs
//!
//! Includes the implementation of [`Gizmos::arc_2d`],
//! Includes the implementation of [`GizmoBuffer::arc_2d`],
//! and assorted support items.
use crate::{
circles::DEFAULT_CIRCLE_RESOLUTION,
prelude::{GizmoConfigGroup, Gizmos},
};
use crate::{circles::DEFAULT_CIRCLE_RESOLUTION, gizmos::GizmoBuffer, prelude::GizmoConfigGroup};
use bevy_color::Color;
use bevy_math::{Isometry2d, Isometry3d, Quat, Rot2, Vec2, Vec3};
use core::f32::consts::{FRAC_PI_2, TAU};

// === 2D ===

impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand Down Expand Up @@ -54,7 +51,7 @@ where
arc_angle: f32,
radius: f32,
color: impl Into<Color>,
) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> {
) -> Arc2dBuilder<'_, Config, Clear> {
Arc2dBuilder {
gizmos: self,
isometry: isometry.into(),
Expand All @@ -66,21 +63,21 @@ where
}
}

/// A builder returned by [`Gizmos::arc_2d`].
pub struct Arc2dBuilder<'a, 'w, 's, Config, Clear>
/// A builder returned by [`GizmoBuffer::arc_2d`].
pub struct Arc2dBuilder<'a, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
gizmos: &'a mut GizmoBuffer<Config, Clear>,
isometry: Isometry2d,
arc_angle: f32,
radius: f32,
color: Color,
resolution: Option<u32>,
}

impl<Config, Clear> Arc2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Arc2dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand All @@ -92,7 +89,7 @@ where
}
}

impl<Config, Clear> Drop for Arc2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Drop for Arc2dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand Down Expand Up @@ -122,7 +119,7 @@ fn arc_2d_inner(arc_angle: f32, radius: f32, resolution: u32) -> impl Iterator<I

// === 3D ===

impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand Down Expand Up @@ -178,7 +175,7 @@ where
radius: f32,
isometry: impl Into<Isometry3d>,
color: impl Into<Color>,
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
) -> Arc3dBuilder<'_, Config, Clear> {
Arc3dBuilder {
gizmos: self,
start_vertex: Vec3::X,
Expand Down Expand Up @@ -233,7 +230,7 @@ where
from: Vec3,
to: Vec3,
color: impl Into<Color>,
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
) -> Arc3dBuilder<'_, Config, Clear> {
self.arc_from_to(center, from, to, color, |x| x)
}

Expand Down Expand Up @@ -279,7 +276,7 @@ where
from: Vec3,
to: Vec3,
color: impl Into<Color>,
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
) -> Arc3dBuilder<'_, Config, Clear> {
self.arc_from_to(center, from, to, color, |angle| {
if angle > 0.0 {
TAU - angle
Expand All @@ -299,7 +296,7 @@ where
to: Vec3,
color: impl Into<Color>,
angle_fn: impl Fn(f32) -> f32,
) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> {
) -> Arc3dBuilder<'_, Config, Clear> {
// `from` and `to` can be the same here since in either case nothing gets rendered and the
// orientation ambiguity of `up` doesn't matter
let from_axis = (from - center).normalize_or_zero();
Expand Down Expand Up @@ -366,7 +363,7 @@ where
from: Vec2,
to: Vec2,
color: impl Into<Color>,
) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> {
) -> Arc2dBuilder<'_, Config, Clear> {
self.arc_2d_from_to(center, from, to, color, core::convert::identity)
}

Expand Down Expand Up @@ -412,7 +409,7 @@ where
from: Vec2,
to: Vec2,
color: impl Into<Color>,
) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> {
) -> Arc2dBuilder<'_, Config, Clear> {
self.arc_2d_from_to(center, from, to, color, |angle| angle - TAU)
}

Expand All @@ -424,7 +421,7 @@ where
to: Vec2,
color: impl Into<Color>,
angle_fn: impl Fn(f32) -> f32,
) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> {
) -> Arc2dBuilder<'_, Config, Clear> {
// `from` and `to` can be the same here since in either case nothing gets rendered and the
// orientation ambiguity of `up` doesn't matter
let from_axis = (from - center).normalize_or_zero();
Expand All @@ -446,13 +443,13 @@ where
}
}

/// A builder returned by [`Gizmos::arc_2d`].
pub struct Arc3dBuilder<'a, 'w, 's, Config, Clear>
/// A builder returned by [`GizmoBuffer::arc_2d`].
pub struct Arc3dBuilder<'a, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
gizmos: &'a mut GizmoBuffer<Config, Clear>,
// this is the vertex the arc starts on in the XZ plane. For the normal arc_3d method this is
// always starting at Vec3::X. For the short/long arc methods we actually need a way to start
// at the from position and this is where this internal field comes into play. Some implicit
Expand All @@ -470,7 +467,7 @@ where
resolution: Option<u32>,
}

impl<Config, Clear> Arc3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Arc3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand All @@ -482,7 +479,7 @@ where
}
}

impl<Config, Clear> Drop for Arc3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Drop for Arc3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand Down
26 changes: 13 additions & 13 deletions crates/bevy_gizmos/src/arrows.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
//! Additional [`Gizmos`] Functions -- Arrows
//! Additional [`GizmoBuffer`] Functions -- Arrows
//!
//! Includes the implementation of [`Gizmos::arrow`] and [`Gizmos::arrow_2d`],
//! Includes the implementation of [`GizmoBuffer::arrow`] and [`GizmoBuffer::arrow_2d`],
//! and assorted support items.
use crate::prelude::{GizmoConfigGroup, Gizmos};
use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup};
use bevy_color::{
palettes::basic::{BLUE, GREEN, RED},
Color,
};
use bevy_math::{Quat, Vec2, Vec3, Vec3Swizzles};
use bevy_transform::TransformPoint;

/// A builder returned by [`Gizmos::arrow`] and [`Gizmos::arrow_2d`]
pub struct ArrowBuilder<'a, 'w, 's, Config, Clear>
/// A builder returned by [`GizmoBuffer::arrow`] and [`GizmoBuffer::arrow_2d`]
pub struct ArrowBuilder<'a, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
gizmos: &'a mut Gizmos<'w, 's, Config, Clear>,
gizmos: &'a mut GizmoBuffer<Config, Clear>,
start: Vec3,
end: Vec3,
color: Color,
double_ended: bool,
tip_length: f32,
}

impl<Config, Clear> ArrowBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> ArrowBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand Down Expand Up @@ -58,12 +58,12 @@ where
}
}

impl<Config, Clear> Drop for ArrowBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Drop for ArrowBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Draws the arrow, by drawing lines with the stored [`Gizmos`]
/// Draws the arrow, by drawing lines with the stored [`GizmoBuffer`]
fn drop(&mut self) {
if !self.gizmos.enabled {
return;
Expand Down Expand Up @@ -101,7 +101,7 @@ where
}
}

impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand All @@ -125,7 +125,7 @@ where
start: Vec3,
end: Vec3,
color: impl Into<Color>,
) -> ArrowBuilder<'_, 'w, 's, Config, Clear> {
) -> ArrowBuilder<'_, Config, Clear> {
let length = (end - start).length();
ArrowBuilder {
gizmos: self,
Expand Down Expand Up @@ -156,12 +156,12 @@ where
start: Vec2,
end: Vec2,
color: impl Into<Color>,
) -> ArrowBuilder<'_, 'w, 's, Config, Clear> {
) -> ArrowBuilder<'_, Config, Clear> {
self.arrow(start.extend(0.), end.extend(0.), color)
}
}

impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
Expand Down
Loading

0 comments on commit d2a07f9

Please sign in to comment.