Skip to content

Commit

Permalink
Fix draw order not working (#5794)
Browse files Browse the repository at this point in the history
### What

Draw order was not doing anything on 0.14!

The underlying issue was that the depth offset context system which
converts draw order to depth offsets didn't iterate any entities since
it used a iterator for entities.
This means we're now iterating a much larger entity set. I checked with
the "many entities" manual test and the context system takes about 0.5ms
for me in release there. This is kinda bad but it's not a big dent here
(unfortunately) and it runs in parallel to the much slower transform
context system.

This PR moves the (also broken due to heuristic changes) test from
"api_test" to a release check with a description what should be
observable.

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
* Using newly built examples:
[rerun.io/viewer](https://rerun.io/viewer/pr/5794)
* Using examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/5794?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/5794?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!

- [PR Build Summary](https://build.rerun.io/pr/5794)
- [Docs
preview](https://rerun.io/preview/a10a84ef97637dd05c615ac65b63b6711edeba44/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/a10a84ef97637dd05c615ac65b63b6711edeba44/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)

---------

Co-authored-by: Jeremy Leibs <[email protected]>
  • Loading branch information
Wumpf and jleibs authored Apr 4, 2024
1 parent f5dcf05 commit ac9f767
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 136 deletions.
8 changes: 7 additions & 1 deletion crates/re_space_view_spatial/src/contexts/depth_offsets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ impl ViewContextSystem for EntityDepthOffsets {

// Use a BTreeSet for entity hashes to get a stable order.
let mut entities_per_draw_order = BTreeMap::<DrawOrder, BTreeSet<DrawOrderTarget>>::new();
for data_result in query.iter_visible_data_results(ctx, Self::identifier()) {
for data_result in query.iter_all_data_results() {
// Note that we can't use `query.iter_visible_data_results` here since `EntityDepthOffsets` isn't a visualizer
// and thus not in the list of per system data results.
if !data_result.is_visible(ctx) {
continue;
}

if let Some(draw_order) = store
.query_latest_component::<DrawOrder>(&data_result.entity_path, &ctx.current_query())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl ViewContextSystem for TransformContext {
// Build an entity_property_map for just the CamerasParts, where we would expect to find
// the image_depth_plane_distance property.
let entity_prop_map: EntityPropertyMap = query
.per_system_data_results
.per_visualizer_data_results
.get(&CamerasVisualizer::identifier())
.map(|results| {
results
Expand Down
12 changes: 6 additions & 6 deletions crates/re_viewer_context/src/space_view/view_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,10 @@ pub struct ViewQuery<'s> {
/// The root of the space in which context the query happens.
pub space_origin: &'s EntityPath,

/// All queried [`DataResult`]s.
/// All [`DataResult`]s that are queried by active visualizers.
///
/// Contains also invisible objects, use `iter_entities` to iterate over visible ones.
pub per_system_data_results: PerSystemDataResults<'s>,
/// Contains also invisible objects, use `iter_visible_data_results` to iterate over visible ones.
pub per_visualizer_data_results: PerSystemDataResults<'s>,

/// The timeline we're on.
pub timeline: Timeline,
Expand All @@ -423,12 +423,12 @@ impl<'s> ViewQuery<'s> {
pub fn iter_visible_data_results<'a>(
&'a self,
ctx: &'a ViewerContext<'a>,
system: ViewSystemIdentifier,
visualizer: ViewSystemIdentifier,
) -> impl Iterator<Item = &DataResult>
where
's: 'a,
{
self.per_system_data_results.get(&system).map_or(
self.per_visualizer_data_results.get(&visualizer).map_or(
itertools::Either::Left(std::iter::empty()),
|results| {
itertools::Either::Right(
Expand All @@ -443,7 +443,7 @@ impl<'s> ViewQuery<'s> {

/// Iterates over all [`DataResult`]s of the [`ViewQuery`].
pub fn iter_all_data_results(&self) -> impl Iterator<Item = &DataResult> + '_ {
self.per_system_data_results
self.per_visualizer_data_results
.values()
.flat_map(|data_results| data_results.iter().copied())
}
Expand Down
6 changes: 3 additions & 3 deletions crates/re_viewport/src/system_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ pub fn execute_systems_for_space_view<'a>(

let query_result = ctx.lookup_query_result(space_view.id);

let mut per_system_data_results = PerSystemDataResults::default();
let mut per_visualizer_data_results = PerSystemDataResults::default();
{
re_tracing::profile_scope!("per_system_data_results");

query_result.tree.visit(&mut |node| {
for system in &node.data_result.visualizers {
per_system_data_results
per_visualizer_data_results
.entry(*system)
.or_default()
.push(&node.data_result);
Expand All @@ -122,7 +122,7 @@ pub fn execute_systems_for_space_view<'a>(
let query = re_viewer_context::ViewQuery {
space_view_id: space_view.id,
space_origin: &space_view.space_origin,
per_system_data_results,
per_visualizer_data_results,
timeline: *ctx.rec_cfg.time_ctrl.read().timeline(),
latest_at,
highlights,
Expand Down
94 changes: 94 additions & 0 deletions tests/python/release_checklist/check_draw_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import annotations

import os
from argparse import Namespace
from uuid import uuid4

import numpy as np
import rerun as rr
import rerun.blueprint as rrb

README = """
# 2D Draw order
This checks whether the draw order correctly determines the layering of 2D content.
### Action
You should see a single 2D space view with the following features:
- Gray background image
- On top of the background a green/red gradient image
- On top of that a blue (slightly transparent) blue square
- On top of the blue square a white square. *Nothing* is overlapping the white square!
- Between the gradient and the blue square rectangle (Box2D)
- Lines *behind* the rectangle
- Regular raster of points *in front* of the rectangle (unbroken by the rectangle)
"""


def log_readme() -> None:
rr.log("readme", rr.TextDocument(README, media_type=rr.MediaType.MARKDOWN), timeless=True)


def run_2d_layering() -> None:
rr.set_time_seconds("sim_time", 1)

# Large gray background.
img = np.full((512, 512), 64, dtype="uint8")
rr.log("2d_layering/background", rr.Image(img, draw_order=0.0))

# Smaller gradient in the middle.
img = np.zeros((256, 256, 3), dtype="uint8")
img[:, :, 0] = np.linspace(0, 255, 256, dtype="uint8")
img[:, :, 1] = np.linspace(0, 255, 256, dtype="uint8")
img[:, :, 1] = img[:, :, 1].transpose()
rr.log("2d_layering/middle_gradient", rr.Image(img, draw_order=1.0))

# Slightly smaller blue in the middle, on the same layer as the previous.
img = np.full((192, 192, 3), (0, 0, 255), dtype="uint8")
rr.log("2d_layering/middle_blue", rr.Image(img, draw_order=1.0))

# Small white on top.
img = np.full((128, 128), 255, dtype="uint8")
rr.log("2d_layering/top", rr.Image(img, draw_order=2.0))

# Rectangle in between the top and the middle.
rr.log(
"2d_layering/rect_between_top_and_middle",
rr.Boxes2D(array=[64, 64, 256, 256], draw_order=1.5, array_format=rr.Box2DFormat.XYWH),
)

# Lines behind the rectangle.
rr.log(
"2d_layering/lines_behind_rect",
rr.LineStrips2D([(i * 20, i % 2 * 100 + 100) for i in range(20)], draw_order=1.25),
)

# And some points in front of the rectangle.
rr.log(
"2d_layering/points_between_top_and_middle",
rr.Points2D(
[(32.0 + int(i / 16) * 16.0, 64.0 + (i % 16) * 16.0) for i in range(16 * 16)],
draw_order=1.51,
),
)


def run(args: Namespace) -> None:
rr.script_setup(
args,
f"{os.path.basename(__file__)}",
recording_id=uuid4(),
default_blueprint=rrb.Grid(rrb.Spatial2DView(origin="/"), rrb.TextDocumentView(origin="readme")),
)

log_readme()
run_2d_layering()


if __name__ == "__main__":
import argparse

parser = argparse.ArgumentParser(description="Interactive release checklist")
rr.script_add_args(parser)
args = parser.parse_args()
run(args)
45 changes: 0 additions & 45 deletions tests/python/test_api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,50 +97,6 @@ def small_image() -> None:
rr.log("small_image", rr.Image(img))


def run_2d_layering() -> None:
rr.set_time_seconds("sim_time", 1)

# Large gray background.
img = np.full((512, 512), 64, dtype="uint8")
rr.log("2d_layering/background", rr.Image(img, draw_order=0.0))

# Smaller gradient in the middle.
img = np.zeros((256, 256, 3), dtype="uint8")
img[:, :, 0] = np.linspace(0, 255, 256, dtype="uint8")
img[:, :, 1] = np.linspace(0, 255, 256, dtype="uint8")
img[:, :, 1] = img[:, :, 1].transpose()
rr.log("2d_layering/middle_gradient", rr.Image(img, draw_order=1.0))

# Slightly smaller blue in the middle, on the same layer as the previous.
img = np.full((192, 192, 3), (0, 0, 255), dtype="uint8")
rr.log("2d_layering/middle_blue", rr.Image(img, draw_order=1.0))

# Small white on top.
img = np.full((128, 128), 255, dtype="uint8")
rr.log("2d_layering/top", rr.Image(img, draw_order=2.0))

# Rectangle in between the top and the middle.
rr.log(
"2d_layering/rect_between_top_and_middle",
rr.Boxes2D(array=[64, 64, 256, 256], draw_order=1.5, array_format=rr.Box2DFormat.XYWH),
)

# Lines behind the rectangle.
rr.log(
"2d_layering/lines_behind_rect",
rr.LineStrips2D([(i * 20, i % 2 * 100 + 100) for i in range(20)], draw_order=1.25),
)

# And some points in front of the rectangle.
rr.log(
"2d_layering/points_between_top_and_middle",
rr.Points2D(
[(32.0 + int(i / 16) * 16.0, 64.0 + (i % 16) * 16.0) for i in range(16 * 16)],
draw_order=1.51,
),
)


def transforms() -> None:
rr.log("transforms", rr.ViewCoordinates.RIGHT_HAND_Y_UP, timeless=True)

Expand Down Expand Up @@ -474,7 +430,6 @@ def spawn_test(test: Callable[[], None], rec: rr.RecordingStream) -> None:

def main() -> None:
tests = {
"2d_layering": run_2d_layering,
"2d_lines": run_2d_lines,
"3d_points": run_3d_points,
"bbox": run_bounding_box,
Expand Down
80 changes: 0 additions & 80 deletions tests/rust/test_api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,82 +200,6 @@ fn test_rects(rec: &RecordingStream) -> anyhow::Result<()> {
Ok(())
}

fn colored_tensor<F: Fn(usize, usize) -> [u8; 3]>(
width: usize,
height: usize,
pos_to_color: F,
) -> ndarray::Array3<u8> {
let pos_to_color = &pos_to_color; // lambda borrow workaround.
ndarray::Array3::from_shape_vec(
(height, width, 3),
(0..height)
.flat_map(|y| (0..width).flat_map(move |x| pos_to_color(x, y)))
.collect_vec(),
)
.unwrap()
}

fn test_2d_layering(rec: &RecordingStream) -> anyhow::Result<()> {
use ndarray::prelude::*;

use rerun::archetypes::{Boxes2D, Image, LineStrips2D, Points2D};

rec.set_time_seconds("sim_time", 1f64);

// Add several overlapping images.
// Large dark gray in the background
let img = Array::<u8, _>::from_elem((512, 512, 1).f(), 64)
.as_standard_layout()
.view()
.to_owned();
rec.log(
"2d_layering/background",
&Image::try_from(img)?.with_draw_order(0.0),
)?;
// Smaller gradient in the middle
let img = colored_tensor(256, 256, |x, y| [x as u8, y as u8, 0]);
rec.log(
"2d_layering/middle_gradient",
&Image::try_from(img)?.with_draw_order(1.0),
)?;
// Slightly smaller blue in the middle, on the same layer as the previous.
let img = colored_tensor(192, 192, |_, _| [0, 0, 255]);
rec.log(
"2d_layering/middle_blue",
&Image::try_from(img)?.with_draw_order(1.0),
)?;
// Small white on top.
let img = Array::<u8, _>::from_elem((128, 128, 1).f(), 255);
rec.log(
"2d_layering/top",
&Image::try_from(img)?.with_draw_order(2.0),
)?;

// Rectangle in between the top and the middle.
rec.log(
"2d_layering/rect_between_top_and_middle",
&Boxes2D::from_mins_and_sizes([(64.0, 64.0)], [(256.0, 256.0)]).with_draw_order(1.5),
)?;

// Lines behind the rectangle.
rec.log(
"2d_layering/lines_behind_rect",
&LineStrips2D::new([(0..20).map(|i| ((i * 20) as f32, (i % 2 * 100 + 100) as f32))])
.with_draw_order(1.25),
)?;

// And some points in front of the rectangle.
rec.log(
"2d_layering/points_between_top_and_middle",
&Points2D::new(
(0..256).map(|i| (32.0 + (i / 16) as f32 * 16.0, 64.0 + (i % 16) as f32 * 16.0)),
)
.with_draw_order(1.51),
)?;

Ok(())
}

fn test_segmentation(rec: &RecordingStream) -> anyhow::Result<()> {
use rerun::{
archetypes::{AnnotationContext, Points2D},
Expand Down Expand Up @@ -539,9 +463,6 @@ enum Demo {
#[value(name("rects"))]
Rects,

#[value(name("2d_ordering"))]
TwoDOrdering,

#[value(name("segmentation"))]
Segmentation,

Expand Down Expand Up @@ -576,7 +497,6 @@ fn run(rec: &RecordingStream, args: &Args) -> anyhow::Result<()> {
Demo::LogCleared => test_log_cleared(rec)?,
Demo::Points3D => test_3d_points(rec)?,
Demo::Rects => test_rects(rec)?,
Demo::TwoDOrdering => test_2d_layering(rec)?,
Demo::Segmentation => test_segmentation(rec)?,
Demo::TextLogs => test_text_logs(rec)?,
Demo::Transforms3D => test_transforms_3d(rec)?,
Expand Down

0 comments on commit ac9f767

Please sign in to comment.