diff --git a/.cargo/config.toml b/.cargo/config.toml
index b44a938e2904..3bd9e9b437d3 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -7,3 +7,19 @@ run-wasm = "run --release --package run_wasm --"
# Some of our build.rs files only run if this is set,
# so that we don't run them on cargo publish or on users machines.
IS_IN_RERUN_WORKSPACE = "yes"
+
+
+# [target.x86_64-unknown-linux-gnu]
+# linker = "clang"
+# rustflags = [
+# "-C",
+# "link-arg=-fuse-ld=/usr/bin/mold",
+# "-C",
+# "split-debuginfo=unpacked",
+# ]
+# web_sys_unstable_apis is required to enable the web_sys clipboard API which egui_web uses,
+# https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Clipboard.html
+# as well as WebGPU apis.
+# https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html
+[target.wasm32-unknown-unknown]
+rustflags = ["--cfg=web_sys_unstable_apis"]
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 99f0096817a2..f2e91a7e8adf 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -13,5 +13,10 @@ To get an auto-generated PR description you can put "copilot:summary" or "copilo
### What
### Checklist
-* [ ] 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)
-* [ ] I've included a screenshot or gif (if applicable)
+
+- [ ] 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)
+- [ ] I've included a screenshot or gif (if applicable)
+
+
+
+PR Build Summary: {{ pr-build-summary }}
diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
index aed2272b2b4b..979dccb13717 100644
--- a/.github/workflows/labels.yml
+++ b/.github/workflows/labels.yml
@@ -29,4 +29,4 @@ jobs:
with:
mode: minimum
count: 1
- labels: "π analytics, πͺ³ bug, π§βπ» dev experience, dependencies, π documentation, π¬ discussion, examples, π performance, π python API, β re_datastore, πΊ re_viewer, πΊ re_renderer, π refactor, β΄ release, π¦ rust SDK, π¨ testing, ui, πΈοΈ web"
+ labels: "π analytics, πͺ³ bug, π§βπ» dev experience, π documentation, examples, π performance, π python API, β re_datastore, πΊ re_viewer, πΊ re_renderer, β΄ release, π¦ rust SDK, π¨ testing, ui, πΈοΈ web"
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index 491c5353550a..36cc25318920 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -8,19 +8,12 @@ on:
- "main"
tags:
- "v*.*.*" # on release tag
- workflow_dispatch:
- inputs:
- force_build_wheel:
- description: "Build python wheels"
- required: true
- default: false
- type: boolean
env:
PYTHON_VERSION: "3.8"
PRE_RELEASE_INSTRUCTIONS: |
## Installing the pre-release Python SDK
- 1. Download the correct `.whl`.
+ 1. Download the correct `.whl`. For Mac M1/M2, grab the "universal2" `.whl`
2. Run `pip install rerun_sdk<...>.whl` (replace `<...>` with the actual filename)
3. Test it: `rerun --version`
UBUNTU_REQUIRED_PKGS: libgtk-3-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libfontconfig1-dev libatk-bridge2.0 libfreetype6-dev libglib2.0-dev
@@ -63,163 +56,11 @@ jobs:
just py-requirements
# ---------------------------------------------------------------------------
- # We need one wheel-build to be special so the other builds (namely mac arm) can use its rrd
- # This copy-paste is awful, but we'll refactor the build soon.
- wheels-linux:
- if: github.event_name == 'push' || github.event.inputs.force_build_wheel
- name: Build Python Wheels (Linux)
- runs-on: ubuntu-latest
- container:
- image: rerunio/ci_docker:0.6
- steps:
- - uses: actions/checkout@v3
-
- # These should already be in the docker container, but run for good measure. A no-op install
- # should be fast, and this way things don't break if we add new packages without rebuilding
- # docker
- - name: Cache APT Packages
- uses: awalsh128/cache-apt-pkgs-action@v1.2.2
- with:
- packages: ${{ env.UBUNTU_REQUIRED_PKGS }}
- version: 2.0 # Increment this to pull newer packages
- execute_install_scripts: true
-
- - name: Set up cargo cache
- uses: Swatinem/rust-cache@v2
- with:
- env-vars: CARGO CC CFLAGS CXX CMAKE RUST CACHE_KEY
- # Don't update the cache -- it will be updated by the lint job
- # TODO(jleibs): this job will likely run before rust.yml updates
- # the cache. Better cross-job sequencing would be nice here
- save-if: False
-
- # These should already be in the docker container, but run for good measure. A no-op install
- # should be fast, and this way things don't break if we add new packages without rebuilding
- # docker
- - run: pip install -r rerun_py/requirements-build.txt
-
- # ----------------------------------------------------------------------------------
-
- - name: Patch Cargo.toml for pre-release
- if: github.ref == 'refs/heads/main'
- # After patching the pre-release version, run cargo update.
- # This updates the cargo.lock file with the new version numbers and keeps the wheel build from failing
- run: |
- python3 scripts/version_util.py --patch_prerelease
- cargo update -w
-
- - name: Version check for tagged-release
- if: startsWith(github.ref, 'refs/tags/v')
- # This call to version_util.py will assert version from Cargo.toml matches git tagged version vX.Y.Z
- run: |
- python3 scripts/version_util.py --check_version
-
- - name: Store the expected version
- # Find the current cargo version and store it in the GITHUB_ENV var: `expected_version`
- shell: bash
- run: |
- echo "expected_version=$(python3 scripts/version_util.py --bare_cargo_version)" >> $GITHUB_ENV
-
- - name: Build Wheel
- uses: PyO3/maturin-action@v1
- with:
- maturin-version: "0.14.10"
- manylinux: manylinux_2_31
- container: off
- command: build
- args: |
- --manifest-path rerun_py/Cargo.toml
- --release
- --target x86_64-unknown-linux-gnu
- --no-default-features
- --features pypi
- --out pre-dist
-
- - name: Install wheel dependencies
- # First we install the dependencies manually so we can use `--no-index` when installing the wheel.
- # This needs to be a separate step for some reason or the following step fails
- # TODO(jleibs): pull these deps from pyproject.toml
- # TODO(jleibs): understand why deps can't be installed in the same step as the wheel
- shell: bash
- run: |
- pip install deprecated numpy>=1.23 pyarrow==10.0.1
-
- - name: Install built wheel
- # Now install the wheel using a specific version and --no-index to guarantee we get the version from
- # the pre-dist folder. Note we don't use --force-reinstall here because --no-index means it wouldn't
- # find the dependencies to reinstall them.
- shell: bash
- run: |
- pip uninstall rerun-sdk
- pip install rerun-sdk==${{ env.expected_version }} --no-index --find-links pre-dist
-
- - name: Verify built wheel version
- shell: bash
- run: |
- python3 -m rerun --version
- which rerun
- rerun --version
-
- - name: Run unit tests
- shell: bash
- run: cd rerun_py/tests && pytest
-
- - name: Run e2e test
- shell: bash
- run: RUST_LOG=debug scripts/run_python_e2e_test.py --no-build # rerun-sdk is already built and installed
-
- - name: Unpack the wheel
- shell: bash
- run: |
- mkdir unpack-dist
- wheel unpack pre-dist/*.whl --dest unpack-dist
-
- - name: Get the folder name
- shell: bash
- run: |
- echo "pkg_folder=$(ls unpack-dist)" >> $GITHUB_ENV
-
- - name: Cache RRD dataset
- id: dataset
- uses: actions/cache@v3
- with:
- path: examples/python/colmap/dataset/
- # TODO(jleibs): Derive this key from the invocation below
- key: colmap-dataset-colmap-fiat-v0
-
- - name: Generate Embedded RRD file
- shell: bash
- # If you change the line below you should almost definitely change the `key:` line above by giving it a new, unique name
- run: |
- mkdir rrd
- pip install -r examples/python/colmap/requirements.txt
- python3 examples/python/colmap/main.py --dataset colmap_fiat --resize 800x600 --save rrd/colmap_fiat.rrd
- cp rrd/colmap_fiat.rrd unpack-dist/${{ env.pkg_folder }}/rerun_sdk/rerun_demo/colmap_fiat.rrd
-
- - name: Repack the wheel
- shell: bash
- run: |
- mkdir dist
- wheel pack unpack-dist/${{ env.pkg_folder }} --dest dist/
-
- - name: Upload wheels
- uses: actions/upload-artifact@v3
- with:
- name: wheels
- path: dist
-
- # All platforms are currently creating the same rrd file, upload one of them
- - name: Save RRD artifact
- uses: actions/upload-artifact@v3
- with:
- name: rrd
- path: rrd
- # ---------------------------------------------------------------------------
matrix-setup:
# Building all the wheels is expensive, so we only run this job when we push (to main or release tags),
- # or if the job was manually triggered with `force_build_wheel` set to true.
- if: github.event_name == 'push' || github.event.inputs.force_build_wheel
+ # or if the PR has the 'build wheels' label for explicit testing of wheels.
+ if: github.event_name == 'push' || contains( github.event.pull_request.labels.*.name, 'π build wheels')
runs-on: ubuntu-latest
outputs:
@@ -234,33 +75,52 @@ jobs:
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: echo "$JOB_CONTEXT"
+ # Sets TAGGED_OR_MAIN if this workflow is running on a tag or the main branch.
+ - name: Set TAGGED_OR_MAIN
+ if: startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main'
+ run: echo "TAGGED_OR_MAIN=1" >> $GITHUB_ENV
- id: set-matrix
shell: bash
- # TODO(jleibs): figure out why tests are failing to complete on `x86_64-apple-darwin`
- # See: https://github.com/rerun-io/rerun/pull/1853
run: |
matrix=()
- matrix+=('{"platform": "macos", "target": "x86_64-apple-darwin", "run_tests": false, "runs_on": "macos-latest" },')
- matrix+=('{"platform": "macos", "target": "aarch64-apple-darwin", "run_tests": false, "runs_on": "macos-latest" },') # NOTE: we can't run tests on arm since our macos runner is x86_64
- matrix+=('{"platform": "windows", "target": "x86_64-pc-windows-msvc", "run_tests": true, "runs_on": "windows-latest-8-cores"},')
+
+ if [[ $TAGGED_OR_MAIN ]]; then
+ # MacOS build is really slow (>30 mins); uses up a lot of CI minutes
+ matrix+=('{"platform": "macos", "runs_on": "macos-latest"},')
+ fi
+ matrix+=('{"platform": "windows", "runs_on": "windows-latest-8-cores"},')
+ matrix+=('{"platform": "linux", "runs_on": "ubuntu-latest-16-cores", container: {"image": "rerunio/ci_docker:0.5"}}')
echo "Matrix values: ${matrix[@]}"
echo "matrix={\"include\":[${matrix[@]}]}" >> $GITHUB_OUTPUT
wheels:
- name: Build Remaining Python Wheels
- needs: [lint, matrix-setup, wheels-linux]
+ name: Build Python Wheels
+ needs: [lint, matrix-setup]
strategy:
matrix: ${{fromJson(needs.matrix-setup.outputs.matrix)}}
runs-on: ${{ matrix.runs_on }}
+ container: ${{ matrix.container }}
+
steps:
- uses: actions/checkout@v3
+ # These should already be in the docker container, but run for good measure. A no-op install
+ # should be fast, and this way things don't break if we add new packages without rebuilding
+ # docker
+ - name: Cache APT Packages
+ if: matrix.platform == 'linux'
+ uses: awalsh128/cache-apt-pkgs-action@v1.2.2
+ with:
+ packages: ${{ env.UBUNTU_REQUIRED_PKGS }}
+ version: 2.0 # Increment this to pull newer packages
+ execute_install_scripts: true
+
- name: Set up cargo cache
uses: Swatinem/rust-cache@v2
with:
@@ -273,6 +133,7 @@ jobs:
# The pip-cache setup logic doesn't work in the ubuntu docker container
# That's probably fine since we bake these deps into the container already
- name: Setup python
+ if: matrix.platform != 'linux'
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -321,11 +182,11 @@ jobs:
- name: Patch Cargo.toml for pre-release
if: github.ref == 'refs/heads/main'
- # After patching the pre-release version, run cargo update.
+ # After patching the pre-release version, run cargo check.
# This updates the cargo.lock file with the new version numbers and keeps the wheel build from failing
run: |
python3 scripts/version_util.py --patch_prerelease
- cargo update -w
+ cargo check
- name: Version check for tagged-release
if: startsWith(github.ref, 'refs/tags/v')
@@ -333,12 +194,6 @@ jobs:
run: |
python3 scripts/version_util.py --check_version
- - name: Store the expected version
- # Find the current cargo version and store it in the GITHUB_ENV var: `expected_version`
- shell: bash
- run: |
- echo "expected_version=$(python3 scripts/version_util.py --bare_cargo_version)" >> $GITHUB_ENV
-
- name: Build Wheel
uses: PyO3/maturin-action@v1
with:
@@ -349,42 +204,18 @@ jobs:
args: |
--manifest-path rerun_py/Cargo.toml
--release
- --target ${{ matrix.target }}
--no-default-features
--features pypi
+ --universal2
--out pre-dist
- name: Install built wheel
- if: ${{ matrix.run_tests }}
- # First we install the dependencies manually so we can use `--no-index` when installing the wheel.
- # Then install the wheel using a specific version and --no-index to guarantee we get the version from
- # the pre-dist folder. Note we don't use --force-reinstall here because --no-index means it wouldn't
- # find the dependencies to reinstall them.
- # TODO(jleibs): pull these deps from pyproject.toml
- shell: bash
run: |
- pip uninstall rerun-sdk
- pip install deprecated numpy>=1.23 pyarrow==10.0.1
- pip install rerun-sdk==${{ env.expected_version }} --no-index --find-links pre-dist
+ pip install rerun-sdk --find-links pre-dist --force-reinstall
- - name: Verify built wheel version
- if: ${{ matrix.run_tests }}
- shell: bash
- run: |
- python3 -m rerun --version
- which rerun
- rerun --version
-
- - name: Run unit tests
- if: ${{ matrix.run_tests }}
- shell: bash
+ - name: Run tests
run: cd rerun_py/tests && pytest
- - name: Run e2e test
- if: ${{ matrix.run_tests }}
- shell: bash
- run: RUST_LOG=debug scripts/run_python_e2e_test.py --no-build # rerun-sdk is already built and installed
-
- name: Unpack the wheel
shell: bash
run: |
@@ -396,17 +227,21 @@ jobs:
run: |
echo "pkg_folder=$(ls unpack-dist)" >> $GITHUB_ENV
- - name: Download RRD
- uses: actions/download-artifact@v3
+ - name: Cache RRD dataset
+ id: dataset
+ uses: actions/cache@v3
with:
- name: rrd
- path: rrd
+ path: examples/python/colmap/dataset/
+ # TODO(jleibs): Derive this key from the invocation below
+ key: colmap-dataset-colmap-fiat-v0
- - name: Insert the rrd
+ - name: Generate Embedded RRD file
shell: bash
# If you change the line below you should almost definitely change the `key:` line above by giving it a new, unique name
run: |
- cp rrd/colmap_fiat.rrd unpack-dist/${{ env.pkg_folder }}/rerun_sdk/rerun_demo/colmap_fiat.rrd
+ pip install -r examples/python/colmap/requirements.txt
+ python3 examples/python/colmap/main.py --dataset colmap_fiat --resize 800x600 --save colmap.rrd
+ cp colmap.rrd unpack-dist/${{ env.pkg_folder }}/rerun_sdk/rerun_demo/colmap.rrd
- name: Repack the wheel
shell: bash
@@ -422,60 +257,6 @@ jobs:
# ---------------------------------------------------------------------------
- upload_rrd:
- name: Upload RRD to GCloud
- permissions:
- contents: "read"
- id-token: "write"
- needs: [wheels]
- runs-on: "ubuntu-latest"
- steps:
- - name: Download Artifact
- uses: actions/download-artifact@v3
- with:
- name: rrd
- path: rrd
-
- - id: "auth"
- uses: google-github-actions/auth@v1
- with:
- workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }}
- service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT }}
-
- - name: Add SHORT_SHA env property with commit short sha
- run: echo "SHORT_SHA=`echo ${{github.sha}} | cut -c1-7`" >> $GITHUB_ENV
-
- - name: "Upload RRD (commit)"
- uses: google-github-actions/upload-cloud-storage@v1
- with:
- path: "rrd"
- destination: "rerun-example-rrd/commit/${{env.SHORT_SHA}}"
- parent: false
-
- - name: "Upload RRD (prerelease)"
- if: "!startsWith(github.ref , 'refs/tags/v')"
- uses: google-github-actions/upload-cloud-storage@v1
- with:
- path: "rrd"
- destination: "rerun-example-rrd/prerelease"
- parent: false
-
- - name: "Upload RRD (tagged)"
- if: startsWith(github.ref, 'refs/tags/v')
- uses: google-github-actions/upload-cloud-storage@v1
- with:
- path: "rrd"
- destination: "rerun-example-rrd/version/${{github.ref_name}}"
- parent: false
-
- - name: "Upload RRD (latest release)"
- if: github.ref == 'latest'
- uses: google-github-actions/upload-cloud-storage@v1
- with:
- path: "rrd"
- destination: "rerun-example-rrd/latest"
- parent: false
-
# See https://github.com/ncipollo/release-action
pre-release:
name: Pre Release
@@ -492,28 +273,34 @@ jobs:
# First delete the old prerelease. If we don't do this, we don't get things like
# proper source-archives and changelog info.
# https://github.com/dev-drprasad/delete-tag-and-release
- - uses: dev-drprasad/delete-tag-and-release@v0.2.1
+ - uses: dev-drprasad/delete-tag-and-release@v0.2.0
with:
tag_name: prerelease
delete_release: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # Re-tag the prerelease with the commit from this build
+ # https://github.com/richardsimko/update-tag
+ - name: Update prerelease tag
+ uses: richardsimko/update-tag@v1
+ with:
+ tag_name: prerelease
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
# Create the actual prerelease
- # https://github.com/ncipollo/release-action
+ # https://github.com/softprops/action-gh-release
- name: GitHub Release
- uses: ncipollo/release-action@v1.12.0
+ uses: softprops/action-gh-release@v0.1.15
with:
+ name: "Development Build"
body: ${{ env.PRE_RELEASE_INSTRUCTIONS }}
prerelease: true
- artifacts: dist/*
- name: "Development Build"
- tag: "prerelease"
+ tag_name: prerelease
+ files: dist/*
token: ${{ secrets.GITHUB_TOKEN }}
- generateReleaseNotes: true
- allowUpdates: true
- removeArtifacts: true
- replacesArtifacts: true
+ generate_release_notes: true
# ---------------------------------------------------------------------------
@@ -530,14 +317,13 @@ jobs:
name: wheels
path: dist
- # https://github.com/ncipollo/release-action
- name: GitHub Release
- uses: ncipollo/release-action@v1.12.0
+ uses: softprops/action-gh-release@v0.1.15
with:
- prerelease: true
- artifacts: dist/*
+ prerelease: false
+ files: dist/*
token: ${{ secrets.GITHUB_TOKEN }}
- generateReleaseNotes: true
+ generate_release_notes: true
- name: Publish to PyPI
uses: PyO3/maturin-action@v1
@@ -602,28 +388,14 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- # Mike will incrementally update the existing gh-pages branch
- # We then check it out, and reset it to a new orphaned branch, which we force-push to origin
- # to make sure we don't accumulate unnecessary history in gh-pages branch
- name: Deploy via mike # https://github.com/jimporter/mike
if: startsWith(github.ref, 'refs/tags/v')
run: |
git fetch
- mike deploy -F rerun_py/mkdocs.yml --rebase -b gh-pages --prefix docs/python -u ${{github.ref_name}} latest
- git checkout gh-pages
- git checkout --orphan gh-pages-orphan
- git commit -m "Update docs for ${GITHUB_SHA}"
- git push origin gh-pages-orphan:gh-pages -f
-
- # Mike will incrementally update the existing gh-pages branch
- # We then check it out, and reset it to a new orphaned branch, which we force-push to origin
- # to make sure we don't accumulate unnecessary history in gh-pages branch
+ mike deploy -F rerun_py/mkdocs.yml -p --rebase -b gh-pages --prefix docs/python -u ${{github.ref_name}} latest
+
- name: Deploy tag via mike # https://github.com/jimporter/mike
if: github.ref == 'refs/heads/main'
run: |
git fetch
- mike deploy -F rerun_py/mkdocs.yml --rebase -b gh-pages --prefix docs/python HEAD
- git checkout gh-pages
- git checkout --orphan gh-pages-orphan
- git commit -m "Update docs for ${GITHUB_SHA}"
- git push origin gh-pages-orphan:gh-pages -f
+ mike deploy -F rerun_py/mkdocs.yml -p --rebase -b gh-pages --prefix docs/python HEAD
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index f48703b53395..22cc2658a4d0 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -5,18 +5,6 @@ on:
branches:
- "main"
pull_request:
- workflow_dispatch:
- inputs:
- force_update_web_build:
- description: "Upload web build to google cloud"
- required: true
- default: false
- type: boolean
- force_run_benchmarks:
- description: "Run the rust benchmarks"
- required: true
- default: false
- type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }} # Cancel previous CI jobs on the same branch
@@ -26,10 +14,10 @@ env:
# web_sys_unstable_apis is required to enable the web_sys clipboard API which egui_web uses
# https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Clipboard.html
# https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html
- RUSTFLAGS: --cfg=web_sys_unstable_apis --deny warnings
+ RUSTFLAGS: --cfg=web_sys_unstable_apis -D warnings
# See https://github.com/ericseppanen/cargo-cranky/issues/8
- RUSTDOCFLAGS: --deny warnings --deny rustdoc::missing_crate_level_docs
+ RUSTDOCFLAGS: -D warnings -D rustdoc::missing_crate_level_docs
permissions:
# deployments permission to deploy GitHub pages website
@@ -38,16 +26,15 @@ permissions:
contents: write
jobs:
- # See the benchmarks at https://ref.rerun.io/dev/bench/
rs-benchmarks:
name: Rust Criterion benchmarks
- # Only run the rs-benchmarks job when a commit gets pushed to main or we manually specify it
- if: ${{ github.ref == 'refs/heads/main' || github.event.inputs.force_run_benchmarks }}
+ # Only save the rs-benchmarks job when a commit gets pushed to main
+ if: ${{ github.event_name == 'push' }}
runs-on: ubuntu-latest-16-cores
container:
- image: rerunio/ci_docker:0.6
+ image: rerunio/ci_docker:0.5
env:
RUSTFLAGS: ${{env.RUSTFLAGS}}
RUSTDOCFLAGS: ${{env.RUSTDOCFLAGS}}
@@ -71,13 +58,12 @@ jobs:
--all-features \
-p re_arrow_store \
-p re_data_store \
- -p re_log_encoding \
+ -p re_log_types \
-p re_query \
-p re_tuid \
-- --output-format=bencher | tee output.txt
- name: Store benchmark result
- # https://github.com/benchmark-action/github-action-benchmark
uses: benchmark-action/github-action-benchmark@v1
with:
name: Rust Benchmark
@@ -89,11 +75,10 @@ jobs:
comment-on-alert: true
alert-threshold: "150%"
fail-on-alert: true
- comment-always: false # Generates too much GitHub notification spam
+ comment-always: true
- # Save, results and push to GitHub only on main
- save-data-file: ${{ github.ref == 'refs/heads/main' }}
- auto-push: ${{ github.ref == 'refs/heads/main' }}
+ # Push and deploy GitHub pages branch automatically
+ auto-push: true
gh-pages-branch: gh-pages
benchmark-data-dir-path: dev/bench
max-items-in-chart: 30
@@ -104,7 +89,7 @@ jobs:
name: Rust lints (fmt, check, cranky, tests, doc)
runs-on: ubuntu-latest-16-cores
container:
- image: rerunio/ci_docker:0.6
+ image: rerunio/ci_docker:0.5
env:
RUSTFLAGS: ${{env.RUSTFLAGS}}
RUSTDOCFLAGS: ${{env.RUSTDOCFLAGS}}
@@ -137,22 +122,13 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: cranky
- args: --all-targets --all-features -- --deny warnings
+ args: --all-targets --all-features -- -D warnings
- # --------------------------------------------------------------------------------
- # Check a few important permutations of the feature flags for our `rerun` library:
- - name: Check rerun with `--no-default-features``
+ - name: Check no default features
uses: actions-rs/cargo@v1
with:
- command: cranky
- args: --locked -p rerun --no-default-features
-
- - name: Check rerun with `--features sdk`
- uses: actions-rs/cargo@v1
- with:
- command: cranky
- args: --locked -p rerun --no-default-features --features sdk
- # --------------------------------------------------------------------------------
+ command: check
+ args: --locked --no-default-features --features __ci --lib
- name: Test doc-tests
uses: actions-rs/cargo@v1
@@ -210,7 +186,7 @@ jobs:
name: Check Rust web build (wasm32 + wasm-bindgen)
runs-on: ubuntu-latest-16-cores
container:
- image: rerunio/ci_docker:0.6
+ image: rerunio/ci_docker:0.5
env:
RUSTFLAGS: ${{env.RUSTFLAGS}}
RUSTDOCFLAGS: ${{env.RUSTDOCFLAGS}}
@@ -219,8 +195,8 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
- profile: default
- toolchain: 1.67.1
+ profile: minimal
+ toolchain: 1.67.0
target: wasm32-unknown-unknown
override: true
@@ -231,138 +207,53 @@ jobs:
# See: https://github.com/rerun-io/rerun/pull/497
save-if: ${{ github.event_name == 'push'}}
- - name: clippy check re_viewer wasm32
- run: ./scripts/clippy_wasm.sh
-
- - name: Check re_renderer examples wasm32
+ - name: Check re_viewer wasm32
uses: actions-rs/cargo@v1
with:
command: check
- args: --locked --target wasm32-unknown-unknown --target-dir target_wasm -p re_renderer --examples
+ args: --locked --all-features --lib --target wasm32-unknown-unknown -p re_viewer
- - name: Build web-viewer (debug)
+ - name: Check re_renderer examples wasm32
uses: actions-rs/cargo@v1
with:
- command: run
- args: --locked -p re_build_web_viewer -- --debug
+ command: check
+ args: --locked --target wasm32-unknown-unknown -p re_renderer --examples
- # ---------------------------------------------------------------------------
+ - run: ./scripts/wasm_bindgen_check.sh --skip-setup
- rs-build-web-viewer:
- name: Upload web build to google cloud (wasm32 + wasm-bindgen)
- permissions:
- contents: "read"
- id-token: "write"
+ # ---------------------------------------------------------------------------
- if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event.inputs.force_update_web_build
+ rs-cargo-deny:
+ name: Check Rust dependencies (cargo-deny)
runs-on: ubuntu-latest-16-cores
container:
- image: rerunio/ci_docker:0.6
+ image: rerunio/ci_docker:0.5
env:
RUSTFLAGS: ${{env.RUSTFLAGS}}
RUSTDOCFLAGS: ${{env.RUSTDOCFLAGS}}
steps:
- uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: 1.67.0
- target: wasm32-unknown-unknown
- override: true
-
- - name: Set up cargo cache
- uses: Swatinem/rust-cache@v2
- with:
- env-vars: CARGO CC CFLAGS CXX CMAKE RUST CACHE_KEY
- # See: https://github.com/rerun-io/rerun/pull/497
- save-if: ${{ github.event_name == 'push'}}
-
- - name: Patch Cargo.toml for pre-release
- if: "!startsWith(github.ref , 'refs/tags/v')"
- # After patching the pre-release version, run cargo update.
- # This updates the cargo.lock file with the new version numbers and keeps the wheel build from failing
- run: |
- python3 scripts/version_util.py --patch_prerelease
- cargo update -w
-
- - name: Build web-viewer (release)
+ - name: cargo deny aarch64-apple-darwin check
uses: actions-rs/cargo@v1
with:
- command: run
- args: --locked -p re_build_web_viewer -- --release
-
- # Upload the wasm, html etc to a Google cloud bucket:
- - id: "auth"
- uses: google-github-actions/auth@v1
- with:
- workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }}
- service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT }}
-
- - name: Add SHORT_SHA env property with commit short sha
- run: echo "SHORT_SHA=`echo ${{github.sha}} | cut -c1-7`" >> $GITHUB_ENV
-
- - name: "Upload web-viewer (commit)"
- uses: google-github-actions/upload-cloud-storage@v1
- with:
- path: "web_viewer"
- destination: "rerun-web-viewer/commit/${{env.SHORT_SHA}}"
- parent: false
-
- - name: "Upload web-viewer (prerelease)"
- if: github.ref == 'refs/heads/main'
- uses: google-github-actions/upload-cloud-storage@v1
- with:
- path: "web_viewer"
- destination: "rerun-web-viewer/prerelease"
- parent: false
+ command: deny
+ args: --log-level=error --all-features --target aarch64-apple-darwin check
- - name: "Upload web-viewer (tagged)"
- if: startsWith(github.ref, 'refs/tags/v')
- uses: google-github-actions/upload-cloud-storage@v1
+ - name: cargo deny wasm32-unknown-unknown check
+ uses: actions-rs/cargo@v1
with:
- path: "web_viewer"
- destination: "rerun-web-viewer/version/${{github.ref_name}}"
- parent: false
+ command: deny
+ args: --log-level=error --all-features --target wasm32-unknown-unknown check
- - name: "Upload web-viewer (latest release)"
- if: github.ref == 'latest'
- uses: google-github-actions/upload-cloud-storage@v1
+ - name: cargo deny x86_64-pc-windows-msvc
+ uses: actions-rs/cargo@v1
with:
- path: "web_viewer"
- destination: "rerun-web-viewer/latest"
- parent: false
-
- # ---------------------------------------------------------------------------
-
- rs-cargo-deny:
- name: Check Rust dependencies (cargo-deny)
- runs-on: ubuntu-latest-16-cores
- container:
- image: rerunio/ci_docker:0.6
- env:
- RUSTFLAGS: ${{env.RUSTFLAGS}}
- RUSTDOCFLAGS: ${{env.RUSTDOCFLAGS}}
+ command: deny
+ args: --log-level=error --all-features --target x86_64-pc-windows-msvc check
- # TODO(emilk): remove this matrix when https://github.com/EmbarkStudios/cargo-deny/issues/324 is resolved
- strategy:
- fail-fast: false
- matrix:
- platform:
- - i686-pc-windows-gnu
- - i686-pc-windows-msvc
- - i686-unknown-linux-gnu
- - wasm32-unknown-unknown
- - x86_64-apple-darwin
- - x86_64-pc-windows-gnu
- - x86_64-pc-windows-msvc
- - x86_64-unknown-linux-gnu
- - x86_64-unknown-redox
-
- steps:
- - uses: actions/checkout@v3
- - uses: EmbarkStudios/cargo-deny-action@v1
+ - name: cargo deny x86_64-unknown-linux-musl check
+ uses: actions-rs/cargo@v1
with:
- command: check
- log-level: error
- arguments: --all-features --target ${{ matrix.platform }}
+ command: deny
+ args: --log-level=error --all-features --target x86_64-unknown-linux-musl check
diff --git a/.github/workflows/toml.yml b/.github/workflows/toml.yml
index f3a60ec74885..120735d1f729 100644
--- a/.github/workflows/toml.yml
+++ b/.github/workflows/toml.yml
@@ -20,7 +20,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
- toolchain: 1.67.1
+ toolchain: 1.67.0
override: true
- name: Set up cargo cache
diff --git a/.github/workflows/typos.yml b/.github/workflows/typos.yml
deleted file mode 100644
index c3bc84125f51..000000000000
--- a/.github/workflows/typos.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-# https://github.com/crate-ci/typos
-# Add exceptions to _typos.toml
-# install and run locally: cargo install typos-cli && typos
-
-name: Spell Check
-on: [pull_request]
-
-jobs:
- run:
- name: Spell Check
- runs-on: ubuntu-latest
- steps:
- - name: Checkout Actions Repository
- uses: actions/checkout@v2
-
- - name: Check spelling of entire workspace
- uses: crate-ci/typos@master
diff --git a/.mypy.ini b/.mypy.ini
index 3975c2924ca1..e1c3f9421600 100644
--- a/.mypy.ini
+++ b/.mypy.ini
@@ -1,5 +1,5 @@
[mypy]
-files = rerun_py/rerun_sdk/rerun, rerun_py/tests, examples/python
+files = rerun_py/rerun_sdk/depthai_viewer, rerun_py/tests, examples/python
exclude = examples/python/objectron/proto|examples/python/ros
namespace_packages = True
show_error_codes = True
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index aae305c13ea6..32731cdfadeb 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -2,17 +2,18 @@
// See https://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
- "charliermarsh.ruff", // Ruff for linting
+ "charliermarsh.ruff",
+ "github.vscode-github-actions",
"ms-python.python",
- "ms-vsliveshare.vsliveshare", // Live Share
+ "ms-vsliveshare.vsliveshare",
"polymeilex.wgsl",
- "rust-lang.rust-analyzer", // Rust-analyzer
+ "rust-lang.rust-analyzer",
"serayuzgur.crates",
"streetsidesoftware.code-spell-checker",
- "tamasfe.even-better-toml", // TOML LSP
- "vadimcn.vscode-lldb", // CodeLLDB
- "wayou.vscode-todo-highlight", // TODO Highlight
- "webfreak.debug", // Native Debug
- "zxh404.vscode-proto3", // vscode-proto3
+ "tamasfe.even-better-toml",
+ "vadimcn.vscode-lldb",
+ "wayou.vscode-todo-highlight",
+ "webfreak.debug",
+ "zxh404.vscode-proto3",
]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index c52ea2c7178d..1722a88e03b3 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,82 +1,75 @@
{
- "editor.formatOnSave": true,
- "editor.semanticTokenColorCustomizations": {
- "rules": {
- "*.unsafe:rust": "#eb5046"
- }
- },
- "files.insertFinalNewline": true,
- "files.trimTrailingWhitespace": true,
- "files.exclude": {
- "env/**": true,
- "target/**": true,
- "target_ra/**": true,
- "target_wasm/**": true,
- },
- "files.autoGuessEncoding": true,
- "python.formatting.provider": "black",
- "python.formatting.blackArgs": [
- "--config",
- "rerun_py/pyproject.toml"
- ],
- "python.linting.mypyEnabled": true,
- "python.linting.enabled": true,
- "cSpell.words": [
- "andreas",
- "bbox",
- "bindgroup",
- "colormap",
- "emath",
- "framebuffer",
- "hoverable",
- "ilog",
- "jumpflooding",
- "Keypoint",
- "memoffset",
- "nyud",
- "objectron",
- "Readback",
- "readbacks",
- "Skybox",
- "smallvec",
- "swapchain",
- "texcoords",
- "Tonemapper",
- "tonemapping",
- "voronoi",
- "vram",
- "Wgsl"
- ],
- // don't share a cargo lock with rust-analyzer.
- // see https://github.com/rerun-io/rerun/pull/519 for rationale
- "rust-analyzer.checkOnSave.overrideCommand": [
- "cargo",
- "cranky",
- "--target-dir=target_ra",
- "--workspace",
- "--message-format=json",
- "--all-targets",
- "--all-features", // --all-features will set the `__ci` feature flag, which stops crates/re_web_viewer_server/build.rs from building the web viewer
- ],
- "rust-analyzer.cargo.buildScripts.overrideCommand": [
- "cargo",
- "check",
- "--quiet",
- "--target-dir=target_ra",
- "--workspace",
- "--message-format=json",
- "--all-targets",
- "--all-features", // --all-features will set the `__ci` feature flag, which stops crates/re_web_viewer_server/build.rs from building the web viewer
- ],
- // Our build scripts are generating code.
- // Having Rust Analyzer do this while doing other builds can lead to catastrophic failures.
- // INCLUDING attempts to publish a new release!
- "rust-analyzer.cargo.buildScripts.enable": false,
- "python.analysis.extraPaths": [
- "rerun_py/rerun_sdk"
- ],
- "ruff.args": [
- "--config",
- "rerun_py/pyproject.toml"
- ],
+ "editor.formatOnSave": true,
+ "editor.semanticTokenColorCustomizations": {
+ "rules": {
+ "*.unsafe:rust": "#eb5046"
+ }
+ },
+ "files.insertFinalNewline": true,
+ "files.trimTrailingWhitespace": true,
+ "files.exclude": {
+ "env/**": true,
+ "target/**": true,
+ "target_ra/**": true,
+ "target_wasm/**": true
+ },
+ "files.autoGuessEncoding": true,
+ "python.formatting.provider": "black",
+ "python.formatting.blackArgs": ["--config", "rerun_py/pyproject.toml"],
+ "python.linting.mypyEnabled": true,
+ "python.linting.enabled": true,
+ "cSpell.words": [
+ "andreas",
+ "bbox",
+ "bindgroup",
+ "colormap",
+ "emath",
+ "framebuffer",
+ "hoverable",
+ "ilog",
+ "jumpflooding",
+ "Keypoint",
+ "memoffset",
+ "nyud",
+ "objectron",
+ "Readback",
+ "readbacks",
+ "Skybox",
+ "smallvec",
+ "swapchain",
+ "texcoords",
+ "Tonemapper",
+ "tonemapping",
+ "unsmoothed",
+ "voronoi",
+ "vram",
+ "Wgsl"
+ ],
+ // don't share a cargo lock with rust-analyzer.
+ // see https://github.com/rerun-io/rerun/pull/519 for rationale
+ "rust-analyzer.checkOnSave.overrideCommand": [
+ "cargo",
+ "cranky",
+ "--target-dir=target_ra",
+ "--workspace",
+ "--message-format=json",
+ "--all-targets",
+ "--all-features" // --all-features will set the `__ci` feature flag, which stops crates/re_web_viewer_server/build.rs from building the web viewer
+ ],
+ "rust-analyzer.cargo.buildScripts.overrideCommand": [
+ "cargo",
+ "check",
+ "--quiet",
+ "--target-dir=target_ra",
+ "--workspace",
+ "--message-format=json",
+ "--all-targets",
+ "--all-features" // --all-features will set the `__ci` feature flag, which stops crates/re_web_viewer_server/build.rs from building the web viewer
+ ],
+ // Our build scripts are generating code.
+ // Having Rust Analyzer do this while doing other builds can lead to catastrophic failures.
+ // INCLUDING attempts to publish a new release!
+ "rust-analyzer.cargo.buildScripts.enable": false,
+ "python.analysis.extraPaths": ["rerun_py/rerun_sdk"],
+ "ruff.args": ["--config", "rerun_py/pyproject.toml"]
}
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 31fb2a572c63..cd2e4c7115e3 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -22,10 +22,10 @@ The logging data can be written to disk as `.rrd` files, or transmitted over TCP
The Rerun Viewer is where log data is visualized. It is usually run as a native app, but can also be compiled to WebAssembly (Wasm) and run in a browser.
#### Native viewer
-The easiest way to launch the viewer is directly from the logging API with `rr.init("my_app", spawn=True)`. However, the standalone viewer can also be run from the command line, for example to view an `.rrd` file: `rerun mydata.rrd`.
+The easiest way to launch the viewer is directly from the logging API with `viewer.init("my_app", spawn=True)`. However, the standalone viewer can also be run from the command line, for example to view an `.rrd` file: `rerun mydata.rrd`.
#### Web viewer
-You can try running the viewer in a browser using `rr.serve()` in python, or using `rerun --web-viewer mydata.rrd`.
+You can try running the viewer in a browser using `viewer.serve()` in python, or using `rerun --web-viewer mydata.rrd`.
The web viewer consists of just a few small files - a thin `.html`, a `.wasm` blob, and an auto-generated `.js` bridge for the wasm. These files are served using the [`re_web_viewer_server`](https://github.com/rerun-io/rerun/tree/latest/crates/re_web_viewer_server) crate.
diff --git a/BUILD.md b/BUILD.md
index 011e4d29b023..433d64953270 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -1,22 +1,22 @@
# Building Rerun
-This is a guide to how to build Rerun.
+This is a guide to how to build Rerun.
## See also
-* [`rerun_py/README.md`](rerun_py/README.md) - build instructions for Python SDK
-* [`ARCHITECTURE.md`](ARCHITECTURE.md)
-* [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)
-* [`CODE_STYLE.md`](CODE_STYLE.md)
-* [`CONTRIBUTING.md`](CONTRIBUTING.md)
-* [`RELEASES.md`](RELEASES.md)
+- [`rerun_py/README.md`](rerun_py/README.md) - build instructions for Python SDK
+- [`ARCHITECTURE.md`](ARCHITECTURE.md)
+- [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)
+- [`CODE_STYLE.md`](CODE_STYLE.md)
+- [`CONTRIBUTING.md`](CONTRIBUTING.md)
+- [`RELEASES.md`](RELEASES.md)
## Getting started with the repository.
-* Install the Rust toolchain:
-* `git clone git@github.com:rerun-io/rerun.git && cd rerun`
-* Run `./scripts/setup_dev.sh`.
-* Make sure `cargo --version` prints `1.67.1` once you are done
+- Install the Rust toolchain:
+- `git clone git@github.com:rerun-io/rerun.git && cd rerun`
+- Run `./scripts/setup_dev.sh`.
+- Make sure `cargo --version` prints `1.67.1` once you are done
### Apple-silicon Macs
@@ -35,7 +35,8 @@ Python API docs can be found at and are built
Rust documentation is hosted on . You can build them locally with: `cargo doc --all-features --no-deps --open`
## Build and install the Rerun Python SDK
-Rerun is available as a package on PyPi and can be installed with `pip install rerun-sdk`
+
+Rerun is available as a package on PyPi and can be installed with `pip install depthai-viewer`
Additionally, prebuilt dev wheels from head of main are available at .
@@ -64,18 +65,19 @@ From here on out, we assume you have this virtualenv activated.
### Build and install
You need to setup your build environment once with
+
```sh
./scripts/setup.sh
```
Then install the Rerun SDK with:
+
```
pip install ./rerun_py
```
> Note: If you are unable to upgrade pip to version `>=21.3`, you need to pass `--use-feature=in-tree-build` to the `pip install` command.
-
## Improving compile times
As of today, we link everything statically in both debug and release builds, which makes custom linkers and split debuginfo the two most impactful tools we have at our disposal in order to improve compile times.
@@ -87,9 +89,11 @@ These tools can configured through your `Cargo` configuration, available at `$HO
On x64 macOS, use the [zld](https://github.com/michaeleisel/zld) linker and keep debuginfo in a single separate file.
Pre-requisites:
+
- Install [zld](https://github.com/michaeleisel/zld): `brew install michaeleisel/zld/zld`.
`config.toml` (x64):
+
```toml
[target.x86_64-apple-darwin]
rustflags = [
@@ -103,6 +107,7 @@ rustflags = [
On Apple-silicon Mac (M1, M2), the default settings are already pretty good. The default linker is just as good as `zld`. Do NOT set `split-debuginfo=packed`, as that will make linking a lot slower. You can set `split-debuginfo=unpacked` for a small improvement.
`config.toml` (M1, M2):
+
```toml
[target.aarch64-apple-darwin]
rustflags = [
@@ -116,9 +121,11 @@ rustflags = [
On Linux, use the [mold](https://github.com/rui314/mold) linker and keep DWARF debuginfo in separate files.
Pre-requisites:
+
- Install [mold](https://github.com/rui314/mold) through your package manager.
`config.toml`:
+
```toml
[target.x86_64-unknown-linux-gnu]
linker = "clang"
@@ -135,13 +142,16 @@ rustflags = [
On Windows, use LLVM's `lld` linker and keep debuginfo in a single separate file.
Pre-requisites:
+
- Install `lld`:
+
```
cargo install -f cargo-binutils
rustup component add llvm-tools-preview
```
`config.toml`:
+
```toml
[target.x86_64-pc-windows-msvc]
linker = "rust-lld.exe"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a7093067a19..2a840fe9a9b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,347 +1,5 @@
-# Rerun changelog
+# Depthai Viewer changelog
+## 0.0.1
-## [Unreleased](https://github.com/rerun-io/rerun/compare/latest...HEAD)
-β¦
-
-## [0.4.0](https://github.com/rerun-io/rerun/compare/v0.3.1...v0.4.0) - Outlines, web viewer and performance improvements
-
-https://user-images.githubusercontent.com/1220815/228241887-03b311e2-80e9-4541-9281-6d334a15ab04.mp4
-
-
-## Overview & Highlights
-* Add support for mesh vertex colors [#1671](https://github.com/rerun-io/rerun/pull/1671)
-* Lower memory use [#1535](https://github.com/rerun-io/rerun/pull/1535)
-* Improve garbage collection [#1560](https://github.com/rerun-io/rerun/pull/1560)
-* Improve the web viewer [#1596](https://github.com/rerun-io/rerun/pull/1596) [#1594](https://github.com/rerun-io/rerun/pull/1594) [#1682](https://github.com/rerun-io/rerun/pull/1682) [#1716](https://github.com/rerun-io/rerun/pull/1716) β¦
-* Nice outlines when hovering/selecting
-* Add an example of forever-streaming a web-camera image to Rerun [#1502](https://github.com/rerun-io/rerun/pull/1502)
-* Fix crash-on-save on some versions of Linux [#1402](https://github.com/rerun-io/rerun/pull/1402)
-* And a lot of other bug fixes
-* Many performance improvements
-
-We now host an experimental and unpolished web-viewer at for anyone to try out!
-
-### In Detail
-
-#### π Python SDK
-- Expose all Rerun enums and types to main module scope [#1598](https://github.com/rerun-io/rerun/pull/1598)
-- Make `log_point` more forgiving and update docstring [#1663](https://github.com/rerun-io/rerun/pull/1663)
-- Add support for mesh vertex colors [#1671](https://github.com/rerun-io/rerun/pull/1671)
-
-#### π¦ Rust SDK
-- β οΈ `Session::new` has been replaced with `SessionBuilder` [#1528](https://github.com/rerun-io/rerun/pull/1528)
-- β οΈ `session.spawn(β¦)` -> `rerun::native_viewer::spawn(session, β¦)` [#1507](https://github.com/rerun-io/rerun/pull/1507)
-- β οΈ `session.show()` -> `rerun::native_viewer::show(session)` [#1507](https://github.com/rerun-io/rerun/pull/1507)
-- β οΈ `session.serve(β¦)` -> `rerun::serve_web_viewer(session, β¦);` [#1507](https://github.com/rerun-io/rerun/pull/1507)
-- β οΈ `rerun::global_session` is now hidden behind the `global_session` feature flag [#1507](https://github.com/rerun-io/rerun/pull/1507)
-- Add support for mesh vertex colors [#1671](https://github.com/rerun-io/rerun/pull/1671)
-
-#### πͺ³ Bug Fixes
-- datastore: disable compaction (fixes 2x memory issue) [#1535](https://github.com/rerun-io/rerun/pull/1535)
-- Fix garbage collection [#1560](https://github.com/rerun-io/rerun/pull/1560)
-- Avoid using undefined extern "C" on windows [#1577](https://github.com/rerun-io/rerun/pull/1577)
-- Fix crash on decoding old .rrd files [#1579](https://github.com/rerun-io/rerun/pull/1579)
-- datastore: stabilize dataframe sorts [#1549](https://github.com/rerun-io/rerun/pull/1549)
-- Stop using infinities in wgsl shaders [#1594](https://github.com/rerun-io/rerun/pull/1594)
-- Workaround for alpha to coverage state leaking on (Web)GL renderer [#1596](https://github.com/rerun-io/rerun/pull/1596)
-- Use a patched `wasm-bindgen-cli` with fix for 2GiB bug [#1605](https://github.com/rerun-io/rerun/pull/1605)
-- Misc: make example in `log_pinhole` runable [#1609](https://github.com/rerun-io/rerun/pull/1609) (thanks [@Sjouks](https://github.com/Sjouks)!)
-- Early-out on zero-sized space-views to prevent crashes [#1623](https://github.com/rerun-io/rerun/pull/1623)
-- Print our own callstack on panics [#1622](https://github.com/rerun-io/rerun/pull/1622)
-- Handle ctrl+c to gracefully shutdown the server(s) [#1613](https://github.com/rerun-io/rerun/pull/1613)
-- Fix crash on serve exit, second attempt [#1633](https://github.com/rerun-io/rerun/pull/1633)
-- Fix wrong remove-tooltip for entities and groups [#1637](https://github.com/rerun-io/rerun/pull/1637)
-- Fix requiring requiring focus for shutdown via ctrl+c when starting viewer from command line [#1646](https://github.com/rerun-io/rerun/pull/1646)
-- Fix eye spin after eye reset [#1652](https://github.com/rerun-io/rerun/pull/1652)
-- Fix crash on negative radii by instead warning [#1654](https://github.com/rerun-io/rerun/pull/1654)
-- Fix crash when trying to listen on a taken TCP port [#1650](https://github.com/rerun-io/rerun/pull/1650)
-- Don't show 2D labels in 3D space views. [#1641](https://github.com/rerun-io/rerun/pull/1641)
-- Fix Z fighting with improved depth offset math [#1661](https://github.com/rerun-io/rerun/pull/1661)
-- Whether a spatial view is 2d or 3d is now reevaluated over time unless picked explicitly [#1660](https://github.com/rerun-io/rerun/pull/1660)
-- Update wgpu to v0.15.3, fixing meshes on Windows Chrome [#1682](https://github.com/rerun-io/rerun/pull/1682)
-- Fix a bug in the image hover code, causing the wrong RGBA values to be printed [#1690](https://github.com/rerun-io/rerun/pull/1690)
-- Fix a bug that caused points to be render too large [#1690](https://github.com/rerun-io/rerun/pull/1690)
-- Fix web crash on missing uniform buffer padding [#1699](https://github.com/rerun-io/rerun/pull/1699)
-- Fix `memory_usage` example relying on implicit recursive features [#1709](https://github.com/rerun-io/rerun/pull/1709)
-- Track changed state in nav mode combo box [#1703](https://github.com/rerun-io/rerun/pull/1703)
-- Fix crash-on-save by switching file-picker dialog to `xdg-portal` [#1402](https://github.com/rerun-io/rerun/pull/1402)
-- Change roll-shortcut from ALT to SHIFT [#1715](https://github.com/rerun-io/rerun/pull/1715)
-- Fix CpuWriteGpuReadBelt producing unaligned gpu buffer offsets [#1716](https://github.com/rerun-io/rerun/pull/1716)
-- Fix arrows requiring a radius to be visible [#1720](https://github.com/rerun-io/rerun/pull/1720)
-
-#### π Performance Improvements
-- Add re_arrow_store profile scopes [#1546](https://github.com/rerun-io/rerun/pull/1546)
-- datastore: early exit missing components at table level [#1554](https://github.com/rerun-io/rerun/pull/1554)
-- datastore: track bucket count in store stats & mem panel [#1555](https://github.com/rerun-io/rerun/pull/1555)
-- LogDb: dont split on index bucket size [#1558](https://github.com/rerun-io/rerun/pull/1558)
-- Introduce a simpler cache dedicated to just decode JPEGs [#1550](https://github.com/rerun-io/rerun/pull/1550)
-- Implement outlines for points 2d/3d/depth & use them for select & hover in Viewer [#1568](https://github.com/rerun-io/rerun/pull/1568)
-- Simplify ImageCache [#1551](https://github.com/rerun-io/rerun/pull/1551)
-- New time panel density graph [#1557](https://github.com/rerun-io/rerun/pull/1557)
-- Refactor the Arrow Mesh3D type to use zero-copy Buffers [#1691](https://github.com/rerun-io/rerun/pull/1691)
-- Remove the redundant costly transform check during categorization [#1695](https://github.com/rerun-io/rerun/pull/1695)
-- batching 3: `DataRow` & `DataTable` + no bundles outside of transport [#1673](https://github.com/rerun-io/rerun/pull/1673)
-
-#### π§βπ« Examples
-- Very simple example streaming from an opencv camera [#1502](https://github.com/rerun-io/rerun/pull/1502)
-- Initial TurtleBot subscriber demo [#1523](https://github.com/rerun-io/rerun/pull/1523)
-
-#### π Docs
-- Link to the Python SDK build instructions in `rerun_py/README.md` [#1565](https://github.com/rerun-io/rerun/pull/1565)
-
-#### πΌ UI Improvements
-- Fix combining outline mask for selection & hover [#1552](https://github.com/rerun-io/rerun/pull/1552)
-- Implement outlines for rectangles & use them for select & hover of image primitives in Viewer [#1559](https://github.com/rerun-io/rerun/pull/1559)
-- Show log messages in egui toast notifications [#1603](https://github.com/rerun-io/rerun/pull/1603)
-- Adapt UI for smaller screens [#1608](https://github.com/rerun-io/rerun/pull/1608)
-- Nicer toast notifications [#1621](https://github.com/rerun-io/rerun/pull/1621)
-- Don't hover things in 2D/3D views if we are dragging something [#1643](https://github.com/rerun-io/rerun/pull/1643)
-- Allow rolling 3D camera with primary mouse button + alt modifier [#1659](https://github.com/rerun-io/rerun/pull/1659)
-- Name space views after the space and indicate duplicate names [#1653](https://github.com/rerun-io/rerun/pull/1653)
-- Add banner about mobile browsers being unsupported [#1674](https://github.com/rerun-io/rerun/pull/1674)
-- Improve ui for tensors and color map selection [#1683](https://github.com/rerun-io/rerun/pull/1683)
-- Only show the mobile OS warning banner on web [#1685](https://github.com/rerun-io/rerun/pull/1685)
-- Improve the depth backprojection feature [#1690](https://github.com/rerun-io/rerun/pull/1690)
-- Swap overlay order of selection & hover outlines [#1705](https://github.com/rerun-io/rerun/pull/1705)
-- Turn on depth cloud backprojection by default [#1710](https://github.com/rerun-io/rerun/pull/1710)
-- Add radius boost for depth clouds on outline [#1713](https://github.com/rerun-io/rerun/pull/1713)
-
-#### π€·ββοΈ Other Viewer Improvements
-- Fix web feature name in error messages [#1521](https://github.com/rerun-io/rerun/pull/1521)
-- Use outlines for mesh selections instead of highlight colors [#1540](https://github.com/rerun-io/rerun/pull/1540)
-- Implement outlines for line renderer & use them for select & hover of "line-like" primitives in Viewer [#1553](https://github.com/rerun-io/rerun/pull/1553)
-- Load .rrd file over HTTP [#1600](https://github.com/rerun-io/rerun/pull/1600)
-- Revert "Handle ctrl+c to gracefully shutdown the server(s)" [#1632](https://github.com/rerun-io/rerun/pull/1632)
-- More eager GC, and remove `--fast-math` optimization for wasm [#1656](https://github.com/rerun-io/rerun/pull/1656)
-- Detect failure to install GUI log callback [#1655](https://github.com/rerun-io/rerun/pull/1655)
-- Warn when most of the RAM has been used up by Rerun [#1651](https://github.com/rerun-io/rerun/pull/1651)
-- Apply color maps to all types of depth tensors [#1686](https://github.com/rerun-io/rerun/pull/1686)
-- Size boosted outlines for points & lines, color & size tweaking [#1667](https://github.com/rerun-io/rerun/pull/1667)
-- Default point radius to 1.5 ui points [#1706](https://github.com/rerun-io/rerun/pull/1706)
-- When streaming an rrd from http: play it, don't follow it [#1707](https://github.com/rerun-io/rerun/pull/1707)
-
-#### πΈοΈ Web
-- Use `log` as our log backend instead of `tracing` [#1590](https://github.com/rerun-io/rerun/pull/1590)
-- Turn on allocation tracker at run-time and for web [#1591](https://github.com/rerun-io/rerun/pull/1591)
-- Set correct MIME types in re_web_viewer_server [#1602](https://github.com/rerun-io/rerun/pull/1602)
-- Upload web viewer to a bucket [#1606](https://github.com/rerun-io/rerun/pull/1606)
-- Use hostname for default websocket address [#1664](https://github.com/rerun-io/rerun/pull/1664)
-- Upload the colmap rrd file to gcloud [#1666](https://github.com/rerun-io/rerun/pull/1666)
-- Show a warning by default on mobile browsers [#1670](https://github.com/rerun-io/rerun/pull/1670)
-- Add analytics to the hosted index.html [#1675](https://github.com/rerun-io/rerun/pull/1675)
-- Always upload latest prerelease to a dedicated prefix [#1676](https://github.com/rerun-io/rerun/pull/1676)
-- Allow url param override on app.rerun.io [#1678](https://github.com/rerun-io/rerun/pull/1678)
-- Show the git commit in the about section in pre-release builds [#1677](https://github.com/rerun-io/rerun/pull/1677)
-- Update the web icon [#1688](https://github.com/rerun-io/rerun/pull/1688)
-
-#### π¨ Renderer Improvements
-- Outlines via masking & postprocessing in `re_renderer` [#1532](https://github.com/rerun-io/rerun/pull/1532)
-- Add missing profiling scopes in `re_renderer` [#1567](https://github.com/rerun-io/rerun/pull/1567)
-- Don't call `wgpu::Device::poll` on the web [#1626](https://github.com/rerun-io/rerun/pull/1626)
-- Merge final outline render into composite step in order to fix blending [#1629](https://github.com/rerun-io/rerun/pull/1629)
-- renderer: fix the groupby logic in mesh instancing [#1657](https://github.com/rerun-io/rerun/pull/1657)
-- Fix outlines being offset diagonally by about half a pixel [#1668](https://github.com/rerun-io/rerun/pull/1668)
-- Gpu readback belt for fast & easy data readback from gpu [#1687](https://github.com/rerun-io/rerun/pull/1687)
-- Make CpuWriteGpuReadBelt texture copies easier/less error prone [#1689](https://github.com/rerun-io/rerun/pull/1689)
-
-#### β¨ Other Enhancement
-- datastore: split out formatting & sanity checks in their own modules [#1625](https://github.com/rerun-io/rerun/pull/1625)
-- Add `rerun --save`: stream incoming log stream to an rrd file [#1662](https://github.com/rerun-io/rerun/pull/1662)
-- batching 1: introduce `DataCell` & retire `ComponentBundle` [#1634](https://github.com/rerun-io/rerun/pull/1634)
-- Data store batching 2: split out component traits [#1636](https://github.com/rerun-io/rerun/pull/1636)
-
-#### π Analytics
-- Analytics: don't spam warning when there is an HTTP connection problem [#1564](https://github.com/rerun-io/rerun/pull/1564)
-- Analytics: Rename "location" to "file_line" in the "crash-panic" event [#1575](https://github.com/rerun-io/rerun/pull/1575)
-
-#### π£ Merged RFCs
-- RFC: component-datatype conversions [#1595](https://github.com/rerun-io/rerun/pull/1595)
-- RFC: pre-proposal for blueprint store [#1582](https://github.com/rerun-io/rerun/pull/1582)
-
-#### π§βπ» Dev-experience
-- Update `rayon` [#1541](https://github.com/rerun-io/rerun/pull/1541)
-- Fix some `1.68` clippy lints [#1569](https://github.com/rerun-io/rerun/pull/1569)
-- Remove duplicated 'nix' crate [#1479](https://github.com/rerun-io/rerun/pull/1479)
-- Better MsgId format [#1566](https://github.com/rerun-io/rerun/pull/1566)
-- Lint vertical spacing in Rust code [#1572](https://github.com/rerun-io/rerun/pull/1572)
-- CI: Replace wasm_bindgen_check.sh with actually building the web-viewer [#1604](https://github.com/rerun-io/rerun/pull/1604)
-- Add --all-features to Rust Analyzer flags [#1624](https://github.com/rerun-io/rerun/pull/1624)
-- Run clippy for wasm, with own clippy.toml config file [#1628](https://github.com/rerun-io/rerun/pull/1628)
-- Update tokio v1.24.1 -> v1.26.0 [#1635](https://github.com/rerun-io/rerun/pull/1635)
-- Add a workflow input for running benchmarks manually [#1698](https://github.com/rerun-io/rerun/pull/1698)
-- Add missing } to fix rust workflow [#1700](https://github.com/rerun-io/rerun/pull/1700)
-- Fix `lint.py` [#1719](https://github.com/rerun-io/rerun/pull/1719)
-- Add a script that generates a changelog from recent PRs and their labels [#1718](https://github.com/rerun-io/rerun/pull/1718)
-
-#### π€·ββοΈ Other
-- Clean up opencv_canny example slightly [b487e550dcb87225858dc6f76b791a25e938e75e](https://github.com/rerun-io/rerun/commit/b487e550dcb87225858dc6f76b791a25e938e75e)
-- Lint fixes [9901e7c6735356b1970ddabc926bc5378d82e057](https://github.com/rerun-io/rerun/commit/9901e7c6735356b1970ddabc926bc5378d82e057)
-
-
-## [0.3.1](https://github.com/rerun-io/rerun/compare/v0.3.0...v0.3.1) - Remove potentially sensitive analytics
-
-Remove potentially sensitive analytics, including path to rerun source code on panics, and rerun branch name when building from source [#1563](https://github.com/rerun-io/rerun/pull/1563)
-
-
-## [0.3.0](https://github.com/rerun-io/rerun/compare/v0.2.0...v0.3.0)
-### Overview & Highlights
-
-After a successful launch a couple of weeks ago, we're back with our second release!
-With a few exceptions this release focuses on internal refactors & improving our processes.
-However, we think you'll enjoy these goodies that made it in nonetheless!
-
-https://user-images.githubusercontent.com/2910679/222510504-23871b8c-0bef-49c2-bbd2-37baab4247e8.mp4
-
-
-You can now generate point clouds directly from depth textures and choose a wide variety of color maps.
-Check out this [video](https://user-images.githubusercontent.com/1220815/223365363-da13585f-3a91-4cb8-a6ef-8a6fadbeb4eb.webm) on how to use it.
-This is **a lot** faster and more convenient than doing so manually in your own code
-Some caveats: Picking is not yet working and visible history may behave differently (related to [#723](https://github.com/rerun-io/rerun/issues/723))
-
-Other highlights:
-
-* Viewer
- * Improved formatting of date-times in plots [#1356](https://github.com/rerun-io/rerun/pull/1356)
- * Labels for 3D objects have now a color can now be selected & hovered [#1438](https://github.com/rerun-io/rerun/pull/1438)
- * Scale factor is saved across sessions and more persistent between screens [#1448](https://github.com/rerun-io/rerun/pull/1448)
- * Showing tensors in the viewer is now faster
-* SDK
- * Python packages now work with Ubuntu-20.04 [#1334](https://github.com/rerun-io/rerun/pull/1334)
- * u8 segmentation stay u8 now (they converted to u16 before) [#1376](https://github.com/rerun-io/rerun/pull/1376)
- * 2D Line strips can now be logged directly [#1430](https://github.com/rerun-io/rerun/pull/1430)
- * Add a `strict` mode to the Python SDK where misuses of the API result in exceptions being raised.[#1477](https://github.com/rerun-io/rerun/pull/1477)
- * Fix disabling Python API through `init` not working [#1517](https://github.com/rerun-io/rerun/pull/1517)
-* General
- * We build now with fewer build dependencies (there is however [still more work to do!](https://github.com/rerun-io/rerun/issues/1316)).
- Notably, we previously used a version of the `time` crate which had a security issue (CVE-2020-26235), thanks @mpizenberg for helping out!
- * Print more information & troubleshooting info on crash
-
-Meanwhile, we did a bunch of improvements to our manual. If you had trouble running Rerun so far, check our updated [troubleshooting](https://www.rerun.io/docs/getting-started/troubleshooting) page (and as always, please [open an issue](https://github.com/rerun-io/rerun/issues/new/choose) if something doesn't work).
-
-β οΈ BREAKING: old `.rrd` files no longer load β οΈ
-
-### In Detail
-#### New Features
-* Generate point clouds directly from depth textures
- * re_renderer: implement depth cloud renderer [#1415](https://github.com/rerun-io/rerun/pull/1415)
- * Integrate depth clouds into Rerun [#1421](https://github.com/rerun-io/rerun/pull/1421)
- * CPU & GPU color maps [#1484](https://github.com/rerun-io/rerun/pull/1484)
- * Integrate GPU color maps into depth clouds [#1486](https://github.com/rerun-io/rerun/pull/1486)
-* Python SDK: Add strict mode [#1477](https://github.com/rerun-io/rerun/pull/1477)
-* OS independent Zoom factor & serialization thereof [#1448](https://github.com/rerun-io/rerun/pull/1448)
-* Labels for 3D objects have now a color can now be selected & hovered [#1438](https://github.com/rerun-io/rerun/pull/1438)
-* Add 2d support for linestrips [#1430](https://github.com/rerun-io/rerun/pull/1430)
-* Add signal handler on *nix with troubleshooting and stacktrace [#1340](https://github.com/rerun-io/rerun/pull/1340)
- * Point users to our troubleshooting page on panic [#1338](https://github.com/rerun-io/rerun/pull/1338)
-
-#### Performance
-* Speed up conversions for color arrays in Python [#1454](https://github.com/rerun-io/rerun/pull/1454)
-* Speed up fixed-sized array iteration [#1050](https://github.com/rerun-io/rerun/pull/1050)
-* Speed up tensor handling by padding data through more directly
- * Direct conversion to dynamic image from Tensors [#1455](https://github.com/rerun-io/rerun/pull/1455)
- * Convert view_tensor to use the new native Tensors [#1439](https://github.com/rerun-io/rerun/pull/1439)
-* Add option to show performance metrics in the UI in release builds too [#1444](https://github.com/rerun-io/rerun/pull/1444)
-* Faster stable diffusion sample [#1364](https://github.com/rerun-io/rerun/pull/1364)
-* SDK: stream to disk with `save` feature [#1405](https://github.com/rerun-io/rerun/pull/1405)
-* `re_renderer` has now a direct CPU->GPU copy mechanism
- * `CpuWriteGpuReadBelt` for fast frame by frame memory transfers [#1382](https://github.com/rerun-io/rerun/pull/1382)
- * Uniform buffer utility using `CpuWriteGpuReadBelt` [#1400](https://github.com/rerun-io/rerun/pull/1400)
- * Use `CpuWriteGpuReadBelt` for mesh data gpu upload [#1416](https://github.com/rerun-io/rerun/pull/1416)
-
-#### Small improvements & Bugfixes
-* UI
- * Add scroll-bars the "Add/Remove entities" window [#1445](https://github.com/rerun-io/rerun/pull/1445)
- * Unify the time formatting between the time panel and the plot [#1369](https://github.com/rerun-io/rerun/pull/1369)
- * Timeline
- * Fix precision issue when zooming in on the timeline [#1370](https://github.com/rerun-io/rerun/pull/1370)
- * Improve the gap-detector [#1363](https://github.com/rerun-io/rerun/pull/1363)
- * Better time axis on plot view [#1356](https://github.com/rerun-io/rerun/pull/1356)
- * Prevent wrap on 'Streams' text [#1308](https://github.com/rerun-io/rerun/pull/1308)
- * Update to eframe 0.21.3 with fix for web text input [#1311](https://github.com/rerun-io/rerun/pull/1311)
-* `re_renderer`
- * Fix crash due to always expecting Rgba8Unorm backbuffer on Web & Bgra8Unorm on native [#1413](https://github.com/rerun-io/rerun/pull/1413)
- * Allow controlling the graphics backend & power preference through standard wgpu env vars [#1332](https://github.com/rerun-io/rerun/pull/1332)
-* Heuristic for camera frustum length is now based on scene size [#1433](https://github.com/rerun-io/rerun/pull/1433)
-* Fix python type signature for tensor names [#1443](https://github.com/rerun-io/rerun/pull/1443)
-* Don't convert u8 segmentation images to u16 [#1376](https://github.com/rerun-io/rerun/pull/1376)
-* Docs (excluding the manual)
- * Improve the docs of `connect` and `serve` [#1450](https://github.com/rerun-io/rerun/pull/1450)
- * Update log_mesh and log_meshes docs. [#1286](https://github.com/rerun-io/rerun/pull/1286)
- * Add guidelines for adding dependencies in a PR [#1431](https://github.com/rerun-io/rerun/pull/1431)
- * Add a few more sections to `CODE_STYLE.md` [#1365](https://github.com/rerun-io/rerun/pull/1365)
- * Fixup for some doc links [#1314](https://github.com/rerun-io/rerun/pull/1314)
- * Document undocumented environment variables on help page. [#1335](https://github.com/rerun-io/rerun/pull/1335)
- * Link to SDK operating modes doc in both SDK [#1330](https://github.com/rerun-io/rerun/pull/1330)
-* More information in `--version` [#1388](https://github.com/rerun-io/rerun/pull/1388)
-* Remove already broken `show` method from Python SDK [#1429](https://github.com/rerun-io/rerun/pull/1429)
-* Analytics
- * Send analytics events with callstacks on panics and signals [#1409](https://github.com/rerun-io/rerun/pull/1409)
- * Put all analytics to one bucket [#1390](https://github.com/rerun-io/rerun/pull/1390)
- * add event for when we serve the web-viewer .wasm [#1379](https://github.com/rerun-io/rerun/pull/1379)
- * register SDK language and data source [#1371](https://github.com/rerun-io/rerun/pull/1371)
- * Refactor analytics [#1368](https://github.com/rerun-io/rerun/pull/1368)
-* Versioned log streams streams [#1420](https://github.com/rerun-io/rerun/pull/1420)
-* Fix path issues when running debug viewer within workspace [#1341](https://github.com/rerun-io/rerun/pull/1341)
-* Detailed errors for re_renderer `include_file!` [#1339](https://github.com/rerun-io/rerun/pull/1339)
-* Limit logging in web-viewer to `warn` in order to workaround a crash issue (and reduce log spam) [1514](https://github.com/rerun-io/rerun/pull/1514)
-* Fix disabling API through `init` not working [#1517](https://github.com/rerun-io/rerun/pull/1517)
-
-#### CI, Testing & Build improvements
-* Reduce build dependencies
- * Get rid of time 0.1.* dependency [#1408](https://github.com/rerun-io/rerun/pull/1408)
- * Remove unnecessary ordered-float [#1461](https://github.com/rerun-io/rerun/pull/1461)
- * Remove extraneous `image` features and dependencies [#1425](https://github.com/rerun-io/rerun/pull/1425)
- * Replace `reqwest` with `ureq` [#1407](https://github.com/rerun-io/rerun/pull/1407)
- * Remove derive_more dependency [#1406](https://github.com/rerun-io/rerun/pull/1406)
-* Use different artifact names for wasm/js in debug builds [#1428](https://github.com/rerun-io/rerun/pull/1428)
-* Separate mac wheels & trigger wheel build from ui [#1499](https://github.com/rerun-io/rerun/pull/1499)
-* Add spell checking to CI [#1492](https://github.com/rerun-io/rerun/pull/1492)
-* Repo size
- * Always create new orphaned branch for gh-pages [#1490](https://github.com/rerun-io/rerun/pull/1490)
- * GitHub Action to prevent large files [#1478](https://github.com/rerun-io/rerun/pull/1478)
-* Python
- * Remove the python job path filters [#1452](https://github.com/rerun-io/rerun/pull/1452)
- * Use ruff for our python lints [#1378](https://github.com/rerun-io/rerun/pull/1378)
- * Use python3 in the jobs that weren't tested in PR [#1348](https://github.com/rerun-io/rerun/pull/1348)
-* Testing
- * Add a test of memory use when logging a lot of big images [#1372](https://github.com/rerun-io/rerun/pull/1372)
-* Switch ci_docker to a container based on ubuntu 20.04 [#1334](https://github.com/rerun-io/rerun/pull/1334)
-* Release handling
- * Switch release action to ncipollo [#1489](https://github.com/rerun-io/rerun/pull/1489)
- * Fix our continuous pre-releases [#1458](https://github.com/rerun-io/rerun/pull/1458)
- * Delete the prerelease before creating the new one [#1485](https://github.com/rerun-io/rerun/pull/1485)
- * Set prerelease to true even for version-tagged CI job [#1504](https://github.com/rerun-io/rerun/pull/1504)
- * Let the release job take care of creating the tag [#1501](https://github.com/rerun-io/rerun/pull/1501)
- * Use `cargo update -w` instead of `cargo check` when prepping prerelease [#1500](https://github.com/rerun-io/rerun/pull/1500)
- * Use prerelease tag instead of latest and update pointer on prerelease [#1481](https://github.com/rerun-io/rerun/pull/1481)
- * Include date in pre-release version [#1472](https://github.com/rerun-io/rerun/pull/1472)
- * Switch pre-release action to ncipollo/release-action [#1466](https://github.com/rerun-io/rerun/pull/1466)
-* Disallow some methods and types via Clippy[#1411](https://github.com/rerun-io/rerun/pull/1411)
-
-#### Other non-user-facing refactors
-* Fix: don't create a dummy LogDb when opening the Rerun Menu [#1440](https://github.com/rerun-io/rerun/pull/1440)
-* `re_renderer`
- * `Draw Phases` in preparation of executing `Renderer` several times on different targets [#1419](https://github.com/rerun-io/rerun/pull/1419)
- * Fix mesh creation failing to copy index data. [#1473](https://github.com/rerun-io/rerun/pull/1473)
- * do not silently drop draw phases [#1471](https://github.com/rerun-io/rerun/pull/1471)
- * Simplify bind group allocation call by passing pool collection object. [#1459](https://github.com/rerun-io/rerun/pull/1459)
- * Interior mutable buffer/texture/bindgroup pools [#1374](https://github.com/rerun-io/rerun/pull/1374)
- * Rename all instances of `frame_maintenance` to `begin_frame` [#1360](https://github.com/rerun-io/rerun/pull/1360)
- * Texture & buffer call now wgpu's `destroy` on removal from pool [#1359](https://github.com/rerun-io/rerun/pull/1359)
- * Arrow buffers as (optional) first-class citizen [#1482](https://github.com/rerun-io/rerun/pull/1482)
- * Log static re_renderer resource generation [#1464](https://github.com/rerun-io/rerun/pull/1464)
-* Internal log_text_entry_internal to break circular deps [#1488](https://github.com/rerun-io/rerun/pull/1488)
-* Delete ClassicTensor and cleanup [#1456](https://github.com/rerun-io/rerun/pull/1456)
-* Fix re_renderer file watcher watching the same file several times [#1463](https://github.com/rerun-io/rerun/pull/1463)
-* Analytics
- * More ergonomic API [#1410](https://github.com/rerun-io/rerun/pull/1410)
- * Streamlining host vs. recorder python/rust versions [#1380](https://github.com/rerun-io/rerun/pull/1380)
- * Fix workspace detection [#1437](https://github.com/rerun-io/rerun/pull/1437)
-* Introduce `DeserializableComponent` trait and high-level `query_latest` [#1417](https://github.com/rerun-io/rerun/pull/1417)
-
-
-[Full Changelog](https://github.com/rerun-io/rerun/compare/v0.2.0...v0.3.0)
-
-## 0.2.0
-First public release!
+Beta release of the new Depthai Viewer.
diff --git a/Cargo.lock b/Cargo.lock
index 60f09380c940..681b3ab79782 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -20,9 +20,9 @@ checksum = "330223a1aecc308757b9926e9391c9b47f8ef2dbd8aea9df88312aea18c5e8d6"
[[package]]
name = "accesskit"
-version = "0.9.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4803cf8c252f374ae6bfbb341e49e5a37f7601f2ce74a105927a663eba952c67"
+checksum = "704d532b1cd3d912bb37499c55a81ac748cc1afa737eedd100ba441acdd47d38"
dependencies = [
"enumn",
"serde",
@@ -84,7 +84,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4165a1aef703232031b40a6e8908c2f9e314d495f11aa7f98db75d39a497cc6a"
dependencies = [
"android-properties",
- "bitflags",
+ "bitflags 1.3.2",
"cc",
"jni-sys",
"libc",
@@ -124,16 +124,16 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]]
name = "api_demo"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"anyhow",
"clap 4.1.4",
+ "depthai-viewer",
"glam",
"itertools",
"ndarray",
"ndarray-rand",
"rand",
- "rerun",
]
[[package]]
@@ -298,6 +298,21 @@ dependencies = [
"slab",
]
+[[package]]
+name = "async-global-executor"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "blocking",
+ "futures-lite",
+ "once_cell",
+]
+
[[package]]
name = "async-io"
version = "1.13.0"
@@ -339,6 +354,32 @@ dependencies = [
"syn 1.0.103",
]
+[[package]]
+name = "async-std"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
+dependencies = [
+ "async-channel",
+ "async-global-executor",
+ "async-io",
+ "async-lock",
+ "crossbeam-utils",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "once_cell",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
[[package]]
name = "async-stream"
version = "0.3.3"
@@ -377,6 +418,12 @@ dependencies = [
"syn 1.0.103",
]
+[[package]]
+name = "atomic-waker"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
+
[[package]]
name = "atomic_refcell"
version = "0.1.8"
@@ -467,6 +514,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+[[package]]
+name = "bitflags"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813"
+
[[package]]
name = "block"
version = "0.1.6"
@@ -501,6 +554,21 @@ dependencies = [
"objc2-encode",
]
+[[package]]
+name = "blocking"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65"
+dependencies = [
+ "async-channel",
+ "async-lock",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "log",
+]
+
[[package]]
name = "bumpalo"
version = "3.11.1"
@@ -572,12 +640,13 @@ dependencies = [
[[package]]
name = "cargo-run-wasm"
-version = "0.2.0"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "611b811fad83eebfcdcf47ae1e425c82d1249608bc571d537448d706be08cf27"
+checksum = "cc1e37cf14ef470ed74ec2a8b95e51b8623bcf6f76d24f233ebaeb209f766230"
dependencies = [
"devserver_lib",
"pico-args",
+ "serde_json",
"wasm-bindgen-cli-support",
]
@@ -676,7 +745,7 @@ version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"clap_lex 0.2.4",
"indexmap",
"textwrap",
@@ -688,7 +757,7 @@ version = "4.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"clap_derive",
"clap_lex 0.3.0",
"is-terminal",
@@ -751,7 +820,7 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"block",
"cocoa-foundation",
"core-foundation",
@@ -767,7 +836,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"block",
"core-foundation",
"core-graphics-types",
@@ -907,7 +976,7 @@ version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"core-foundation",
"core-graphics-types",
"foreign-types",
@@ -920,7 +989,7 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"core-foundation",
"foreign-types",
"libc",
@@ -1053,7 +1122,7 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"crossterm_winapi",
"libc",
"mio",
@@ -1088,6 +1157,16 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "ctor"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+dependencies = [
+ "quote",
+ "syn 1.0.103",
+]
+
[[package]]
name = "ctrlc"
version = "3.2.2"
@@ -1098,12 +1177,6 @@ dependencies = [
"winapi",
]
-[[package]]
-name = "cty"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
-
[[package]]
name = "cxx"
version = "1.0.82"
@@ -1151,10 +1224,9 @@ dependencies = [
[[package]]
name = "d3d12"
version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da"
+source = "git+https://github.com/gfx-rs/d3d12-rs?rev=b940b1d71#b940b1d71ab7083ae80eec697872672dc1f2bd32"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"libloading",
"winapi",
]
@@ -1193,6 +1265,38 @@ dependencies = [
"syn 1.0.103",
]
+[[package]]
+name = "depthai-viewer"
+version = "0.6.0-alpha.0"
+dependencies = [
+ "anyhow",
+ "backtrace",
+ "clap 4.1.4",
+ "ctrlc",
+ "document-features",
+ "itertools",
+ "libc",
+ "mimalloc",
+ "parking_lot 0.12.1",
+ "re_analytics",
+ "re_build_build_info",
+ "re_build_info",
+ "re_data_store",
+ "re_format",
+ "re_log",
+ "re_log_encoding",
+ "re_log_types",
+ "re_memory",
+ "re_sdk",
+ "re_sdk_comms",
+ "re_smart_channel",
+ "re_viewer",
+ "re_web_viewer_server",
+ "re_ws_comms",
+ "tokio",
+ "webbrowser",
+]
+
[[package]]
name = "derivative"
version = "2.2.0"
@@ -1278,11 +1382,11 @@ dependencies = [
[[package]]
name = "dna"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
+ "depthai-viewer",
"itertools",
"rand",
- "rerun",
]
[[package]]
@@ -1309,8 +1413,7 @@ checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
[[package]]
name = "ecolor"
version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f99fe3cac305af9d6d92971af60d0f7ea4d783201ef1673571567b6699964d9"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
"bytemuck",
"serde",
@@ -1319,16 +1422,19 @@ dependencies = [
[[package]]
name = "eframe"
version = "0.21.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3df3ce60931e5f2d83bab4484d1a283908534d5308cc6b0c5c22c59cd15ee7cc"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
"bytemuck",
+ "cocoa",
"directories-next",
"egui",
"egui-wgpu",
"egui-winit",
"egui_glow",
+ "image",
"js-sys",
+ "log",
+ "objc",
"percent-encoding",
"pollster",
"puffin",
@@ -1336,39 +1442,38 @@ dependencies = [
"ron",
"serde",
"thiserror",
- "tracing",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu",
+ "winapi",
"winit",
]
[[package]]
name = "egui"
version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6412a21e0bde7c0918f7fb44bbbb86b5e1f88e63c026a4e747cc7af02f76dfbe"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
"accesskit",
"ahash 0.8.2",
"epaint",
+ "log",
"nohash-hasher",
"ron",
"serde",
- "tracing",
]
[[package]]
name = "egui-wgpu"
version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1678d8f30181193e78c15dddae16e6027c6058fdda9631950ade511b8a4b26f5"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
"bytemuck",
"epaint",
+ "log",
"puffin",
- "tracing",
+ "thiserror",
"type-map",
"wgpu",
"winit",
@@ -1377,17 +1482,16 @@ dependencies = [
[[package]]
name = "egui-winit"
version = "0.21.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab43597ba41f0ce39a364ad83185594578bfd8b3409b99dbcbb01df23afc3dbb"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
- "android-activity",
"arboard",
"egui",
"instant",
+ "log",
"puffin",
+ "raw-window-handle",
"serde",
"smithay-clipboard",
- "tracing",
"webbrowser",
"winit",
]
@@ -1405,27 +1509,25 @@ dependencies = [
[[package]]
name = "egui_extras"
version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f051342e97dfa2445107cb7d2e720617f5c840199b5cb4fe0ffcf481fcf5cce"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
"egui",
+ "log",
"serde",
- "tracing",
]
[[package]]
name = "egui_glow"
version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8257332fb168a965b3dca81d6a344e053153773c889cabdba0b3b76f1629ae81"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
"bytemuck",
"egui",
"egui-winit",
"glow",
+ "log",
"memoffset 0.6.5",
"puffin",
- "tracing",
"wasm-bindgen",
"web-sys",
]
@@ -1452,8 +1554,7 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "emath"
version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8ecd80612937e0267909d5351770fe150004e24dab93954f69ca62eecd3f77e"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
"bytemuck",
"serde",
@@ -1534,8 +1635,7 @@ dependencies = [
[[package]]
name = "epaint"
version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12e78b5c58a1f7f621f9d546add2adce20636422c9b251e29f749e8a2f713c95"
+source = "git+https://github.com/emilk/egui?rev=f76eefb98d23cbf71989255aafe75a07d343f6ed#f76eefb98d23cbf71989255aafe75a07d343f6ed"
dependencies = [
"ab_glyph",
"ahash 0.8.2",
@@ -1543,6 +1643,7 @@ dependencies = [
"bytemuck",
"ecolor",
"emath",
+ "log",
"nohash-hasher",
"parking_lot 0.12.1",
"serde",
@@ -1834,15 +1935,6 @@ dependencies = [
"slab",
]
-[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
[[package]]
name = "generic-array"
version = "0.14.6"
@@ -1899,11 +1991,23 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+[[package]]
+name = "gloo-timers"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "glow"
-version = "0.12.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8edf6019dff2d92ad27c1e3ff82ad50a0aea5b01370353cc928bfdc33e95925c"
+checksum = "4e007a07a24de5ecae94160f141029e9a347282cfe25d1d58d85d845cf3130f1"
dependencies = [
"js-sys",
"slotmap",
@@ -1955,7 +2059,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"gpu-alloc-types",
]
@@ -1965,7 +2069,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -1987,7 +2091,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"gpu-descriptor-types",
"hashbrown 0.12.3",
]
@@ -1998,7 +2102,7 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -2064,11 +2168,11 @@ dependencies = [
[[package]]
name = "hassle-rs"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90601c6189668c7345fc53842cb3f3a3d872203d523be1b3cb44a36a3e62fb85"
+checksum = "1397650ee315e8891a0df210707f0fc61771b0cc518c3023896064c5407cb3b0"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"com-rs",
"libc",
"libloading",
@@ -2282,7 +2386,7 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"inotify-sys",
"libc",
]
@@ -2429,10 +2533,19 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"libc",
]
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -2591,6 +2704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
+ "value-bag",
]
[[package]]
@@ -2689,7 +2803,7 @@ version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"block",
"core-graphics-types",
"foreign-types",
@@ -2708,9 +2822,9 @@ dependencies = [
[[package]]
name = "minimal"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
- "rerun",
+ "depthai-viewer",
]
[[package]]
@@ -2721,12 +2835,12 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "minimal_options"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"anyhow",
"clap 4.1.4",
+ "depthai-viewer",
"glam",
- "rerun",
]
[[package]]
@@ -2784,12 +2898,11 @@ dependencies = [
[[package]]
name = "naga"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5eafe22a23b797c9bc227c6c896419b26b5bb88fa903417a3adaed08778850d5"
+version = "0.12.0"
+source = "git+https://github.com/gfx-rs/naga?rev=b99d58ea435090e561377949f428bce2c18451bb#b99d58ea435090e561377949f428bce2c18451bb"
dependencies = [
"bit-set",
- "bitflags",
+ "bitflags 1.3.2",
"codespan-reporting",
"hexf-parse",
"indexmap",
@@ -2832,7 +2945,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"jni-sys",
"ndk-sys",
"num_enum",
@@ -2855,13 +2968,19 @@ dependencies = [
"jni-sys",
]
+[[package]]
+name = "never"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
+
[[package]]
name = "nix"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"cc",
"cfg-if",
"libc",
@@ -2874,7 +2993,7 @@ version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset 0.6.5",
@@ -2902,7 +3021,7 @@ version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@@ -3134,14 +3253,14 @@ dependencies = [
[[package]]
name = "objectron"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"anyhow",
"clap 4.1.4",
+ "depthai-viewer",
"glam",
"prost",
"prost-build",
- "rerun",
]
[[package]]
@@ -3354,7 +3473,7 @@ version = "0.17.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"crc32fast",
"flate2",
"miniz_oxide",
@@ -3381,7 +3500,7 @@ dependencies = [
"ahash 0.8.2",
"anyhow",
"arrow2",
- "bitflags",
+ "bitflags 1.3.2",
"chrono",
"comfy-table 6.1.4",
"hashbrown 0.13.1",
@@ -3621,6 +3740,31 @@ dependencies = [
"unindent",
]
+[[package]]
+name = "pyo3-asyncio"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3564762e37035cfc486228e10b0528460fa026d681b5763873c693aa0d5c260"
+dependencies = [
+ "futures",
+ "once_cell",
+ "pin-project-lite",
+ "pyo3",
+ "pyo3-asyncio-macros",
+ "tokio",
+]
+
+[[package]]
+name = "pyo3-asyncio-macros"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be72d4cd43a27530306bd0d20d3932182fbdd072c6b98d3638bc37efb9d559dd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.103",
+]
+
[[package]]
name = "pyo3-build-config"
version = "0.18.0"
@@ -3721,23 +3865,20 @@ checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6"
[[package]]
name = "raw-window-handle"
-version = "0.5.0"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a"
-dependencies = [
- "cty",
-]
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "raw_mesh"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"anyhow",
"bytes",
"clap 4.1.4",
+ "depthai-viewer",
"gltf",
"mimalloc",
- "rerun",
]
[[package]]
@@ -3770,7 +3911,7 @@ dependencies = [
[[package]]
name = "re_analytics"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"anyhow",
"crossbeam",
@@ -3791,7 +3932,7 @@ dependencies = [
[[package]]
name = "re_arrow_store"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"anyhow",
@@ -3818,7 +3959,7 @@ dependencies = [
[[package]]
name = "re_build_build_info"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"anyhow",
"time 0.3.20",
@@ -3826,18 +3967,18 @@ dependencies = [
[[package]]
name = "re_build_info"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
[[package]]
name = "re_build_web_viewer"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"cargo_metadata",
]
[[package]]
name = "re_data_store"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"criterion",
@@ -3860,14 +4001,14 @@ dependencies = [
[[package]]
name = "re_error"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"anyhow",
]
[[package]]
name = "re_format"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"arrow2",
"arrow2_convert",
@@ -3877,7 +4018,7 @@ dependencies = [
[[package]]
name = "re_int_histogram"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"criterion",
"insta",
@@ -3888,7 +4029,7 @@ dependencies = [
[[package]]
name = "re_log"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"env_logger",
"js-sys",
@@ -3901,7 +4042,7 @@ dependencies = [
[[package]]
name = "re_log_encoding"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"criterion",
"ehttp",
@@ -3926,7 +4067,7 @@ dependencies = [
[[package]]
name = "re_log_types"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"array-init",
@@ -3964,7 +4105,7 @@ dependencies = [
[[package]]
name = "re_memory"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"backtrace",
@@ -3984,7 +4125,7 @@ dependencies = [
[[package]]
name = "re_query"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"arrow2",
"criterion",
@@ -4002,13 +4143,13 @@ dependencies = [
[[package]]
name = "re_renderer"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"anyhow",
"arrow2",
"async-executor",
- "bitflags",
+ "bitflags 1.3.2",
"bytemuck",
"clean-path",
"console_error_panic_hook",
@@ -4026,6 +4167,7 @@ dependencies = [
"itertools",
"log",
"macaw",
+ "never",
"notify",
"ordered-float",
"parking_lot 0.12.1",
@@ -4048,14 +4190,13 @@ dependencies = [
"web-sys",
"wgpu",
"wgpu-core",
- "wgpu-hal",
"winit",
"zip",
]
[[package]]
name = "re_sdk"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"arrow2_convert",
"document-features",
@@ -4075,7 +4216,7 @@ dependencies = [
[[package]]
name = "re_sdk_comms"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"anyhow",
@@ -4091,7 +4232,7 @@ dependencies = [
[[package]]
name = "re_smart_channel"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"crossbeam",
"instant",
@@ -4099,7 +4240,7 @@ dependencies = [
[[package]]
name = "re_string_interner"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"nohash-hasher",
@@ -4110,7 +4251,7 @@ dependencies = [
[[package]]
name = "re_tensor_ops"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"ndarray",
@@ -4120,7 +4261,7 @@ dependencies = [
[[package]]
name = "re_tuid"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"arrow2",
"arrow2_convert",
@@ -4134,7 +4275,7 @@ dependencies = [
[[package]]
name = "re_ui"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"eframe",
"egui",
@@ -4152,20 +4293,23 @@ dependencies = [
[[package]]
name = "re_viewer"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"ahash 0.8.2",
"anyhow",
"arboard",
+ "async-std",
"bytemuck",
"cocoa",
"console_error_panic_hook",
+ "crossbeam-channel",
"eframe",
"egui",
"egui-wgpu",
"egui_dock",
"egui_extras",
"enumset",
+ "ewebsock",
"glam",
"half 2.2.1",
"image",
@@ -4179,6 +4323,8 @@ dependencies = [
"poll-promise",
"puffin",
"puffin_http",
+ "pyo3",
+ "pyo3-asyncio",
"re_analytics",
"re_arrow_store",
"re_build_build_info",
@@ -4198,10 +4344,15 @@ dependencies = [
"re_ws_comms",
"rfd",
"serde",
+ "serde_json",
"slotmap",
"smallvec",
+ "strum 0.24.1",
+ "strum_macros 0.24.3",
"thiserror",
"time 0.3.20",
+ "tokio",
+ "url",
"uuid",
"vec1",
"wasm-bindgen-futures",
@@ -4212,7 +4363,7 @@ dependencies = [
[[package]]
name = "re_web_viewer_server"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"cargo_metadata",
"ctrlc",
@@ -4229,7 +4380,7 @@ dependencies = [
[[package]]
name = "re_ws_comms"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"anyhow",
"bincode",
@@ -4253,7 +4404,7 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -4262,7 +4413,7 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb02a9aee8e8c7ad8d86890f1e16b49e0bbbffc9961ff3788c31d57c98bcbf03"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -4295,47 +4446,16 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "renderdoc-sys"
-version = "0.7.1"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
-
-[[package]]
-name = "rerun"
-version = "0.4.0"
-dependencies = [
- "anyhow",
- "backtrace",
- "clap 4.1.4",
- "ctrlc",
- "document-features",
- "itertools",
- "libc",
- "mimalloc",
- "parking_lot 0.12.1",
- "re_analytics",
- "re_build_build_info",
- "re_build_info",
- "re_data_store",
- "re_format",
- "re_log",
- "re_log_encoding",
- "re_log_types",
- "re_memory",
- "re_sdk",
- "re_sdk_comms",
- "re_smart_channel",
- "re_viewer",
- "re_web_viewer_server",
- "re_ws_comms",
- "tokio",
- "webbrowser",
-]
+checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
[[package]]
name = "rerun_py"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"arrow2",
+ "depthai-viewer",
"document-features",
"glam",
"image",
@@ -4356,7 +4476,6 @@ dependencies = [
"re_memory",
"re_web_viewer_server",
"re_ws_comms",
- "rerun",
"thiserror",
"tokio",
"uuid",
@@ -4431,13 +4550,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff"
dependencies = [
"base64 0.13.1",
- "bitflags",
+ "bitflags 1.3.2",
"serde",
]
[[package]]
name = "run_wasm"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
"cargo-run-wasm",
"pico-args",
@@ -4471,7 +4590,7 @@ version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"errno 0.2.8",
"io-lifetimes",
"libc",
@@ -4485,7 +4604,7 @@ version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"errno 0.3.0",
"io-lifetimes",
"libc",
@@ -4777,7 +4896,7 @@ version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"calloop",
"dlib",
"lazy_static",
@@ -4822,7 +4941,7 @@ version = "0.2.0+1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"num-traits",
]
@@ -4991,11 +5110,11 @@ dependencies = [
[[package]]
name = "test_image_memory"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
dependencies = [
+ "depthai-viewer",
"mimalloc",
"re_format",
- "rerun",
]
[[package]]
@@ -5443,6 +5562,16 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "value-bag"
+version = "1.0.0-alpha.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55"
+dependencies = [
+ "ctor",
+ "version_check",
+]
+
[[package]]
name = "vec1"
version = "1.10.1"
@@ -5712,7 +5841,7 @@ version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"downcast-rs",
"libc",
"nix 0.24.2",
@@ -5751,7 +5880,7 @@ version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"wayland-client",
"wayland-commons",
"wayland-scanner",
@@ -5842,9 +5971,8 @@ dependencies = [
[[package]]
name = "wgpu"
-version = "0.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d745a1b6d91d85c33defbb29f0eee0450e1d2614d987e14bf6baf26009d132d7"
+version = "0.16.0"
+source = "git+https://github.com/rerun-io/wgpu?rev=de497aeda152a3515bac5eb4bf1b17f1757b9dac#de497aeda152a3515bac5eb4bf1b17f1757b9dac"
dependencies = [
"arrayvec",
"cfg-if",
@@ -5866,20 +5994,19 @@ dependencies = [
[[package]]
name = "wgpu-core"
-version = "0.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7131408d940e335792645a98f03639573b0480e9e2e7cddbbab74f7c6d9f3fff"
+version = "0.16.0"
+source = "git+https://github.com/rerun-io/wgpu?rev=de497aeda152a3515bac5eb4bf1b17f1757b9dac#de497aeda152a3515bac5eb4bf1b17f1757b9dac"
dependencies = [
"arrayvec",
"bit-vec",
- "bitflags",
+ "bitflags 2.2.1",
"codespan-reporting",
- "fxhash",
"log",
"naga",
"parking_lot 0.12.1",
"profiling",
"raw-window-handle",
+ "rustc-hash",
"smallvec",
"thiserror",
"web-sys",
@@ -5889,20 +6016,18 @@ dependencies = [
[[package]]
name = "wgpu-hal"
-version = "0.15.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdcf61a283adc744bb5453dd88ea91f3f86d5ca6b027661c6c73c7734ae0288b"
+version = "0.16.0"
+source = "git+https://github.com/rerun-io/wgpu?rev=de497aeda152a3515bac5eb4bf1b17f1757b9dac#de497aeda152a3515bac5eb4bf1b17f1757b9dac"
dependencies = [
"android_system_properties",
"arrayvec",
"ash",
"bit-set",
- "bitflags",
+ "bitflags 2.2.1",
"block",
"core-graphics-types",
"d3d12",
"foreign-types",
- "fxhash",
"glow",
"gpu-alloc",
"gpu-allocator",
@@ -5921,6 +6046,7 @@ dependencies = [
"range-alloc",
"raw-window-handle",
"renderdoc-sys",
+ "rustc-hash",
"smallvec",
"thiserror",
"wasm-bindgen",
@@ -5931,11 +6057,10 @@ dependencies = [
[[package]]
name = "wgpu-types"
-version = "0.15.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32444e121b0bd00cb02c0de32fde457a9491bd44e03e7a5db6df9b1da2f6f110"
+version = "0.16.0"
+source = "git+https://github.com/rerun-io/wgpu?rev=de497aeda152a3515bac5eb4bf1b17f1757b9dac#de497aeda152a3515bac5eb4bf1b17f1757b9dac"
dependencies = [
- "bitflags",
+ "bitflags 2.2.1",
"js-sys",
"web-sys",
]
@@ -5953,9 +6078,9 @@ dependencies = [
[[package]]
name = "widestring"
-version = "0.5.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
+checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
[[package]]
name = "winapi"
@@ -6094,7 +6219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4755d4ba0e3d30fc7beef2095e246b1e6a6fad0717608bcb87a2df4b003bcf"
dependencies = [
"android-activity",
- "bitflags",
+ "bitflags 1.3.2",
"cfg_aliases",
"core-foundation",
"core-graphics",
diff --git a/Cargo.toml b/Cargo.toml
index 6a433c6954ee..bd3b9f138eec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,39 +16,39 @@ include = ["../../LICENSE-APACHE", "../../LICENSE-MIT", "**/*.rs", "Cargo.toml"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rerun-io/rerun"
rust-version = "1.67"
-version = "0.4.0"
+version = "0.6.0-alpha.0"
[workspace.dependencies]
# When using alpha-release, always use exact version, e.g. `version = "=0.x.y-alpha.z"
# This is because we treat alpha-releases as incompatible, but semver doesn't.
# In particular: if we compile rerun 0.3.0-alpha.0 we only want it to use
# re_log_types 0.3.0-alpha.0, NOT 0.3.0-alpha.4 even though it is newer and semver-compatible.
-re_analytics = { path = "crates/re_analytics", version = "0.4.0" }
-re_arrow_store = { path = "crates/re_arrow_store", version = "0.4.0" }
-re_build_build_info = { path = "crates/re_build_build_info", version = "0.4.0" }
-re_build_info = { path = "crates/re_build_info", version = "0.4.0" }
-re_build_web_viewer = { path = "crates/re_build_web_viewer", version = "0.4.0" }
-re_data_store = { path = "crates/re_data_store", version = "0.4.0" }
-re_error = { path = "crates/re_error", version = "0.4.0" }
-re_format = { path = "crates/re_format", version = "0.4.0" }
-re_int_histogram = { path = "crates/re_int_histogram", version = "0.4.0" }
-re_log = { path = "crates/re_log", version = "0.4.0" }
-re_log_encoding = { path = "crates/re_log_encoding", version = "0.4.0" }
-re_log_types = { path = "crates/re_log_types", version = "0.4.0" }
-re_memory = { path = "crates/re_memory", version = "0.4.0" }
-re_query = { path = "crates/re_query", version = "0.4.0" }
-re_renderer = { path = "crates/re_renderer", version = "0.4.0" }
-re_sdk = { path = "crates/re_sdk", version = "0.4.0" }
-re_sdk_comms = { path = "crates/re_sdk_comms", version = "0.4.0" }
-re_smart_channel = { path = "crates/re_smart_channel", version = "0.4.0" }
-re_string_interner = { path = "crates/re_string_interner", version = "0.4.0" }
-re_tensor_ops = { path = "crates/re_tensor_ops", version = "0.4.0" }
-re_tuid = { path = "crates/re_tuid", version = "0.4.0" }
-re_ui = { path = "crates/re_ui", version = "0.4.0" }
-re_viewer = { path = "crates/re_viewer", version = "0.4.0" }
-re_web_viewer_server = { path = "crates/re_web_viewer_server", version = "0.4.0" }
-re_ws_comms = { path = "crates/re_ws_comms", version = "0.4.0" }
-rerun = { path = "crates/rerun", version = "0.4.0" }
+re_sdk_comms = { path = "crates/re_sdk_comms", version = "=0.6.0-alpha.0" }
+re_analytics = { path = "crates/re_analytics", version = "=0.6.0-alpha.0" }
+re_arrow_store = { path = "crates/re_arrow_store", version = "=0.6.0-alpha.0" }
+re_build_build_info = { path = "crates/re_build_build_info", version = "=0.6.0-alpha.0" }
+re_build_info = { path = "crates/re_build_info", version = "=0.6.0-alpha.0" }
+re_build_web_viewer = { path = "crates/re_build_web_viewer", version = "=0.6.0-alpha.0" }
+re_data_store = { path = "crates/re_data_store", version = "=0.6.0-alpha.0" }
+re_error = { path = "crates/re_error", version = "=0.6.0-alpha.0" }
+re_format = { path = "crates/re_format", version = "=0.6.0-alpha.0" }
+re_int_histogram = { path = "crates/re_int_histogram", version = "=0.6.0-alpha.0" }
+re_log = { path = "crates/re_log", version = "=0.6.0-alpha.0" }
+re_log_encoding = { path = "crates/re_log_encoding", version = "=0.6.0-alpha.0" }
+re_log_types = { path = "crates/re_log_types", version = "=0.6.0-alpha.0" }
+re_memory = { path = "crates/re_memory", version = "=0.6.0-alpha.0" }
+re_query = { path = "crates/re_query", version = "=0.6.0-alpha.0" }
+re_renderer = { path = "crates/re_renderer", version = "=0.6.0-alpha.0", default-features = false }
+re_sdk = { path = "crates/re_sdk", version = "=0.6.0-alpha.0" }
+re_smart_channel = { path = "crates/re_smart_channel", version = "=0.6.0-alpha.0" }
+re_string_interner = { path = "crates/re_string_interner", version = "=0.6.0-alpha.0" }
+re_tensor_ops = { path = "crates/re_tensor_ops", version = "=0.6.0-alpha.0" }
+re_tuid = { path = "crates/re_tuid", version = "=0.6.0-alpha.0" }
+re_ui = { path = "crates/re_ui", version = "=0.6.0-alpha.0" }
+re_viewer = { path = "crates/re_viewer", version = "=0.6.0-alpha.0", default-features = false }
+re_web_viewer_server = { path = "crates/re_web_viewer_server", version = "=0.6.0-alpha.0" }
+re_ws_comms = { path = "crates/re_ws_comms", version = "=0.6.0-alpha.0" }
+depthai-viewer = { path = "crates/rerun", version = "=0.6.0-alpha.0" }
ahash = "0.8"
anyhow = "1.0"
@@ -59,10 +59,10 @@ comfy-table = { version = "6.1", default-features = false }
ctrlc = { version = "3.0", features = ["termination"] }
ecolor = "0.21.0"
eframe = { version = "0.21.3", default-features = false }
-egui = "0.21.0"
+egui = { version = "0.21.0", features = ["extra_debug_asserts", "log"] }
egui-wgpu = "0.21.0"
egui_dock = "0.4"
-egui_extras = "0.21.0"
+egui_extras = { version = "0.21.0", features = ["log"] }
emath = "0.21.0"
enumset = "1.0.12"
epaint = "0.21.0"
@@ -85,9 +85,8 @@ thiserror = "1.0"
time = { version = "0.3", features = ["wasm-bindgen"] }
tinyvec = { version = "1.6", features = ["alloc", "rustc_1_55"] }
tokio = "1.24"
-wgpu = { version = "0.15.1", default-features = false }
-wgpu-core = { version = "0.15.1", default-features = false }
-wgpu-hal = { version = "0.15.4", default-features = false }
+wgpu = { version = "0.16" }
+wgpu-core = { version = "0.16" }
[profile.dev]
@@ -112,3 +111,17 @@ debug = true
# If that is not possible, patch to a branch that has a PR open on the upstream repo.
# As a last resport, patch with a commit to our own repository.
# ALWAYS document what PR the commit hash is part of, or when it was merged into the upstream trunk.
+
+# TODO(andreas/emilk): Update to a stable egui version
+# wgpu 0.16 support, device configuration dependent on adapter
+ecolor = { git = "https://github.com/emilk/egui", rev = "f76eefb98d23cbf71989255aafe75a07d343f6ed" }
+eframe = { git = "https://github.com/emilk/egui", rev = "f76eefb98d23cbf71989255aafe75a07d343f6ed" }
+egui = { git = "https://github.com/emilk/egui", rev = "f76eefb98d23cbf71989255aafe75a07d343f6ed" }
+egui-wgpu = { git = "https://github.com/emilk/egui", rev = "f76eefb98d23cbf71989255aafe75a07d343f6ed" }
+egui_extras = { git = "https://github.com/emilk/egui", rev = "f76eefb98d23cbf71989255aafe75a07d343f6ed" }
+emath = { git = "https://github.com/emilk/egui", rev = "f76eefb98d23cbf71989255aafe75a07d343f6ed" }
+
+# TODO(andreas): Either work around this issue in wgpu-egui (never discard command buffers) or wait for wgpu patch release.
+# Fix for command buffer dropping crash https://github.com/gfx-rs/wgpu/pull/3726
+wgpu = { git = "https://github.com/rerun-io/wgpu", rev = "de497aeda152a3515bac5eb4bf1b17f1757b9dac" }
+wgpu-core = { git = "https://github.com/rerun-io/wgpu", rev = "de497aeda152a3515bac5eb4bf1b17f1757b9dac" }
diff --git a/README.md b/README.md
index bae9d9f0f30c..69788eb69ebd 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
-
+
@@ -19,11 +19,11 @@ Use one of our logging APIs (Python or Rust) to log rich data, such as images an
```py
import rerun as rr
-rr.init("my_app", spawn = True) # Spawn a Rerun Viewer and stream log events to it
+viewer.init("my_app", spawn = True) # Spawn a Rerun Viewer and stream log events to it
-rr.log_image("rgb_image", image)
-rr.log_points("points", positions)
-rr.log_rect("car", bbox)
+viewer.log_image("rgb_image", image)
+viewer.log_points("points", positions)
+viewer.log_rect("car", bbox)
β¦
```
@@ -32,57 +32,59 @@ rr.log_rect("car", bbox)
## Getting started
-* **Python**: `pip install rerun-sdk`
-* **Rust**: `cargo add rerun`
-* **C / C++**: Coming soon
+
+- **Python**: `pip install depthai-viewer`
+- **Rust**: `cargo add rerun`
+- **C / C++**: Coming soon
### Rerun Viewer binary
+
Both the Python and Rust library can start the Rerun Viewer, but to stream log data over the network or load our `.rrd` data files you also need the `rerun` binary.
-It can be installed with `pip install rerun-sdk` or with `cargo install rerun`.
+It can be installed with `pip install depthai-viewer` or with `cargo install rerun`.
You should now be able to run `rerun --help` in any terminal.
-
### Documentation
+
- π [High-level docs](http://rerun.io/docs)
- βοΈ [Examples](examples)
- π [Python API docs](https://ref.rerun.io/docs/python)
- π¦ [Rust API docs](https://docs.rs/rerun/)
- βοΈ [Troubleshooting](https://www.rerun.io/docs/getting-started/troubleshooting)
-
## Status
+
We are in early beta.
There are many features we want to add, and the API is still evolving.
_Expect breaking changes!_
Some shortcomings:
-* Big points clouds (1M+) are slow ([#1136](https://github.com/rerun-io/rerun/issues/1136))
-* The data you want to visualize must fit in RAM.
+
+- Big points clouds (1M+) are slow ([#1136](https://github.com/rerun-io/rerun/issues/1136))
+- The data you want to visualize must fit in RAM.
- See for how to bound memory use
- We plan on having a disk-based data store some time in the future
-* The Rust library takes a long time to compile
+- The Rust library takes a long time to compile
- We have way too many big dependencies, and we are planning on improving the situation ([#1316](https://github.com/rerun-io/rerun/pull/1316))
-
## Business model
+
Rerun uses an open-core model. Everything in this repository will stay open source and free (both as in beer and as in freedom).
In the future, Rerun will offer a commercial product that builds on top of the core free project.
The Rerun open source project targets the needs of individual developers.
The commercial product targets the needs specific to teams that build and run computer vision and robotics products.
-
# Development
-* [`ARCHITECTURE.md`](ARCHITECTURE.md)
-* [`BUILD.md`](BUILD.md)
-* [`rerun_py/README.md`](rerun_py/README.md) - build instructions for Python SDK
-* [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)
-* [`CODE_STYLE.md`](CODE_STYLE.md)
-* [`CONTRIBUTING.md`](CONTRIBUTING.md)
-* [`RELEASES.md`](RELEASES.md)
+- [`ARCHITECTURE.md`](ARCHITECTURE.md)
+- [`BUILD.md`](BUILD.md)
+- [`rerun_py/README.md`](rerun_py/README.md) - build instructions for Python SDK
+- [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)
+- [`CODE_STYLE.md`](CODE_STYLE.md)
+- [`CONTRIBUTING.md`](CONTRIBUTING.md)
+- [`RELEASES.md`](RELEASES.md)
## Installing a pre-release Python SDK
diff --git a/RELEASES.md b/RELEASES.md
index 6ab1bded58fd..c83448189c04 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,24 +1,26 @@
# Releases and versioning
-This document describes the current release and versioning strategy. This strategy is likely to change as Rerun matures.
+This document describes the current release and versioning strategy. This strategy is likely to change as Rerun matures.
## See also
-* [`ARCHITECTURE.md`](ARCHITECTURE.md)
-* [`BUILD.md`](BUILD.md)
-* [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)
-* [`CODE_STYLE.md`](CODE_STYLE.md)
-* [`CONTRIBUTING.md`](CONTRIBUTING.md)
+- [`ARCHITECTURE.md`](ARCHITECTURE.md)
+- [`BUILD.md`](BUILD.md)
+- [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)
+- [`CODE_STYLE.md`](CODE_STYLE.md)
+- [`CONTRIBUTING.md`](CONTRIBUTING.md)
## Release Cadence
-New Rerun versions are released every two weeks. Sometimes we do out-of-schedule patch releases.
+New Rerun versions are released every two weeks. Sometimes we do out-of-schedule patch releases.
## Library versioning and release cadence
+
Each release include new versions of:
-* The Python SDK
-* The Rust SDK
-* All rust crates
+
+- The Python SDK
+- The Rust SDK
+- All rust crates
We use semantic versioning. All versions are increased in lockstep, with a minor version bump each time (`0.1.0`, `0.2.0`, `0.3.0`, β¦).
@@ -28,121 +30,127 @@ In rare cases we will do patch releases, e.g. `0.3.1`, when there is a critical
We sometimes do pre-releases. Then we use the versioning `0.2.0-alpha.0` etc.
-
## Data and communication versioning
+
We have not yet committed to any backwards or forwards compatibility.
We tag all data files (`.rrd` files) and communication protocols with the rerun version number. If there is a version mismatch, a warning is logged, but an attempt is still made to load the older or newer data.
-
## Releases
+
Release builds of the Python Wheels are triggered by pushing a release tag to GitHub in the form `v0.2.0`.
If we are doing a patch release, we do a branch off of the latest release tag (e.g. `v0.3.0`) and cherry-pick any fixes we want into that branch.
### Release checklist
+
Go through this checklist from top to bottom, and check each item before moving onto the next.
This is a living document. Strive to improve it on each new release.
-* [ ] Create a release branch called `release-0.x.y`
-* [ ] If it is a patch release branch off `latest` and cherry-pick the commits that should be included
-* [ ] Update `CHANGELOG.md` with the new version number with:
- * [ ] A one-line summary of the release
- * [ ] A multi-line summary of the release
- * [ ] A gif showing a major new feature
- * [ ] Run `pip install GitPython && scripts/generate_changelog.py`
- * [ ] Edit PR descriptions/labels to improve the generated changelog
- * [ ] Copy-paste the results into `CHANGELOG.md`.
- * [ ] Editorialize the changelog if necessary
- * [ ]Β Make sure the changelog includes instructions for handling any breaking changes
- * [ ] Commit and push the changelog
-* [ ] Create a draft PR containing:
- * [ ] One-line summary of the release
- * [ ] A multi-line summary of the release
- * [ ] A gif showing a major new feature
-* [ ] Test the branch ([see below](#testing-a-release))
-* [ ] Open the PR up for review with the `β΄ release` label
-* [ ] Bump version number in root `Cargo.toml`.
-* [ ] Check that CI is green
-* [ ] Publish the crates (see below)
-* [ ] `git tag -a v0.x.y -m 'Release 0.x.y - summary'`
- * `git push --tags`
- * This will trigger a PyPI release when pushed
-* [ ] `git pull --tags && git tag -d latest && git tag -a latest -m 'Latest release' && git push --tags origin latest --force`
-* [ ] Manually trigger a new web viewer build and upload at https://github.com/rerun-io/rerun/actions/workflows/rust.yml
-* [ ] Wait for CI to build release artifacts and publish them on GitHub and PyPI.
-* [ ] Merge PR
-* [ ] Edit the GitHub release at https://github.com/rerun-io/rerun/releases/edit/v0.x.0
- * [ ] Mark it as as the latest release
- * [ ] Paste in the `CHANGELOG.md`
-* [ ] Wait for wheel to appear on https://pypi.org/project/rerun-sdk/
-* [ ] Test the released Python and Rust libraries (see below)
-* [ ] Wait for documentation to build: https://docs.rs/releases/queue
-* [ ] Point to the latest release via instructions in .
-* [ ] Post on:
- * [ ] Community Discord
- * [ ] Rerun Twitter
- * [ ] Reddit?
-
+- [ ] Create a release branch called `release-0.x.y`
+- [ ] If it is a patch release branch off `latest` and cherry-pick the commits that should be included
+- [ ] Update `CHANGELOG.md` with the new version number with:
+ - [ ] A one-line summary of the release
+ - [ ] A multi-line summary of the release
+ - [ ] A gif showing a major new feature
+ - [ ] Run `pip install GitPython && scripts/generate_changelog.py`
+ - [ ] Edit PR descriptions/labels to improve the generated changelog
+ - [ ] Copy-paste the results into `CHANGELOG.md`.
+ - [ ] Editorialize the changelog if necessary
+ - [ ]Β Make sure the changelog includes instructions for handling any breaking changes
+ - [ ] Commit and push the changelog
+- [ ] Create a draft PR containing:
+ - [ ] One-line summary of the release
+ - [ ] A multi-line summary of the release
+ - [ ] A gif showing a major new feature
+- [ ] Test the branch ([see below](#testing-a-release))
+- [ ] Open the PR up for review with the `β΄ release` label
+- [ ] Bump version number in root `Cargo.toml`.
+- [ ] Check that CI is green
+- [ ] Publish the crates (see below)
+- [ ] `git tag -a v0.x.y -m 'Release 0.x.y - summary'`
+ - `git push --tags`
+ - This will trigger a PyPI release when pushed
+- [ ] `git pull --tags && git tag -d latest && git tag -a latest -m 'Latest release' && git push --tags origin latest --force`
+- [ ] Manually trigger a new web viewer build and upload at https://github.com/rerun-io/rerun/actions/workflows/rust.yml
+- [ ] Wait for CI to build release artifacts and publish them on GitHub and PyPI.
+- [ ] Merge PR
+- [ ] Edit the GitHub release at https://github.com/rerun-io/rerun/releases/edit/v0.x.0
+ - [ ] Mark it as as the latest release
+ - [ ] Paste in the `CHANGELOG.md`
+- [ ] Wait for wheel to appear on https://pypi.org/project/depthai-viewer/
+- [ ] Test the released Python and Rust libraries (see below)
+- [ ] Wait for documentation to build: https://docs.rs/releases/queue
+- [ ] Point to the latest release via instructions in .
+- [ ] Post on:
+ - [ ] Community Discord
+ - [ ] Rerun Twitter
+ - [ ] Reddit?
### Testing a release
+
Before pushing the release tag:
- * [ ] `just py-run-all`
- * [ ] Test the web viewer:
- * [ ] `cargo run -p rerun --features web_viewer -- --web-viewer ../nyud.rrd`
- * [ ] Test on:
- * [ ] Chromium
- * [ ] Firefox
- * [ ] Mobile
+
+- [ ] `just py-run-all`
+- [ ] Test the web viewer:
+ - [ ] `cargo run -p rerun --features web_viewer -- --web-viewer ../nyud.rrd`
+ - [ ] Test on:
+ - [ ] Chromium
+ - [ ] Firefox
+ - [ ] Mobile
After tagging and the CI has published:
- * [ ] Test the Python packages from PyPI: `pip install rerun_sdk==0.x.0a1`
- * [ ] Test rust install version: `cargo install -f rerun@0.x.0-alpha.1 -F web_viewer && rerun --web-viewer api.rrd`
- * [ ] Test rust crate: Modify Cargo.toml of any example to not point to the workspace
- * [ ] run with `--serve` to test web player
+
+- [ ] Test the Python packages from PyPI: `pip install rerun_sdk==0.x.0a1`
+- [ ] Test rust install version: `cargo install -f rerun@0.x.0-alpha.1 -F web_viewer && rerun --web-viewer api.rrd`
+- [ ] Test rust crate: Modify Cargo.toml of any example to not point to the workspace
+ - [ ] run with `--serve` to test web player
Checklist for testing alpha releases:
-* Windows
- * [ ] Python Wheel
- * [ ] Web
- * [ ] Native
- * [ ] Rust crate
- * [ ] Web
- * [ ] Native
- * [ ] Rust install
- * [ ] Web
- * [ ] Native
-* Linux
- * [ ] Python Wheel
- * [ ] Web
- * [ ] Native
- * [ ] Rust crate
- * [ ] Web
- * [ ] Native
- * [ ] Rust install
- * [ ] Web
- * [ ] Native
-* Mac
- * [ ] Python Wheel
- * [ ] Web
- * [ ] Native
- * [ ] Rust crate
- * [ ] Web
- * [ ] Native
- * [ ] Rust install
- * [ ] Web
- * [ ] Native
+- Windows
+ - [ ] Python Wheel
+ - [ ] Web
+ - [ ] Native
+ - [ ] Rust crate
+ - [ ] Web
+ - [ ] Native
+ - [ ] Rust install
+ - [ ] Web
+ - [ ] Native
+- Linux
+ - [ ] Python Wheel
+ - [ ] Web
+ - [ ] Native
+ - [ ] Rust crate
+ - [ ] Web
+ - [ ] Native
+ - [ ] Rust install
+ - [ ] Web
+ - [ ] Native
+- Mac
+ - [ ] Python Wheel
+ - [ ] Web
+ - [ ] Native
+ - [ ] Rust crate
+ - [ ] Web
+ - [ ] Native
+ - [ ] Rust install
+ - [ ] Web
+ - [ ] Native
## Publishing
+
First login as https://crates.io/users/rerunio with and API key you get from Emil:
```bash
cargo login $API_KEY
```
------------------------------------------------------------------------------------------------
-!! IMPORTANT !! Shut off VSCode, and don't touch anything while `publish_crates.sh` is running!
-!! IMPORTANT !! Read `publish_crates.sh` for details
------------------------------------------------------------------------------------------------
+---
+
+!! IMPORTANT !! Shut off VSCode, and don't touch anything while `publish_crates.sh` is running!
+!! IMPORTANT !! Read `publish_crates.sh` for details
+
+---
./scripts/publish_crates.sh --execute
diff --git a/api.py b/api.py
new file mode 100644
index 000000000000..cdc7ee253283
--- /dev/null
+++ b/api.py
@@ -0,0 +1,50 @@
+import depthai_viewer as viewer
+import cv2
+import depthai as dai
+import queue
+
+viewer.init("Depthai Viewer")
+viewer.connect()
+
+# Create pipeline
+pipeline = dai.Pipeline()
+color = pipeline.createColorCamera()
+color.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
+color.setFps(60)
+
+# Create output
+xout = pipeline.createXLinkOut()
+xout.setStreamName("color")
+color.video.link(xout.input)
+
+q = queue.Queue(maxsize=4)
+
+
+def color_frame_callback(frame: dai.ImgFrame):
+ global q
+ q.put(frame.getCvFrame())
+
+
+import time
+
+# Connect to device and start pipeline
+with dai.Device(pipeline) as device:
+ q = device.getOutputQueue("color", maxSize=4, blocking=False) # .addCallback(color_frame_callback)
+ t = time.time_ns()
+ fps = 0
+ display_fps = 0
+ while True:
+ # in_color = q.get()
+ # cv2.imshow("color", in_color)
+ in_color = q.get()
+ fps += 1
+ if time.time_ns() - t > 1e9:
+ display_fps = fps
+ fps = 0
+ print("fps: ", display_fps)
+ t = time.time_ns()
+ frame = in_color.getCvFrame()
+ # cv2.putText(frame, f"fps: {display_fps:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
+ # cv2.imshow("color", frame)
+ viewer.log_image("color/camera/rgb/Color camera", cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
+ cv2.waitKey(1)
diff --git a/ci_docker/Dockerfile b/ci_docker/Dockerfile
index 80c2f73bf820..f3fa89019ce5 100644
--- a/ci_docker/Dockerfile
+++ b/ci_docker/Dockerfile
@@ -58,7 +58,8 @@ RUN set -eux; \
# Install some cargo tools we know we'll always need
# We can't do this until after we've installed rust / cargo above
RUN cargo install cargo-deny && \
- cargo install cargo-cranky
+ cargo install cargo-cranky && \
+ cargo install cargo-benchcmp
# Install the python build dependencies
ADD rerun_py/requirements-build.txt requirements-build.txt
diff --git a/clippy.toml b/clippy.toml
index 4da41d009fbc..bd8f68dcbf99 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -1,4 +1,4 @@
-# There is also a clippy_wasm/clippy.toml which forbids some mthods that are not available in wasm.
+# There is also a scripts/clippy_wasm/clippy.toml which forbids some methods that are not available in wasm.
msrv = "1.67"
@@ -47,7 +47,7 @@ disallowed-types = [
# Allow-list of words for markdown in dosctrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
doc-valid-idents = [
- # You must also update the same list in `clippy_wasm/clippy.toml`!
+ # You must also update the same list in `scripts/clippy_wasm/clippy.toml`!
"GitHub",
"GLB",
"GLTF",
diff --git a/crates/re_analytics/src/cli.rs b/crates/re_analytics/src/cli.rs
index 7055563dcd92..4b146daa6996 100644
--- a/crates/re_analytics/src/cli.rs
+++ b/crates/re_analytics/src/cli.rs
@@ -83,7 +83,7 @@ const DETAILS: &str = "
What data is collected?
- The exact set of analytics events and parameters can be found here:
https://github.com/rerun-io/rerun/blob/GIT_HASH/crates/re_viewer/src/viewer_analytics.rs
- - We collect high level events about the usage of the Rerun Viewer. For example:
+ - We collect high level events about the usage of the Depthai Viewer. For example:
- The event 'Viewer Opened' helps us estimate how often Rerun is used.
- The event 'Data Source Connected' helps us understand if users tend to use live
data sources or recordings most, which helps us prioritize features.
diff --git a/crates/re_analytics/src/lib.rs b/crates/re_analytics/src/lib.rs
index 9938887a5f55..8f346c3ea3df 100644
--- a/crates/re_analytics/src/lib.rs
+++ b/crates/re_analytics/src/lib.rs
@@ -211,7 +211,7 @@ const DISCLAIMER: &str = "
help the Rerun team improve the library.
Summary:
- - We only collect high level events about the features used within the Rerun Viewer.
+ - We only collect high level events about the features used within the Depthai Viewer.
- The actual data you log to Rerun, such as point clouds, images, or text logs,
will never be collected.
- We don't log IP addresses.
diff --git a/crates/re_build_info/src/crate_version.rs b/crates/re_build_info/src/crate_version.rs
index 9a29e8e734a7..a4c3ee5e04bf 100644
--- a/crates/re_build_info/src/crate_version.rs
+++ b/crates/re_build_info/src/crate_version.rs
@@ -55,7 +55,7 @@ impl CrateVersion {
pub fn from_bytes([major, minor, patch, suffix_byte]: [u8; 4]) -> Self {
let is_alpha = (suffix_byte & IS_ALPHA_BIT) != 0;
let is_prerelease = (suffix_byte & IS_PRERELEASE_BIT) != 0;
- let alpha_version = suffix_byte & 0b0111_1111;
+ let alpha_version = suffix_byte & !(IS_ALPHA_BIT | IS_PRERELEASE_BIT);
Self {
major,
@@ -273,6 +273,22 @@ fn test_format_parse_roundtrip() {
}
}
+#[test]
+fn test_format_parse_roundtrip_bytes() {
+ let parse = CrateVersion::parse;
+ for version in [
+ "0.2.0",
+ "1.2.3",
+ "12.23.24",
+ "12.23.24-alpha.31",
+ "12.23.24-alpha.31+foo",
+ ] {
+ let version = parse(version);
+ let bytes = version.to_bytes();
+ assert_eq!(CrateVersion::from_bytes(bytes), version);
+ }
+}
+
#[test]
fn test_compatibility() {
fn are_compatible(a: &str, b: &str) -> bool {
diff --git a/crates/re_build_web_viewer/src/lib.rs b/crates/re_build_web_viewer/src/lib.rs
index 8ec1d53f0a88..c5f1c179f10a 100644
--- a/crates/re_build_web_viewer/src/lib.rs
+++ b/crates/re_build_web_viewer/src/lib.rs
@@ -12,7 +12,7 @@ fn target_directory() -> Utf8PathBuf {
}
/// Build `re_viewer` as Wasm, generate .js bindings for it, and place it all into the `./web_viewer` folder.
-pub fn build(release: bool) {
+pub fn build(release: bool, webgpu: bool) {
eprintln!("Building web viewer wasmβ¦");
eprintln!("We assume you've already run ./scripts/setup_web.sh");
@@ -63,7 +63,13 @@ pub fn build(release: bool) {
"wasm32-unknown-unknown",
"--target-dir",
target_wasm_dir.as_str(),
+ "--no-default-features",
]);
+ if webgpu {
+ cmd.arg("--features=analytics");
+ } else {
+ cmd.arg("--features=analytics,webgl");
+ }
if release {
cmd.arg("--release");
}
@@ -71,6 +77,7 @@ pub fn build(release: bool) {
// This is required to enable the web_sys clipboard API which egui_web uses
// https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Clipboard.html
// https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html
+ // Furthermore, it's necessary for unstable WebGPU apis to work.
cmd.env("RUSTFLAGS", "--cfg=web_sys_unstable_apis");
// When executing this script from a Rust build script, do _not_, under any circumstances,
diff --git a/crates/re_build_web_viewer/src/main.rs b/crates/re_build_web_viewer/src/main.rs
index 137cd401a742..1c3d2249d26f 100644
--- a/crates/re_build_web_viewer/src/main.rs
+++ b/crates/re_build_web_viewer/src/main.rs
@@ -2,6 +2,7 @@ use std::process::ExitCode;
fn main() -> ExitCode {
let mut release = None;
+ let mut webgpu = false;
for arg in std::env::args().skip(1) {
match arg.as_str() {
@@ -17,6 +18,9 @@ fn main() -> ExitCode {
assert!(release.is_none(), "Can't set both --release and --debug");
release = Some(true);
}
+ "--webgpu" => {
+ webgpu = true;
+ }
_ => {
print_help();
return ExitCode::FAILURE;
@@ -29,7 +33,7 @@ fn main() -> ExitCode {
return ExitCode::FAILURE;
};
- re_build_web_viewer::build(release);
+ re_build_web_viewer::build(release, webgpu);
ExitCode::SUCCESS
}
@@ -41,6 +45,7 @@ fn print_help() {
--debug: Build a debug binary
--release: Compile for release, and run wasm-opt.
NOTE: --release also removes debug symbols which are otherwise useful for in-browser profiling.
+ --webgpu: Enable WebGPU support (experimental). If not set the viewer will use WebGL instead.
"
);
}
diff --git a/crates/re_data_store/src/entity_properties.rs b/crates/re_data_store/src/entity_properties.rs
index 9929ca4c13c3..96ccd7f91aed 100644
--- a/crates/re_data_store/src/entity_properties.rs
+++ b/crates/re_data_store/src/entity_properties.rs
@@ -44,9 +44,16 @@ pub struct EntityProperties {
pub visible_history: ExtraQueryHistory,
pub interactive: bool,
+ /// Enable color mapping?
+ ///
/// What kind of color mapping should be applied (none, map, texture, transfer..)?
pub color_mapper: EditableAutoValue,
+ /// Points to an entity with an albedo texture.
+ ///
+ /// Only relevant if [`Self::color_mapper`] is set to `AlbedoTexture`.
+ pub albedo_texture: Option,
+
/// Distance of the projection plane (frustum far plane).
///
/// Only applies to pinhole cameras when in a spatial view, using 3D navigation.
@@ -80,6 +87,7 @@ impl Default for EntityProperties {
backproject_depth: EditableAutoValue::Auto(true),
depth_from_world_scale: EditableAutoValue::default(),
backproject_radius_scale: EditableAutoValue::Auto(1.0),
+ albedo_texture: None,
}
}
}
@@ -94,7 +102,7 @@ impl EntityProperties {
interactive: self.interactive && child.interactive,
color_mapper: self.color_mapper.or(&child.color_mapper).clone(),
-
+ albedo_texture: self.albedo_texture.clone().or(child.albedo_texture.clone()),
pinhole_image_plane_distance: self
.pinhole_image_plane_distance
.or(&child.pinhole_image_plane_distance)
@@ -170,25 +178,28 @@ impl std::fmt::Display for Colormap {
pub enum ColorMapper {
/// Use a well-known color map, pre-implemented as a wgsl module.
Colormap(Colormap),
- // TODO(cmc): support textures.
+
+ /// Point to an entity with an albedo texture.
+ AlbedoTexture,
// TODO(cmc): support custom transfer functions.
}
+impl Default for ColorMapper {
+ #[inline]
+ fn default() -> Self {
+ Self::AlbedoTexture
+ }
+}
+
impl std::fmt::Display for ColorMapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- ColorMapper::Colormap(colormap) => colormap.fmt(f),
+ ColorMapper::Colormap(colormap) => write!(f, "Map:{colormap}"),
+ ColorMapper::AlbedoTexture => write!(f, "Albedo texture"),
}
}
}
-impl Default for ColorMapper {
- #[inline]
- fn default() -> Self {
- Self::Colormap(Colormap::default())
- }
-}
-
// ----------------------------------------------------------------------------
/// Get the latest value for a given [`re_log_types::Component`].
diff --git a/crates/re_format/Cargo.toml b/crates/re_format/Cargo.toml
index 198768511bc0..b502f8f6eb4e 100644
--- a/crates/re_format/Cargo.toml
+++ b/crates/re_format/Cargo.toml
@@ -19,4 +19,4 @@ all-features = true
arrow2.workspace = true
arrow2_convert.workspace = true
comfy-table.workspace = true
-re_tuid.workspace = true
+re_tuid = { workspace = true, features = ["arrow2_convert"] }
diff --git a/crates/re_log_types/src/arrow_msg.rs b/crates/re_log_types/src/arrow_msg.rs
index 54e5c01b68e5..b83e24266d29 100644
--- a/crates/re_log_types/src/arrow_msg.rs
+++ b/crates/re_log_types/src/arrow_msg.rs
@@ -42,13 +42,13 @@ impl serde::Serialize for ArrowMsg {
let mut writer = StreamWriter::new(&mut buf, Default::default());
writer
.start(&self.schema, None)
- .map_err(|e| serde::ser::Error::custom(e.to_string()))?;
+ .map_err(|err| serde::ser::Error::custom(err.to_string()))?;
writer
.write(&self.chunk, None)
- .map_err(|e| serde::ser::Error::custom(e.to_string()))?;
+ .map_err(|err| serde::ser::Error::custom(err.to_string()))?;
writer
.finish()
- .map_err(|e| serde::ser::Error::custom(e.to_string()))?;
+ .map_err(|err| serde::ser::Error::custom(err.to_string()))?;
let mut inner = serializer.serialize_tuple(3)?;
inner.serialize_element(&self.table_id)?;
diff --git a/crates/re_log_types/src/component_types/arrow_convert_shims.rs b/crates/re_log_types/src/component_types/arrow_convert_shims.rs
index 8d196842169d..b78ff5f4c065 100644
--- a/crates/re_log_types/src/component_types/arrow_convert_shims.rs
+++ b/crates/re_log_types/src/component_types/arrow_convert_shims.rs
@@ -79,6 +79,7 @@ impl<'a> Iterator for BufferBinaryArrayIter<'a> {
/// Internal `ArrowArray` helper to iterate over a `BinaryArray` while exposing Buffer slices
pub struct BufferBinaryArray;
+#[cfg(not(target_os = "windows"))]
extern "C" {
fn do_not_call_into_iter(); // we never define this function, so the linker will fail
}
diff --git a/crates/re_log_types/src/component_types/imu.rs b/crates/re_log_types/src/component_types/imu.rs
new file mode 100644
index 000000000000..bfcdf7566eb8
--- /dev/null
+++ b/crates/re_log_types/src/component_types/imu.rs
@@ -0,0 +1,25 @@
+use crate::{Component, EntityPath};
+use arrow2_convert::{ArrowDeserialize, ArrowField, ArrowSerialize};
+
+use super::{Point3D, Quaternion};
+
+#[derive(Clone, Debug, PartialEq, ArrowField, ArrowSerialize, ArrowDeserialize)]
+pub struct ImuData {
+ pub accel: Point3D,
+ pub gyro: Point3D,
+ pub mag: Option,
+ pub orientation: Quaternion,
+}
+
+impl ImuData {
+ pub fn entity_path() -> EntityPath {
+ "imu_data".into()
+ }
+}
+
+impl Component for ImuData {
+ #[inline]
+ fn name() -> crate::ComponentName {
+ "rerun.imu".into()
+ }
+}
diff --git a/crates/re_log_types/src/component_types/mod.rs b/crates/re_log_types/src/component_types/mod.rs
index 61066b8c8f58..b1e4ddec13a6 100644
--- a/crates/re_log_types/src/component_types/mod.rs
+++ b/crates/re_log_types/src/component_types/mod.rs
@@ -23,12 +23,14 @@ mod class_id;
mod color;
pub mod context;
pub mod coordinates;
+mod imu;
mod instance_key;
mod keypoint_id;
mod label;
mod linestrip;
mod mat;
mod mesh3d;
+mod node_graph;
mod point;
mod quaternion;
mod radius;
@@ -39,6 +41,7 @@ mod tensor;
mod text_entry;
mod transform;
mod vec;
+mod xlink_stats;
pub use arrow::Arrow3D;
pub use bbox::Box3D;
@@ -46,12 +49,14 @@ pub use class_id::ClassId;
pub use color::ColorRGBA;
pub use context::{AnnotationContext, AnnotationInfo, ClassDescription};
pub use coordinates::ViewCoordinates;
+pub use imu::ImuData;
pub use instance_key::InstanceKey;
pub use keypoint_id::KeypointId;
pub use label::Label;
pub use linestrip::{LineStrip2D, LineStrip3D};
pub use mat::Mat3x3;
pub use mesh3d::{EncodedMesh3D, Mesh3D, MeshFormat, MeshId, RawMesh3D};
+pub use node_graph::NodeGraph;
pub use point::{Point2D, Point3D};
pub use quaternion::Quaternion;
pub use radius::Radius;
@@ -67,10 +72,11 @@ pub use tensor::{TensorImageLoadError, TensorImageSaveError};
pub use text_entry::TextEntry;
pub use transform::{Pinhole, Rigid3, Transform};
pub use vec::{Vec2D, Vec3D, Vec4D};
+pub use xlink_stats::XlinkStats;
lazy_static! {
//TODO(john): use a run-time type registry
- static ref FIELDS: [Field; 25] = [
+ static ref FIELDS: [Field; 28] = [
::field(),
::field(),
::field(),
@@ -96,6 +102,9 @@ lazy_static! {
::field(),
::field(),
::field(),
+ ::field(),
+ ::field(),
+ ::field(),
];
}
@@ -210,6 +219,7 @@ where
pub struct FastFixedSizeListArray(std::marker::PhantomData);
+#[cfg(not(target_os = "windows"))]
extern "C" {
fn do_not_call_into_iter(); // we never define this function, so the linker will fail
}
diff --git a/crates/re_log_types/src/component_types/node_graph.rs b/crates/re_log_types/src/component_types/node_graph.rs
new file mode 100644
index 000000000000..edf8a83270ad
--- /dev/null
+++ b/crates/re_log_types/src/component_types/node_graph.rs
@@ -0,0 +1,40 @@
+use arrow2_convert::{ArrowDeserialize, ArrowField, ArrowSerialize};
+
+use crate::Component;
+
+// ---
+
+/// A double-precision NodeGraph.
+///
+/// ## Examples
+///
+/// ```
+/// # use re_log_types::component_types::NodeGraph;
+/// # use arrow2_convert::field::ArrowField;
+/// # use arrow2::datatypes::{DataType, Field};
+/// assert_eq!(NodeGraph::data_type(), DataType::Float64);
+/// ```
+#[derive(Debug, Clone, Copy, ArrowField, ArrowSerialize, ArrowDeserialize)]
+#[arrow_field(transparent)]
+pub struct NodeGraph(pub f64);
+
+impl Component for NodeGraph {
+ #[inline]
+ fn name() -> crate::ComponentName {
+ "rerun.pipeline_graph".into()
+ }
+}
+
+impl From for NodeGraph {
+ #[inline]
+ fn from(value: f64) -> Self {
+ Self(value)
+ }
+}
+
+impl From for f64 {
+ #[inline]
+ fn from(value: NodeGraph) -> Self {
+ value.0
+ }
+}
diff --git a/crates/re_log_types/src/component_types/tensor.rs b/crates/re_log_types/src/component_types/tensor.rs
index 9c4228b845e6..cf02a3bc2b8e 100644
--- a/crates/re_log_types/src/component_types/tensor.rs
+++ b/crates/re_log_types/src/component_types/tensor.rs
@@ -857,7 +857,7 @@ impl Tensor {
/// A thin wrapper around a [`Tensor`] that is guaranteed to not be compressed (never a jpeg).
///
/// All clones are shallow, like for [`Tensor`].
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct DecodedTensor(Tensor);
impl DecodedTensor {
diff --git a/crates/re_log_types/src/component_types/xlink_stats.rs b/crates/re_log_types/src/component_types/xlink_stats.rs
new file mode 100644
index 000000000000..82b5f3b5522c
--- /dev/null
+++ b/crates/re_log_types/src/component_types/xlink_stats.rs
@@ -0,0 +1,28 @@
+use crate::{Component, EntityPath};
+use arrow2::array::Int128Array;
+use arrow2_convert::{field::I128, ArrowDeserialize, ArrowField, ArrowSerialize};
+
+// TODO(filip): Convert to use i128
+
+/// Stats about the XLink connection throughput
+#[derive(Clone, Debug, PartialEq, ArrowField, ArrowSerialize, ArrowDeserialize)]
+pub struct XlinkStats {
+ /// Bytes read from the XLink by the host (PC)
+ pub bytes_read: i64,
+
+ /// Bytes written to the XLink by the host (PC)
+ pub bytes_written: i64,
+}
+
+impl XlinkStats {
+ pub fn entity_path() -> EntityPath {
+ "xlink_stats".into()
+ }
+}
+
+impl Component for XlinkStats {
+ #[inline]
+ fn name() -> crate::ComponentName {
+ "rerun.xlink_stats".into()
+ }
+}
diff --git a/crates/re_log_types/src/lib.rs b/crates/re_log_types/src/lib.rs
index 7775be821e74..bc2785fd7c2e 100644
--- a/crates/re_log_types/src/lib.rs
+++ b/crates/re_log_types/src/lib.rs
@@ -42,7 +42,9 @@ pub use self::component_types::coordinates;
pub use self::component_types::AnnotationContext;
pub use self::component_types::Arrow3D;
pub use self::component_types::DecodedTensor;
-pub use self::component_types::{EncodedMesh3D, Mesh3D, MeshFormat, MeshId, RawMesh3D};
+pub use self::component_types::{
+ EncodedMesh3D, ImuData, Mesh3D, MeshFormat, MeshId, RawMesh3D, XlinkStats,
+};
pub use self::component_types::{Tensor, ViewCoordinates};
pub use self::data::*;
pub use self::data_cell::{DataCell, DataCellError, DataCellInner, DataCellResult};
diff --git a/crates/re_memory/src/memory_limit.rs b/crates/re_memory/src/memory_limit.rs
index 6d747d2be4ab..ec59cb66e48f 100644
--- a/crates/re_memory/src/memory_limit.rs
+++ b/crates/re_memory/src/memory_limit.rs
@@ -1,13 +1,22 @@
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MemoryLimit {
/// Limit in bytes.
///
/// This is primarily compared to what is reported by [`crate::AccountingAllocator`] ('counted').
/// We limit based on this instead of `resident` (RSS) because `counted` is what we have immediate
/// control over, while RSS depends on what our allocator (MiMalloc) decides to do.
+ /// Default is Some(100MB)
pub limit: Option,
}
+impl Default for MemoryLimit {
+ fn default() -> Self {
+ Self {
+ limit: re_format::parse_bytes("100MB"),
+ }
+ }
+}
+
impl MemoryLimit {
pub fn parse(limit: &str) -> Result {
re_format::parse_bytes(limit)
diff --git a/crates/re_renderer/Cargo.toml b/crates/re_renderer/Cargo.toml
index 03bc85390551..c5a2fb1df2b7 100644
--- a/crates/re_renderer/Cargo.toml
+++ b/crates/re_renderer/Cargo.toml
@@ -24,7 +24,7 @@ targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
[features]
-default = ["arrow", "import-obj", "import-gltf"]
+default = ["import-obj", "import-gltf"]
## Support for Arrow datatypes for end-to-end zero-copy.
arrow = ["dep:arrow2"]
@@ -38,6 +38,8 @@ import-gltf = ["dep:gltf"]
## Enable (de)serialization using serde.
serde = ["dep:serde"]
+## Render using webgl instead of webgpu on wasm builds.
+webgl = ["wgpu/webgl"]
[dependencies]
re_error.workspace = true
@@ -55,6 +57,7 @@ glam = { workspace = true, features = ["bytemuck"] }
half = { workspace = true, features = ["bytemuck"] }
itertools = { workspace = true }
macaw.workspace = true
+never = '0.1'
ordered-float = "3.2"
parking_lot.workspace = true
slotmap = "1.0.6"
@@ -62,6 +65,7 @@ smallvec.workspace = true
static_assertions = "1.1"
thiserror.workspace = true
type-map = "0.5"
+wgpu.workspace = true
# optional
arrow2 = { workspace = true, optional = true }
@@ -74,17 +78,7 @@ tobj = { version = "3.2", optional = true }
crossbeam = "0.8"
notify = "5.0"
puffin.workspace = true
-wgpu = { workspace = true, default-features = false, features = ["wgsl"] }
wgpu-core.workspace = true
-wgpu-hal.workspace = true
-
-# wasm
-[target.'cfg(target_arch = "wasm32")'.dependencies]
-wgpu = { workspace = true, default-features = false, features = [
- "webgl",
- "wgsl",
-] }
-
# For examples:
[dev-dependencies]
@@ -109,7 +103,7 @@ console_error_panic_hook = "0.1.6"
# required to make rand work on wasm, see https://github.com/rust-random/rand#wasm-support
getrandom = { version = "0.2", features = ["js"] }
wasm-bindgen-futures = "0.4.33"
-web-sys = { version = "0.3.60", features = [
+web-sys = { version = "0.3.61", features = [
"Location",
"Blob",
"RequestInit",
diff --git a/crates/re_renderer/examples/2d.rs b/crates/re_renderer/examples/2d.rs
index 32940991c1f2..eb17d6955cfd 100644
--- a/crates/re_renderer/examples/2d.rs
+++ b/crates/re_renderer/examples/2d.rs
@@ -36,16 +36,19 @@ impl framework::Example for Render2D {
);
}
- let rerun_logo_texture = re_ctx.texture_manager_2d.create(
- &mut re_ctx.gpu_resources.textures,
- &Texture2DCreationDesc {
- label: "rerun logo".into(),
- data: image_data.into(),
- format: wgpu::TextureFormat::Rgba8UnormSrgb,
- width: rerun_logo.width(),
- height: rerun_logo.height(),
- },
- );
+ let rerun_logo_texture = re_ctx
+ .texture_manager_2d
+ .create(
+ &mut re_ctx.gpu_resources.textures,
+ &Texture2DCreationDesc {
+ label: "rerun logo".into(),
+ data: image_data.into(),
+ format: wgpu::TextureFormat::Rgba8UnormSrgb,
+ width: rerun_logo.width(),
+ height: rerun_logo.height(),
+ },
+ )
+ .expect("Failed to create texture for rerun logo");
Render2D {
rerun_logo_texture,
diff --git a/crates/re_renderer/examples/depth_cloud.rs b/crates/re_renderer/examples/depth_cloud.rs
index c66c17eb79b3..2f5cdb8f6905 100644
--- a/crates/re_renderer/examples/depth_cloud.rs
+++ b/crates/re_renderer/examples/depth_cloud.rs
@@ -20,8 +20,8 @@ use itertools::Itertools;
use macaw::IsoTransform;
use re_renderer::{
renderer::{
- ColormappedTexture, DepthCloud, DepthCloudDepthData, DepthCloudDrawData, DepthClouds,
- DrawData, GenericSkyboxDrawData, RectangleDrawData, RectangleOptions, TexturedRect,
+ ColormappedTexture, DepthCloud, DepthCloudDrawData, DepthClouds, DrawData,
+ GenericSkyboxDrawData, RectangleDrawData, RectangleOptions, TexturedRect,
},
resource_managers::{GpuTexture2D, Texture2DCreationDesc},
view_builder::{self, Projection, ViewBuilder},
@@ -44,7 +44,6 @@ enum CameraControl {
struct RenderDepthClouds {
depth: DepthTexture,
albedo: AlbedoTexture,
- albedo_handle: GpuTexture2D,
scale: f32,
point_radius_from_world_depth: f32,
@@ -175,14 +174,16 @@ impl RenderDepthClouds {
clouds: vec![DepthCloud {
world_from_obj,
depth_camera_intrinsics: *intrinsics,
- world_depth_from_data_depth: 1.0,
+ world_depth_from_texture_depth: 1.0,
point_radius_from_world_depth: *point_radius_from_world_depth,
max_depth_in_world: 5.0,
depth_dimensions: depth.dimensions,
- depth_data: depth.data.clone(),
+ depth_texture: depth.texture.clone(),
colormap: re_renderer::Colormap::Turbo,
outline_mask_id: Default::default(),
picking_object_id: Default::default(),
+ albedo_dimensions: glam::UVec2::ZERO,
+ albedo_data: None,
}],
radius_boost_in_ui_points_for_outlines: 2.5,
},
@@ -233,19 +234,8 @@ impl framework::Example for RenderDepthClouds {
fn new(re_ctx: &mut re_renderer::RenderContext) -> Self {
re_log::info!("Stop camera movement by pressing 'Space'");
- let depth = DepthTexture::spiral((640, 480).into());
- let albedo = AlbedoTexture::spiral(depth.dimensions);
-
- let albedo_handle = re_ctx.texture_manager_2d.create(
- &mut re_ctx.gpu_resources.textures,
- &Texture2DCreationDesc {
- label: "albedo".into(),
- data: bytemuck::cast_slice(&albedo.rgba8).into(),
- format: wgpu::TextureFormat::Rgba8UnormSrgb,
- width: albedo.dimensions.x,
- height: albedo.dimensions.y,
- },
- );
+ let depth = DepthTexture::spiral(re_ctx, glam::uvec2(640, 480));
+ let albedo = AlbedoTexture::spiral(re_ctx, depth.dimensions);
let scale = 50.0;
let point_radius_from_world_depth = 0.1;
@@ -263,7 +253,6 @@ impl framework::Example for RenderDepthClouds {
RenderDepthClouds {
depth,
albedo,
- albedo_handle,
scale,
point_radius_from_world_depth,
@@ -283,7 +272,6 @@ impl framework::Example for RenderDepthClouds {
) -> Vec {
let Self {
albedo,
- albedo_handle,
camera_control,
camera_position,
..
@@ -326,7 +314,7 @@ impl framework::Example for RenderDepthClouds {
.transform_point3(glam::Vec3::new(1.0, 1.0, 0.0)),
extent_u: world_from_model.transform_vector3(-glam::Vec3::X),
extent_v: world_from_model.transform_vector3(-glam::Vec3::Y),
- colormapped_texture: ColormappedTexture::from_unorm_srgba(albedo_handle.clone()),
+ colormapped_texture: ColormappedTexture::from_unorm_srgba(albedo.texture.clone()),
options: RectangleOptions {
texture_filter_magnification: re_renderer::renderer::TextureFilterMag::Nearest,
texture_filter_minification: re_renderer::renderer::TextureFilterMin::Linear,
@@ -403,40 +391,60 @@ fn spiral(dimensions: glam::UVec2) -> impl Iterator- {
})
}
+pub fn hash(value: &impl std::hash::Hash) -> u64 {
+ ahash::RandomState::with_seeds(1, 2, 3, 4).hash_one(value)
+}
+
struct DepthTexture {
dimensions: glam::UVec2,
- data: DepthCloudDepthData,
+ data: Vec,
+ texture: GpuTexture2D,
}
impl DepthTexture {
- pub fn spiral(dimensions: glam::UVec2) -> Self {
+ pub fn spiral(re_ctx: &mut re_renderer::RenderContext, dimensions: glam::UVec2) -> Self {
let size = (dimensions.x * dimensions.y) as usize;
let mut data = std::iter::repeat(0f32).take(size).collect_vec();
spiral(dimensions).for_each(|(texcoords, d)| {
data[(texcoords.x + texcoords.y * dimensions.x) as usize] = d;
});
- let data = DepthCloudDepthData::F32(data.into());
- Self { dimensions, data }
+ let label = format!("depth texture spiral {dimensions}");
+ let texture = re_ctx
+ .texture_manager_2d
+ .get_or_create(
+ hash(&label),
+ &mut re_ctx.gpu_resources.textures,
+ Texture2DCreationDesc {
+ label: label.into(),
+ data: bytemuck::cast_slice(&data).into(),
+ format: wgpu::TextureFormat::R32Float,
+ width: dimensions.x,
+ height: dimensions.y,
+ },
+ )
+ .expect("Failed to create depth texture.");
+
+ Self {
+ dimensions,
+ data,
+ texture,
+ }
}
pub fn get_linear(&self, x: u32, y: u32) -> f32 {
- match &self.data {
- DepthCloudDepthData::U16(data) => {
- data[(x + y * self.dimensions.x) as usize] as f32 / u16::MAX as f32
- }
- DepthCloudDepthData::F32(data) => data[(x + y * self.dimensions.x) as usize],
- }
+ self.data[(x + y * self.dimensions.x) as usize]
}
}
struct AlbedoTexture {
dimensions: glam::UVec2,
rgba8: Vec,
+ texture: GpuTexture2D,
}
impl AlbedoTexture {
- pub fn spiral(dimensions: glam::UVec2) -> Self {
+ pub fn spiral(re_ctx: &mut re_renderer::RenderContext, dimensions: glam::UVec2) -> Self {
let size = (dimensions.x * dimensions.y) as usize;
let mut rgba8 = std::iter::repeat(0).take(size * 4).collect_vec();
spiral(dimensions).for_each(|(texcoords, d)| {
@@ -444,7 +452,27 @@ impl AlbedoTexture {
rgba8[idx..idx + 4].copy_from_slice(re_renderer::colormap_turbo_srgb(d).as_slice());
});
- Self { dimensions, rgba8 }
+ let label = format!("albedo texture spiral {dimensions}");
+ let texture = re_ctx
+ .texture_manager_2d
+ .get_or_create(
+ hash(&label),
+ &mut re_ctx.gpu_resources.textures,
+ Texture2DCreationDesc {
+ label: label.into(),
+ data: bytemuck::cast_slice(&rgba8).into(),
+ format: wgpu::TextureFormat::Rgba8UnormSrgb,
+ width: dimensions.x,
+ height: dimensions.y,
+ },
+ )
+ .expect("Failed to create albedo texture.");
+
+ Self {
+ dimensions,
+ rgba8,
+ texture,
+ }
}
#[allow(dead_code)]
diff --git a/crates/re_renderer/examples/framework.rs b/crates/re_renderer/examples/framework.rs
index fef6e7544b5b..2575f9d82463 100644
--- a/crates/re_renderer/examples/framework.rs
+++ b/crates/re_renderer/examples/framework.rs
@@ -125,7 +125,7 @@ impl Application {
.await
.context("failed to find an appropriate adapter")?;
- let hardware_tier = HardwareTier::default();
+ let hardware_tier = HardwareTier::from_adapter(&adapter);
hardware_tier.check_downlevel_capabilities(&adapter.get_downlevel_capabilities())?;
let (device, queue) = adapter
.request_device(
@@ -159,6 +159,7 @@ impl Application {
surface.configure(&device, &surface_config);
let mut re_ctx = RenderContext::new(
+ &adapter,
device,
queue,
RenderContextConfig {
diff --git a/crates/re_renderer/shader/colormap.wgsl b/crates/re_renderer/shader/colormap.wgsl
index 59be61afdfe4..7d4676ac8852 100644
--- a/crates/re_renderer/shader/colormap.wgsl
+++ b/crates/re_renderer/shader/colormap.wgsl
@@ -8,11 +8,12 @@ const COLORMAP_MAGMA: u32 = 3u;
const COLORMAP_PLASMA: u32 = 4u;
const COLORMAP_TURBO: u32 = 5u;
const COLORMAP_VIRIDIS: u32 = 6u;
-
+const ALBEDO_TEXTURE: u32 = 7u;
/// Returns a gamma-space sRGB in 0-1 range.
///
/// The input will be saturated to [0, 1] range.
-fn colormap_srgb(which: u32, t: f32) -> Vec3 {
+fn colormap_srgb(which: u32, t_unsaturated: f32) -> Vec3 {
+ let t = saturate(t_unsaturated);
if which == COLORMAP_GRAYSCALE {
return linear_from_srgb(Vec3(t));
} else if which == COLORMAP_INFERNO {
@@ -61,7 +62,6 @@ fn colormap_turbo_srgb(t: f32) -> Vec3 {
let g2 = Vec2(4.27729857, 2.82956604);
let b2 = Vec2(-89.90310912, 27.34824973);
- let t = saturate(t);
let v4 = vec4(1.0, t, t * t, t * t * t);
let v2 = v4.zw * v4.z;
@@ -97,7 +97,6 @@ fn colormap_viridis_srgb(t: f32) -> Vec3 {
let c4 = Vec3(6.228269936347081, 14.17993336680509, 56.69055260068105);
let c5 = Vec3(4.776384997670288, -13.74514537774601, -65.35303263337234);
let c6 = Vec3(-5.435455855934631, 4.645852612178535, 26.3124352495832);
- let t = saturate(t);
return c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * c6)))));
}
@@ -112,7 +111,6 @@ fn colormap_plasma_srgb(t: f32) -> Vec3 {
let c4 = Vec3(-11.10743619062271, -82.66631109428045, 60.13984767418263);
let c5 = Vec3(10.02306557647065, 71.41361770095349, -54.07218655560067);
let c6 = Vec3(-3.658713842777788, -22.93153465461149, 18.19190778539828);
- let t = saturate(t);
return c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * c6)))));
}
@@ -127,7 +125,6 @@ fn colormap_magma_srgb(t: f32) -> Vec3 {
let c4 = Vec3(52.17613981234068, -27.94360607168351, 12.94416944238394);
let c5 = Vec3(-50.76852536473588, 29.04658282127291, 4.23415299384598);
let c6 = Vec3(18.65570506591883, -11.48977351997711, -5.601961508734096);
- let t = saturate(t);
return c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * c6)))));
}
@@ -142,6 +139,5 @@ fn colormap_inferno_srgb(t: f32) -> Vec3 {
let c4 = Vec3(77.162935699427, -33.40235894210092, -81.80730925738993);
let c5 = Vec3(-71.31942824499214, 32.62606426397723, 73.20951985803202);
let c6 = Vec3(25.13112622477341, -12.24266895238567, -23.07032500287172);
- let t = saturate(t);
return c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * c6)))));
}
diff --git a/crates/re_renderer/shader/depth_cloud.wgsl b/crates/re_renderer/shader/depth_cloud.wgsl
index 1e7f7afdf0d7..db39dd6d20d2 100644
--- a/crates/re_renderer/shader/depth_cloud.wgsl
+++ b/crates/re_renderer/shader/depth_cloud.wgsl
@@ -43,16 +43,26 @@ struct DepthCloudInfo {
/// Configures color mapping mode, see `colormap.wgsl`.
colormap: u32,
+ /// Is the albedo texture rgb or mono
+ albedo_color_space: u32,
+
/// Changes between the opaque and outline draw-phases.
radius_boost_in_ui_points: f32,
};
+const ALBEDO_COLOR_RGB: u32 = 0u;
+const ALBEDO_COLOR_MONO: u32 = 1u;
+
@group(1) @binding(0)
var depth_cloud_info: DepthCloudInfo;
@group(1) @binding(1)
var depth_texture: texture_2d;
+/// Only sampled if `DepthCloudInfo::colormap == ALBEDO_TEXTURE`.
+@group(1) @binding(2)
+var albedo_texture: texture_2d;
+
struct VertexOut {
@builtin(position)
pos_in_clip: Vec4,
@@ -82,19 +92,32 @@ struct PointData {
}
// Backprojects the depth texture using the intrinsics passed in the uniform buffer.
-fn compute_point_data(quad_idx: i32) -> PointData {
+fn compute_point_data(quad_idx: u32) -> PointData {
let wh = textureDimensions(depth_texture);
- let texcoords = IVec2(quad_idx % wh.x, quad_idx / wh.x);
+ let texcoords = UVec2(quad_idx % wh.x, quad_idx / wh.x);
// TODO(cmc): expose knobs to linearize/normalize/flip/cam-to-plane depth.
let world_space_depth = depth_cloud_info.world_depth_from_texture_value * textureLoad(depth_texture, texcoords, 0).x;
var data: PointData;
-
if 0.0 < world_space_depth && world_space_depth < f32max {
// TODO(cmc): albedo textures
- let color = Vec4(colormap_linear(depth_cloud_info.colormap, world_space_depth / depth_cloud_info.max_depth_in_world), 1.0);
-
+ // let color = Vec4(colormap_linear(depth_cloud_info.colormap, world_space_depth / depth_cloud_info.max_depth_in_world), 1.0);
+
+ var color: Vec4;
+ if depth_cloud_info.colormap == ALBEDO_TEXTURE {
+ color = textureSampleLevel(
+ albedo_texture,
+ trilinear_sampler,
+ Vec2(texcoords) / Vec2(textureDimensions(albedo_texture)),
+ 0.0
+ );
+ if depth_cloud_info.albedo_color_space == ALBEDO_COLOR_MONO {
+ color = Vec4(linear_from_srgb(Vec3(color.r)), 1.0);
+ }
+ } else {
+ color = Vec4(colormap_srgb(depth_cloud_info.colormap, world_space_depth), 1.0);
+ }
// TODO(cmc): This assumes a pinhole camera; need to support other kinds at some point.
let intrinsics = depth_cloud_info.depth_camera_intrinsics;
let focal_length = Vec2(intrinsics[0][0], intrinsics[1][1]);
diff --git a/crates/re_renderer/shader/generic_skybox.wgsl b/crates/re_renderer/shader/generic_skybox.wgsl
index b94e714ff5a6..854cfc530b34 100644
--- a/crates/re_renderer/shader/generic_skybox.wgsl
+++ b/crates/re_renderer/shader/generic_skybox.wgsl
@@ -18,7 +18,7 @@ fn skybox_light_srgb(dir: Vec3) -> Vec3 {
fn main(in: FragmentInput) -> @location(0) Vec4 {
let camera_dir = camera_ray_direction_from_screenuv(in.texcoord);
// Messing with direction a bit so it looks like in our old three-d based renderer (for easier comparison)
- let rgb = skybox_dark_srgb(camera_dir); // TODO(andreas): Allow switching to skybox_light
+ let rgb = skybox_light_srgb(camera_dir); // TODO(andreas): Allow switching to skybox_light
return Vec4(linear_from_srgb(rgb), 1.0);
//return Vec4(camera_dir, 1.0);
}
diff --git a/crates/re_renderer/shader/lines.wgsl b/crates/re_renderer/shader/lines.wgsl
index c8812e8112df..6a143a8233c8 100644
--- a/crates/re_renderer/shader/lines.wgsl
+++ b/crates/re_renderer/shader/lines.wgsl
@@ -32,11 +32,8 @@ struct BatchUniformBuffer {
@group(2) @binding(0)
var batch: BatchUniformBuffer;
-
-// textureLoad needs i32 right now, so we use that with all sizes & indices to avoid casts
-// https://github.com/gfx-rs/naga/issues/1997
-const POSITION_TEXTURE_SIZE: i32 = 512;
-const LINE_STRIP_TEXTURE_SIZE: i32 = 256;
+const POSITION_TEXTURE_SIZE: u32 = 512u;
+const LINE_STRIP_TEXTURE_SIZE: u32 = 256u;
// Flags
// See lines.rs#LineStripFlags
@@ -87,9 +84,7 @@ struct LineStripData {
// Read and unpack line strip data at a given location
fn read_strip_data(idx: u32) -> LineStripData {
- // can be u32 once https://github.com/gfx-rs/naga/issues/1997 is solved
- let idx = i32(idx);
- let coord = IVec2(idx % LINE_STRIP_TEXTURE_SIZE, idx / LINE_STRIP_TEXTURE_SIZE);
+ let coord = UVec2(idx % LINE_STRIP_TEXTURE_SIZE, idx / LINE_STRIP_TEXTURE_SIZE);
var raw_data = textureLoad(position_data_texture, coord, 0).xy;
var data: LineStripData;
@@ -110,9 +105,7 @@ struct PositionData {
// Read and unpack position data at a given location
fn read_position_data(idx: u32) -> PositionData {
- // can be u32 once https://github.com/gfx-rs/naga/issues/1997 is solved
- let idx = i32(idx);
- var raw_data = textureLoad(line_strip_texture, IVec2(idx % POSITION_TEXTURE_SIZE, idx / POSITION_TEXTURE_SIZE), 0);
+ var raw_data = textureLoad(line_strip_texture, UVec2(idx % POSITION_TEXTURE_SIZE, idx / POSITION_TEXTURE_SIZE), 0);
var data: PositionData;
let pos_4d = batch.world_from_obj * Vec4(raw_data.xyz, 1.0);
@@ -198,7 +191,7 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
quad_dir = pos_data_quad_after.pos - pos_data_quad_end.pos; // Go one pos data forward.
} else if is_cap_triangle {
// Discard vertex.
- center_position = Vec3(0.0/0.0, 0.0/0.0, 0.0/0.0);
+ center_position = Vec3(f32max);
} else {
quad_dir = pos_data_quad_end.pos - pos_data_quad_begin.pos;
}
diff --git a/crates/re_renderer/shader/point_cloud.wgsl b/crates/re_renderer/shader/point_cloud.wgsl
index dc6efe6df9b5..a55404230692 100644
--- a/crates/re_renderer/shader/point_cloud.wgsl
+++ b/crates/re_renderer/shader/point_cloud.wgsl
@@ -36,10 +36,7 @@ var batch: BatchUniformBuffer;
// Flags
// See point_cloud.rs#PointCloudBatchFlags
const ENABLE_SHADING: u32 = 1u;
-
-// textureLoad needs i32 right now, so we use that with all sizes & indices to avoid casts
-// https://github.com/gfx-rs/naga/issues/1997
-var TEXTURE_SIZE: i32 = 2048;
+const TEXTURE_SIZE: u32 = 2048u;
struct VertexOut {
@builtin(position)
@@ -75,8 +72,8 @@ struct PointData {
}
// Read and unpack data at a given location
-fn read_data(idx: i32) -> PointData {
- let coord = IVec2(i32(idx % TEXTURE_SIZE), idx / TEXTURE_SIZE);
+fn read_data(idx: u32) -> PointData {
+ let coord = UVec2(idx % TEXTURE_SIZE, idx / TEXTURE_SIZE);
let position_data = textureLoad(position_data_texture, coord, 0);
let color = textureLoad(color_texture, coord, 0);
diff --git a/crates/re_renderer/shader/rectangle_fs.wgsl b/crates/re_renderer/shader/rectangle_fs.wgsl
index 0d1a35cad961..62f65952fe21 100644
--- a/crates/re_renderer/shader/rectangle_fs.wgsl
+++ b/crates/re_renderer/shader/rectangle_fs.wgsl
@@ -86,10 +86,12 @@ fn fs_main(in: VertexOut) -> @location(0) Vec4 {
let colormap_size = textureDimensions(colormap_texture).xy;
let color_index = normalized_value.r * f32(colormap_size.x * colormap_size.y);
// TODO(emilk): interpolate between neighboring colors for non-integral color indices
- let color_index_i32 = i32(color_index);
- let x = color_index_i32 % colormap_size.x;
- let y = color_index_i32 / colormap_size.x;
- texture_color = textureLoad(colormap_texture, IVec2(x, y), 0);
+ // It's important to round here since otherwise numerical instability can push us to the adjacent class-id
+ // See: https://github.com/rerun-io/rerun/issues/1968
+ let color_index_u32 = u32(round(color_index));
+ let x = color_index_u32 % colormap_size.x;
+ let y = color_index_u32 / colormap_size.x;
+ texture_color = textureLoad(colormap_texture, UVec2(x, y), 0);
} else {
return ERROR_RGBA; // unknown color mapper
}
diff --git a/crates/re_renderer/shader/screen_triangle_vertex.wgsl b/crates/re_renderer/shader/screen_triangle_vertex.wgsl
index 224da3317d4b..e42fac7827a6 100644
--- a/crates/re_renderer/shader/screen_triangle_vertex.wgsl
+++ b/crates/re_renderer/shader/screen_triangle_vertex.wgsl
@@ -4,8 +4,10 @@ struct VertexOutput {
// Mark output position as invariant so it's safe to use it with depth test Equal.
// Without @invariant, different usages in different render pipelines might optimize differently,
// causing slightly different results.
- @invariant @builtin(position) position: Vec4,
- @location(0) texcoord: Vec2,
+ @invariant @builtin(position)
+ position: Vec4,
+ @location(0)
+ texcoord: Vec2,
};
// Workaround for https://github.com/gfx-rs/naga/issues/2252
diff --git a/crates/re_renderer/shader/types.wgsl b/crates/re_renderer/shader/types.wgsl
index 3323c7a6cd1f..6355bcc668b5 100644
--- a/crates/re_renderer/shader/types.wgsl
+++ b/crates/re_renderer/shader/types.wgsl
@@ -1,16 +1,16 @@
// Names chosen to match [`glam`](https://docs.rs/glam/latest/glam/)
-type Vec2 = vec2;
-type Vec3 = vec3;
-type Vec4 = vec4;
-type UVec2 = vec2;
-type UVec3 = vec3;
-type UVec4 = vec4;
-type IVec2 = vec2;
-type IVec3 = vec3;
-type IVec4 = vec4;
-type Mat3 = mat3x3;
-type Mat4x3 = mat4x3;
-type Mat4 = mat4x4;
+alias Vec2 = vec2;
+alias Vec3 = vec3;
+alias Vec4 = vec4;
+alias UVec2 = vec2;
+alias UVec3 = vec3;
+alias UVec4 = vec4;
+alias IVec2 = vec2;
+alias IVec3 = vec3;
+alias IVec4 = vec4;
+alias Mat3 = mat3x3;
+alias Mat4x3 = mat4x3;
+alias Mat4 = mat4x4;
// Extreme values as documented by the spec:
// https://www.w3.org/TR/WGSL/#floating-point-types
@@ -22,7 +22,7 @@ const f32min_normal = 0x1p-126f; // Smallest positive normal float value.
//const f16max = 0x1.ffcp+15h; // Largest positive float value.
//const f16min_normal = 0x1p-14h; // Smallest positive normal float value.
// https://www.w3.org/TR/WGSL/#integer-types
-const i32min = -0x80000000i;
+const i32min = -2147483648; // Naga has some issues with correct negative hexadecimal numbers https://github.com/gfx-rs/naga/issues/2314
const i32max = 0x7fffffffi;
const u32min = 0u;
const u32max = 0xffffffffu;
diff --git a/crates/re_renderer/shader/utils/sphere_quad.wgsl b/crates/re_renderer/shader/utils/sphere_quad.wgsl
index ccdd4b771a0b..00937d8e7701 100644
--- a/crates/re_renderer/shader/utils/sphere_quad.wgsl
+++ b/crates/re_renderer/shader/utils/sphere_quad.wgsl
@@ -56,8 +56,8 @@ fn sphere_quad_span_orthographic(point_pos: Vec3, point_radius: f32, top_bottom:
}
/// Returns the index of the current quad.
-fn sphere_quad_index(vertex_idx: u32) -> i32 {
- return i32(vertex_idx) / 6;
+fn sphere_quad_index(vertex_idx: u32) -> u32 {
+ return vertex_idx / 6u;
}
struct SphereQuadData {
diff --git a/crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs b/crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs
index db1a1d085a90..6bc86a60d112 100644
--- a/crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs
+++ b/crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs
@@ -1,6 +1,9 @@
-use std::{num::NonZeroU32, sync::mpsc};
+use std::sync::mpsc;
-use crate::wgpu_resources::{BufferDesc, GpuBuffer, GpuBufferPool, Texture2DBufferInfo};
+use crate::{
+ texture_info::Texture2DBufferInfo,
+ wgpu_resources::{BufferDesc, GpuBuffer, GpuBufferPool},
+};
/// A sub-allocated staging buffer that can be written to.
///
@@ -119,7 +122,7 @@ where
buffer: &self.chunk_buffer,
layout: wgpu::ImageDataLayout {
offset: self.byte_offset_in_chunk_buffer,
- bytes_per_row: NonZeroU32::new(buffer_info.bytes_per_row_padded),
+ bytes_per_row: Some(buffer_info.bytes_per_row_padded),
rows_per_image: None,
},
},
@@ -290,7 +293,7 @@ impl CpuWriteGpuReadBelt {
);
// Largest uncompressed texture format (btw. many compressed texture format have the same block size!)
debug_assert!(
- wgpu::TextureFormat::Rgba32Uint.describe().block_size as u64
+ wgpu::TextureFormat::Rgba32Uint.block_size(None).unwrap() as u64
<= CpuWriteGpuReadBelt::MIN_OFFSET_ALIGNMENT
);
diff --git a/crates/re_renderer/src/allocator/gpu_readback_belt.rs b/crates/re_renderer/src/allocator/gpu_readback_belt.rs
index 8e5f413743e9..d20e231cb269 100644
--- a/crates/re_renderer/src/allocator/gpu_readback_belt.rs
+++ b/crates/re_renderer/src/allocator/gpu_readback_belt.rs
@@ -1,6 +1,7 @@
-use std::{num::NonZeroU32, ops::Range, sync::mpsc};
+use std::{ops::Range, sync::mpsc};
-use crate::wgpu_resources::{BufferDesc, GpuBuffer, GpuBufferPool, Texture2DBufferInfo};
+use crate::texture_info::Texture2DBufferInfo;
+use crate::wgpu_resources::{BufferDesc, GpuBuffer, GpuBufferPool};
/// Identifier used to identify a buffer upon retrieval of the data.
///
@@ -16,6 +17,12 @@ struct PendingReadbackRange {
user_data: GpuReadbackUserDataStorage,
}
+#[derive(thiserror::Error, Debug)]
+pub enum GpuReadbackError {
+ #[error("Texture format {0:?} is not supported for readback.")]
+ UnsupportedTextureFormatForReadback(wgpu::TextureFormat),
+}
+
/// A reserved slice for GPU readback.
///
/// Readback needs to happen from a buffer/texture with copy-source usage,
@@ -36,8 +43,8 @@ impl GpuReadbackBuffer {
encoder: &mut wgpu::CommandEncoder,
source: wgpu::ImageCopyTexture<'_>,
copy_extents: glam::UVec2,
- ) {
- self.read_multiple_texture2d(encoder, &[(source, copy_extents)]);
+ ) -> Result<(), GpuReadbackError> {
+ self.read_multiple_texture2d(encoder, &[(source, copy_extents)])
}
/// Reads multiple textures into the same buffer.
@@ -54,11 +61,17 @@ impl GpuReadbackBuffer {
mut self,
encoder: &mut wgpu::CommandEncoder,
sources_and_extents: &[(wgpu::ImageCopyTexture<'_>, glam::UVec2)],
- ) {
+ ) -> Result<(), GpuReadbackError> {
for (source, copy_extents) in sources_and_extents {
let start_offset = wgpu::util::align_to(
self.range_in_chunk.start,
- source.texture.format().describe().block_size as u64,
+ source
+ .texture
+ .format()
+ .block_size(Some(source.aspect))
+ .ok_or(GpuReadbackError::UnsupportedTextureFormatForReadback(
+ source.texture.format(),
+ ))? as u64,
);
let buffer_info = Texture2DBufferInfo::new(source.texture.format(), *copy_extents);
@@ -75,7 +88,7 @@ impl GpuReadbackBuffer {
buffer: &self.chunk_buffer,
layout: wgpu::ImageDataLayout {
offset: start_offset,
- bytes_per_row: NonZeroU32::new(buffer_info.bytes_per_row_padded),
+ bytes_per_row: Some(buffer_info.bytes_per_row_padded),
rows_per_image: None,
},
},
@@ -89,6 +102,7 @@ impl GpuReadbackBuffer {
self.range_in_chunk =
(start_offset + buffer_info.buffer_size_padded)..self.range_in_chunk.end;
}
+ Ok(())
}
// TODO(andreas): Unused & untested so far!
diff --git a/crates/re_renderer/src/allocator/mod.rs b/crates/re_renderer/src/allocator/mod.rs
index 11a092b1b374..382376416d54 100644
--- a/crates/re_renderer/src/allocator/mod.rs
+++ b/crates/re_renderer/src/allocator/mod.rs
@@ -9,7 +9,8 @@ mod uniform_buffer_fill;
pub use cpu_write_gpu_read_belt::{CpuWriteGpuReadBelt, CpuWriteGpuReadBuffer};
pub use gpu_readback_belt::{
- GpuReadbackBelt, GpuReadbackBuffer, GpuReadbackIdentifier, GpuReadbackUserDataStorage,
+ GpuReadbackBelt, GpuReadbackBuffer, GpuReadbackError, GpuReadbackIdentifier,
+ GpuReadbackUserDataStorage,
};
pub use uniform_buffer_fill::{
create_and_fill_uniform_buffer, create_and_fill_uniform_buffer_batch,
diff --git a/crates/re_renderer/src/colormap.rs b/crates/re_renderer/src/colormap.rs
index 15cd98d5dc14..e4a4927ed3fe 100644
--- a/crates/re_renderer/src/colormap.rs
+++ b/crates/re_renderer/src/colormap.rs
@@ -18,6 +18,7 @@ pub enum Colormap {
Plasma = 4,
Turbo = 5,
Viridis = 6,
+ AlbedoTexture = 7,
}
impl Colormap {
@@ -40,6 +41,7 @@ impl std::fmt::Display for Colormap {
Colormap::Plasma => write!(f, "Plasma"),
Colormap::Turbo => write!(f, "Turbo"),
Colormap::Viridis => write!(f, "Viridis"),
+ Colormap::AlbedoTexture => write!(f, "AlbedoTexture"),
}
}
}
@@ -52,6 +54,10 @@ pub fn colormap_srgb(which: Colormap, t: f32) -> [u8; 4] {
Colormap::Plasma => colormap_plasma_srgb(t),
Colormap::Magma => colormap_magma_srgb(t),
Colormap::Inferno => colormap_inferno_srgb(t),
+ Colormap::AlbedoTexture => {
+ re_log::error_once!("Trying to do texture sampling on the CPU");
+ [0; 4]
+ }
}
}
diff --git a/crates/re_renderer/src/config.rs b/crates/re_renderer/src/config.rs
index 91e0315d401d..7b85283b505f 100644
--- a/crates/re_renderer/src/config.rs
+++ b/crates/re_renderer/src/config.rs
@@ -2,6 +2,11 @@
///
/// To reduce complexity, we don't do fine-grained feature checks,
/// but instead support set of features, each a superset of the next.
+///
+/// Tiers are sorted from lowest to highest. Certain tiers may not be possible on a given machine/setup,
+/// but choosing lower tiers is always possible.
+/// Tiers may loosely relate to quality settings, but their primary function is an easier way to
+/// do bundle feature *support* checks.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HardwareTier {
/// Limited feature support as provided by WebGL and native GLES2/OpenGL3(ish).
@@ -34,18 +39,22 @@ impl HardwareTier {
}
}
-impl Default for HardwareTier {
- fn default() -> Self {
- // Use "Basic" tier for actual web but also if someone forces the GL backend!
- if supported_backends() == wgpu::Backends::GL {
- HardwareTier::Gles
- } else {
- HardwareTier::FullWebGpuSupport
+impl HardwareTier {
+ /// Picks the highest possible tier for a given adapter.
+ ///
+ /// Note that it is always possible to pick a lower tier!
+ pub fn from_adapter(adapter: &wgpu::Adapter) -> Self {
+ match adapter.get_info().backend {
+ wgpu::Backend::Vulkan
+ | wgpu::Backend::Metal
+ | wgpu::Backend::Dx12
+ | wgpu::Backend::BrowserWebGpu => HardwareTier::FullWebGpuSupport,
+
+ // Dx11 support in wgpu is sporadic, treat it like GLES to be on the safe side.
+ wgpu::Backend::Dx11 | wgpu::Backend::Gl | wgpu::Backend::Empty => HardwareTier::Gles,
}
}
-}
-impl HardwareTier {
/// Wgpu limits required by the given hardware tier.
pub fn limits(self) -> wgpu::Limits {
wgpu::Limits {
@@ -127,22 +136,19 @@ pub struct RenderContextConfig {
///
/// Other backend might work as well, but lack of support isn't regarded as a bug.
pub fn supported_backends() -> wgpu::Backends {
- // Native.
- // Only use Vulkan & Metal unless explicitly told so since this reduces surfaces and thus surprises.
- //
- // Bunch of cases where it's still useful to switch though:
- // * Some Windows VMs only provide DX12 drivers, observed with Parallels on Apple Silicon
- // * May run into Linux issues that warrant trying out the GL backend.
- //
- // For changing the backend we use standard wgpu env var, i.e. WGPU_BACKEND.
- #[cfg(not(target_arch = "wasm32"))]
- {
+ if cfg!(target_arch = "wasm32") {
+ // Web - WebGL is used automatically when wgpu is compiled with `webgl` feature.
+ wgpu::Backends::GL | wgpu::Backends::BROWSER_WEBGPU
+ } else {
+ // Native.
+ // Only use Vulkan & Metal unless explicitly told so since this reduces surfaces and thus surprises.
+ //
+ // Bunch of cases where it's still useful to switch though:
+ // * Some Windows VMs only provide DX12 drivers, observed with Parallels on Apple Silicon
+ // * May run into Linux issues that warrant trying out the GL backend.
+ //
+ // For changing the backend we use standard wgpu env var, i.e. WGPU_BACKEND.
wgpu::util::backend_bits_from_env()
.unwrap_or(wgpu::Backends::VULKAN | wgpu::Backends::METAL)
}
- // Web - we support only WebGL right now, WebGPU should work but hasn't been tested.
- #[cfg(target_arch = "wasm32")]
- {
- wgpu::Backends::GL
- }
}
diff --git a/crates/re_renderer/src/context.rs b/crates/re_renderer/src/context.rs
index 701d1800d561..a2c8d032b819 100644
--- a/crates/re_renderer/src/context.rs
+++ b/crates/re_renderer/src/context.rs
@@ -106,6 +106,7 @@ impl RenderContext {
const MAX_NUM_INFLIGHT_QUEUE_SUBMISSIONS: usize = 4;
pub fn new(
+ adapter: &wgpu::Adapter,
device: Arc,
queue: Arc,
config: RenderContextConfig,
@@ -138,7 +139,16 @@ impl RenderContext {
config.hardware_tier.features(),
device.features(),
);
- // Can't check downlevel feature flags since they sit on the adapter, not on the device.
+ assert!(adapter.get_downlevel_capabilities().flags.contains(config.hardware_tier.required_downlevel_capabilities().flags),
+ "The given device doesn't support the required downlevel capabilities for the given hardware tier {:?}.
+ Required:
+ {:?}
+ Actual:
+ {:?}",
+ config.hardware_tier,
+ config.hardware_tier.required_downlevel_capabilities(),
+ adapter.get_downlevel_capabilities(),
+ );
// In debug builds, make sure to catch all errors, never crash, and try to
// always let the user find a way to return a poisoned pipeline back into a
@@ -178,6 +188,21 @@ impl RenderContext {
frame_index: 0,
};
+ // Register shader workarounds for the current device.
+ if adapter.get_info().backend == wgpu::Backend::BrowserWebGpu {
+ // Chrome/Tint does not support `@invariant` when targeting Metal.
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1439273
+ // (bug is fixed as of writing, but hasn't hit any public released version yet)
+ // Ignoring it is fine in the cases we use it, it's mostly there to avoid a (correct!) warning in wgpu.
+ gpu_resources
+ .shader_modules
+ .shader_text_workaround_replacements
+ .push((
+ "@invariant @builtin(position)".to_owned(),
+ "@builtin(position)".to_owned(),
+ ));
+ }
+
RenderContext {
device,
queue,
diff --git a/crates/re_renderer/src/draw_phases/mod.rs b/crates/re_renderer/src/draw_phases/mod.rs
index fe7ce542f245..f77d70e9ec5c 100644
--- a/crates/re_renderer/src/draw_phases/mod.rs
+++ b/crates/re_renderer/src/draw_phases/mod.rs
@@ -6,7 +6,8 @@ pub use outlines::{OutlineConfig, OutlineMaskPreference, OutlineMaskProcessor};
mod picking_layer;
pub use picking_layer::{
- PickingLayerId, PickingLayerInstanceId, PickingLayerObjectId, PickingLayerProcessor,
+ PickingLayerError, PickingLayerId, PickingLayerInstanceId, PickingLayerObjectId,
+ PickingLayerProcessor,
};
mod screenshot;
diff --git a/crates/re_renderer/src/draw_phases/picking_layer.rs b/crates/re_renderer/src/draw_phases/picking_layer.rs
index dc5cf38f033f..cd4d6601ebb0 100644
--- a/crates/re_renderer/src/draw_phases/picking_layer.rs
+++ b/crates/re_renderer/src/draw_phases/picking_layer.rs
@@ -13,11 +13,12 @@ use crate::{
allocator::create_and_fill_uniform_buffer,
global_bindings::FrameUniformBuffer,
include_shader_module,
+ texture_info::Texture2DBufferInfo,
view_builder::ViewBuilder,
wgpu_resources::{
BindGroupDesc, BindGroupEntry, BindGroupLayoutDesc, GpuBindGroup, GpuRenderPipelineHandle,
GpuTexture, GpuTextureHandle, PipelineLayoutDesc, PoolError, RenderPipelineDesc,
- Texture2DBufferInfo, TextureDesc, WgpuResourcePools,
+ TextureDesc, WgpuResourcePools,
},
DebugLabel, GpuReadbackBuffer, GpuReadbackIdentifier, IntRect, RenderContext,
};
@@ -132,6 +133,15 @@ pub fn pixel_coord_to_ndc(coord: glam::Vec2, target_resolution: glam::Vec2) -> g
)
}
+#[derive(thiserror::Error, Debug)]
+pub enum PickingLayerError {
+ #[error(transparent)]
+ ReadbackError(#[from] crate::allocator::GpuReadbackError),
+
+ #[error(transparent)]
+ ResourcePoolError(#[from] PoolError),
+}
+
/// Manages the rendering of the picking layer pass, its render targets & readback buffer.
///
/// The view builder creates this for every frame that requests a picking result.
@@ -278,8 +288,10 @@ impl PickingLayerProcessor {
// Offset of the depth buffer in the readback buffer needs to be aligned to size of a depth pixel.
// This is "trivially true" if the size of the depth format is a multiple of the size of the id format.
debug_assert!(
- Self::PICKING_LAYER_FORMAT.describe().block_size
- % Self::PICKING_LAYER_DEPTH_FORMAT.describe().block_size
+ Self::PICKING_LAYER_FORMAT.block_size(None).unwrap()
+ % Self::PICKING_LAYER_DEPTH_FORMAT
+ .block_size(Some(wgpu::TextureAspect::DepthOnly))
+ .unwrap()
== 0
);
let buffer_size = row_info_id.buffer_size_padded + row_info_depth.buffer_size_padded;
@@ -342,7 +354,7 @@ impl PickingLayerProcessor {
self,
encoder: &mut wgpu::CommandEncoder,
pools: &WgpuResourcePools,
- ) -> Result<(), PoolError> {
+ ) -> Result<(), PickingLayerError> {
let extent = glam::uvec2(
self.picking_target.texture.width(),
self.picking_target.texture.height(),
@@ -373,12 +385,16 @@ impl PickingLayerProcessor {
texture: &readable_depth_texture.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
- aspect: wgpu::TextureAspect::All,
+ aspect: if self.depth_readback_workaround.is_some() {
+ wgpu::TextureAspect::All
+ } else {
+ wgpu::TextureAspect::DepthOnly
+ },
},
extent,
),
],
- );
+ )?;
Ok(())
}
@@ -401,11 +417,13 @@ impl PickingLayerProcessor {
.readback_data::>(identifier, |data, metadata| {
// Assert that our texture data reinterpretation works out from a pixel size point of view.
debug_assert_eq!(
- Self::PICKING_LAYER_DEPTH_FORMAT.describe().block_size as usize,
- std::mem::size_of::()
+ Self::PICKING_LAYER_DEPTH_FORMAT
+ .block_size(Some(wgpu::TextureAspect::DepthOnly))
+ .unwrap(),
+ std::mem::size_of::() as u32
);
debug_assert_eq!(
- Self::PICKING_LAYER_FORMAT.describe().block_size as usize,
+ Self::PICKING_LAYER_FORMAT.block_size(None).unwrap() as usize,
std::mem::size_of::()
);
@@ -432,8 +450,8 @@ impl PickingLayerProcessor {
// See https://github.com/gfx-rs/wgpu/issues/3644
debug_assert_eq!(
DepthReadbackWorkaround::READBACK_FORMAT
- .describe()
- .block_size as usize,
+ .block_size(None)
+ .unwrap() as usize,
std::mem::size_of::() * 4
);
picking_depth_data = picking_depth_data.into_iter().step_by(4).collect();
diff --git a/crates/re_renderer/src/draw_phases/screenshot.rs b/crates/re_renderer/src/draw_phases/screenshot.rs
index 68c05b3b545c..79cac54c1a6a 100644
--- a/crates/re_renderer/src/draw_phases/screenshot.rs
+++ b/crates/re_renderer/src/draw_phases/screenshot.rs
@@ -11,7 +11,9 @@
//! Or alternatively try to render the images in several tiles π€. In any case this would greatly improve quality!
use crate::{
- wgpu_resources::{GpuTexture, Texture2DBufferInfo, TextureDesc},
+ allocator::GpuReadbackError,
+ texture_info::Texture2DBufferInfo,
+ wgpu_resources::{GpuTexture, TextureDesc},
DebugLabel, GpuReadbackBuffer, GpuReadbackIdentifier, RenderContext,
};
@@ -95,7 +97,10 @@ impl ScreenshotProcessor {
pass
}
- pub fn end_render_pass(self, encoder: &mut wgpu::CommandEncoder) {
+ pub fn end_render_pass(
+ self,
+ encoder: &mut wgpu::CommandEncoder,
+ ) -> Result<(), GpuReadbackError> {
self.screenshot_readback_buffer.read_texture2d(
encoder,
wgpu::ImageCopyTexture {
@@ -108,7 +113,7 @@ impl ScreenshotProcessor {
self.screenshot_texture.texture.width(),
self.screenshot_texture.texture.height(),
),
- );
+ )
}
/// Returns the oldest received screenshot results for a given identifier and user data type.
diff --git a/crates/re_renderer/src/global_bindings.rs b/crates/re_renderer/src/global_bindings.rs
index c00ad0315c67..33fdfa4b2191 100644
--- a/crates/re_renderer/src/global_bindings.rs
+++ b/crates/re_renderer/src/global_bindings.rs
@@ -82,14 +82,14 @@ impl GlobalBindings {
// Sampler without any filtering.
wgpu::BindGroupLayoutEntry {
binding: 1,
- visibility: wgpu::ShaderStages::FRAGMENT,
+ visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
count: None,
},
// Trilinear sampler.
wgpu::BindGroupLayoutEntry {
binding: 2,
- visibility: wgpu::ShaderStages::FRAGMENT,
+ visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
diff --git a/crates/re_renderer/src/importer/gltf.rs b/crates/re_renderer/src/importer/gltf.rs
index 9d628931c6f3..ba259c9f30bb 100644
--- a/crates/re_renderer/src/importer/gltf.rs
+++ b/crates/re_renderer/src/importer/gltf.rs
@@ -68,8 +68,16 @@ pub fn load_gltf_from_buffer(
};
images_as_textures.push(
- ctx.texture_manager_2d
- .create(&mut ctx.gpu_resources.textures, &texture),
+ match ctx
+ .texture_manager_2d
+ .create(&mut ctx.gpu_resources.textures, &texture)
+ {
+ Ok(texture) => texture,
+ Err(err) => {
+ re_log::error!("Failed to create texture: {err}");
+ ctx.texture_manager_2d.white_texture_unorm_handle().clone()
+ }
+ },
);
}
diff --git a/crates/re_renderer/src/lib.rs b/crates/re_renderer/src/lib.rs
index 770c0589f7fe..1932e1aeb9e2 100644
--- a/crates/re_renderer/src/lib.rs
+++ b/crates/re_renderer/src/lib.rs
@@ -12,6 +12,7 @@ pub mod importer;
pub mod mesh;
pub mod renderer;
pub mod resource_managers;
+pub mod texture_info;
pub mod view_builder;
mod allocator;
diff --git a/crates/re_renderer/src/renderer/debug_overlay.rs b/crates/re_renderer/src/renderer/debug_overlay.rs
index 6e615cd4a710..5b78ac3e3139 100644
--- a/crates/re_renderer/src/renderer/debug_overlay.rs
+++ b/crates/re_renderer/src/renderer/debug_overlay.rs
@@ -49,6 +49,12 @@ pub struct DebugOverlayRenderer {
bind_group_layout: GpuBindGroupLayoutHandle,
}
+#[derive(thiserror::Error, Debug)]
+pub enum DebugOverlayError {
+ #[error("Can't display texture with format: {0:?}")]
+ UnsupportedTextureFormat(wgpu::TextureFormat),
+}
+
/// Debug overlay for quick & dirty display of texture contents.
///
/// Executed as part of the composition draw phase in order to allow "direct" output to the screen.
@@ -70,7 +76,7 @@ impl DebugOverlayDrawData {
debug_texture: &GpuTexture,
screen_resolution: glam::UVec2,
overlay_rect: IntRect,
- ) -> Self {
+ ) -> Result {
let mut renderers = ctx.renderers.write();
let debug_overlay = renderers.get_or_create::<_, DebugOverlayRenderer>(
&ctx.shared_renderer_data,
@@ -79,13 +85,22 @@ impl DebugOverlayDrawData {
&mut ctx.resolver,
);
- let mode = match debug_texture.texture.format().describe().sample_type {
- wgpu::TextureSampleType::Depth | wgpu::TextureSampleType::Float { .. } => {
+ let mode = match debug_texture
+ .texture
+ .format()
+ .sample_type(Some(wgpu::TextureAspect::All))
+ {
+ Some(wgpu::TextureSampleType::Depth | wgpu::TextureSampleType::Float { .. }) => {
gpu_data::DebugOverlayMode::ShowFloatTexture
}
- wgpu::TextureSampleType::Sint | wgpu::TextureSampleType::Uint => {
+ Some(wgpu::TextureSampleType::Sint | wgpu::TextureSampleType::Uint) => {
gpu_data::DebugOverlayMode::ShowUintTexture
}
+ None => {
+ return Err(DebugOverlayError::UnsupportedTextureFormat(
+ debug_texture.texture.format(),
+ ))
+ }
};
let uniform_buffer_binding = create_and_fill_uniform_buffer(
@@ -112,7 +127,7 @@ impl DebugOverlayDrawData {
),
};
- DebugOverlayDrawData {
+ Ok(DebugOverlayDrawData {
bind_group: ctx.gpu_resources.bind_groups.alloc(
&ctx.device,
&ctx.gpu_resources,
@@ -126,7 +141,7 @@ impl DebugOverlayDrawData {
layout: debug_overlay.bind_group_layout,
},
),
- }
+ })
}
}
diff --git a/crates/re_renderer/src/renderer/depth_cloud.rs b/crates/re_renderer/src/renderer/depth_cloud.rs
index 285c0a2f9fd0..676d37e45391 100644
--- a/crates/re_renderer/src/renderer/depth_cloud.rs
+++ b/crates/re_renderer/src/renderer/depth_cloud.rs
@@ -11,18 +11,18 @@
//! The vertex shader backprojects the depth texture using the user-specified intrinsics, and then
//! behaves pretty much exactly like our point cloud renderer (see [`point_cloud.rs`]).
+use itertools::Itertools;
use smallvec::smallvec;
use crate::{
allocator::create_and_fill_uniform_buffer_batch,
draw_phases::{DrawPhase, OutlineMaskProcessor},
include_shader_module,
- resource_managers::ResourceManagerError,
+ resource_managers::{GpuTexture2D, ResourceManagerError},
view_builder::ViewBuilder,
wgpu_resources::{
BindGroupDesc, BindGroupEntry, BindGroupLayoutDesc, GpuBindGroup, GpuBindGroupLayoutHandle,
- GpuRenderPipelineHandle, GpuTexture, PipelineLayoutDesc, RenderPipelineDesc,
- Texture2DBufferInfo, TextureDesc,
+ GpuRenderPipelineHandle, GpuTexture, PipelineLayoutDesc, RenderPipelineDesc, TextureDesc,
},
Colormap, OutlineMaskPreference, PickingLayerObjectId, PickingLayerProcessor,
};
@@ -34,9 +34,17 @@ use super::{
// ---
+#[derive(Debug, Clone, Copy)]
+enum AlbedoColorSpace {
+ Rgb,
+ Mono,
+}
+
mod gpu_data {
use crate::{wgpu_buffer_types, PickingLayerObjectId};
+ use super::{AlbedoColorSpace, DepthCloudAlbedoData};
+
/// Keep in sync with mirror in `depth_cloud.wgsl.`
#[repr(C, align(256))]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
@@ -50,7 +58,7 @@ mod gpu_data {
pub picking_layer_object_id: PickingLayerObjectId,
/// Multiplier to get world-space depth from whatever is in the texture.
- pub world_depth_from_texture_value: f32,
+ pub world_depth_from_texture_depth: f32,
/// Point radius is calculated as world-space depth times this value.
pub point_radius_from_world_depth: f32,
@@ -61,10 +69,13 @@ mod gpu_data {
/// Which colormap should be used.
pub colormap: u32,
+ /// Is the albedo texture rgb or mono
+ pub albedo_color_space: wgpu_buffer_types::U32RowPadded,
+
/// Changes over different draw-phases.
pub radius_boost_in_ui_points: wgpu_buffer_types::F32RowPadded,
- pub end_padding: [wgpu_buffer_types::PaddingRow; 16 - 4 - 3 - 1 - 1 - 1],
+ pub end_padding: [wgpu_buffer_types::PaddingRow; 16 - 4 - 3 - 1 - 1 - 1 - 1],
}
impl DepthCloudInfoUBO {
@@ -75,31 +86,35 @@ mod gpu_data {
let super::DepthCloud {
world_from_obj,
depth_camera_intrinsics,
- world_depth_from_data_depth,
+ world_depth_from_texture_depth,
point_radius_from_world_depth,
max_depth_in_world,
depth_dimensions: _,
- depth_data,
+ depth_texture: _,
colormap,
outline_mask_id,
picking_object_id,
+ albedo_dimensions: _,
+ albedo_data: _,
} = depth_cloud;
- let user_depth_from_texture_value = match depth_data {
- super::DepthCloudDepthData::U16(_) => 65535.0, // un-normalize
- super::DepthCloudDepthData::F32(_) => 1.0,
- };
- let world_depth_from_texture_value =
- world_depth_from_data_depth * user_depth_from_texture_value;
-
Self {
world_from_obj: (*world_from_obj).into(),
depth_camera_intrinsics: (*depth_camera_intrinsics).into(),
outline_mask_id: outline_mask_id.0.unwrap_or_default().into(),
- world_depth_from_texture_value,
+ world_depth_from_texture_depth: *world_depth_from_texture_depth,
point_radius_from_world_depth: *point_radius_from_world_depth,
max_depth_in_world: *max_depth_in_world,
colormap: *colormap as u32,
+ albedo_color_space: (depth_cloud
+ .albedo_data
+ .as_ref()
+ .map(|albedo_data| match albedo_data {
+ DepthCloudAlbedoData::Mono8(_) => AlbedoColorSpace::Mono,
+ _ => AlbedoColorSpace::Rgb,
+ })
+ .unwrap_or(AlbedoColorSpace::Rgb) as u32)
+ .into(),
radius_boost_in_ui_points: radius_boost_in_ui_points.into(),
picking_layer_object_id: *picking_object_id,
end_padding: Default::default(),
@@ -108,30 +123,17 @@ mod gpu_data {
}
}
-/// The raw data from a depth texture.
-///
-/// This is either `u16` or `f32` values; in both cases the data will be uploaded to the shader
-/// as-is.
-/// For `u16`s, this results in a `Depth16Unorm` texture, otherwise an `R32Float`.
-/// The reason we normalize `u16` is so that the shader can use a `float` texture in both cases.
-/// However, it means we need to multiply the sampled value by `65535.0` in the shader to get
-/// the actual depth.
-///
-/// The shader assumes that this is normalized, linear, non-flipped depth using the camera
-/// position as reference point (not the camera plane!).
+/// The raw data for the (optional) albedo texture.
//
-// TODO(cmc): support more depth data types.
-// TODO(cmc): expose knobs to linearize/normalize/flip/cam-to-plane depth.
+// TODO(cmc): support more albedo data types.
+// TODO(cmc): arrow buffers for u8...
#[derive(Debug, Clone)]
-pub enum DepthCloudDepthData {
- U16(crate::Buffer),
- F32(crate::Buffer),
-}
-
-impl Default for DepthCloudDepthData {
- fn default() -> Self {
- Self::F32(Default::default())
- }
+pub enum DepthCloudAlbedoData {
+ Rgb8(Vec),
+ Rgb8Srgb(Vec),
+ Rgba8(Vec),
+ Rgba8Srgb(Vec),
+ Mono8(Vec),
}
pub struct DepthCloud {
@@ -143,8 +145,8 @@ pub struct DepthCloud {
/// Only supports pinhole cameras at the moment.
pub depth_camera_intrinsics: glam::Mat3,
- /// Multiplier to get world-space depth from whatever is in [`Self::depth_data`].
- pub world_depth_from_data_depth: f32,
+ /// Multiplier to get world-space depth from whatever is in [`Self::depth_texture`].
+ pub world_depth_from_texture_depth: f32,
/// Point radius is calculated as world-space depth times this value.
pub point_radius_from_world_depth: f32,
@@ -155,10 +157,10 @@ pub struct DepthCloud {
/// The dimensions of the depth texture in pixels.
pub depth_dimensions: glam::UVec2,
- /// The actual data from the depth texture.
+ /// The actual data for the depth texture.
///
- /// See [`DepthCloudDepthData`] for more information.
- pub depth_data: DepthCloudDepthData,
+ /// Only textures with sample type `Float` are supported.
+ pub depth_texture: GpuTexture2D,
/// Configures color mapping mode.
pub colormap: Colormap,
@@ -168,6 +170,16 @@ pub struct DepthCloud {
/// Picking object id that applies for the entire depth cloud.
pub picking_object_id: PickingLayerObjectId,
+
+ /// The dimensions of the (optional) albedo texture in pixels.
+ ///
+ /// Irrelevant if [`Self::albedo_data`] isn't set.
+ pub albedo_dimensions: glam::UVec2,
+
+ /// The actual data for the (optional) albedo texture.
+ ///
+ /// If set, takes precedence over [`Self::colormap`].
+ pub albedo_data: Option,
}
impl DepthCloud {
@@ -192,7 +204,7 @@ impl DepthCloud {
for corner in corners {
let depth = corner.z;
- let pos_in_obj = ((corner.truncate() - offset) * depth / focal_length).extend(depth);
+ let pos_in_obj = (((corner.truncate() - offset) * depth) / focal_length).extend(depth);
let pos_in_world = self.world_from_obj.project_point3(pos_in_obj);
bbox.extend(pos_in_world);
}
@@ -223,11 +235,20 @@ impl DrawData for DepthCloudDrawData {
type Renderer = DepthCloudRenderer;
}
+#[derive(thiserror::Error, Debug)]
+pub enum DepthCloudDrawDataError {
+ #[error("Depth texture format was {0:?}, only formats with sample type float are supported")]
+ InvalidDepthTextureFormat(wgpu::TextureFormat),
+
+ #[error(transparent)]
+ ResourceManagerError(#[from] ResourceManagerError),
+}
+
impl DepthCloudDrawData {
pub fn new(
ctx: &mut RenderContext,
depth_clouds: &DepthClouds,
- ) -> Result {
+ ) -> Result {
crate::profile_function!();
let DepthClouds {
@@ -276,56 +297,84 @@ impl DepthCloudDrawData {
depth_cloud_ubo_binding_outlines,
depth_cloud_ubo_binding_opaque
) {
- let depth_texture = match &depth_cloud.depth_data {
- DepthCloudDepthData::U16(data) => {
- if cfg!(target_arch = "wasm32") {
- // Web: manual normalization because Depth16Unorm textures aren't supported on
- // the web (and won't ever be on the WebGL backend, see
- // https://github.com/gfx-rs/wgpu/issues/3537).
- //
- // TODO(cmc): use an RG8 texture and unpack it manually in the shader instead.
- use itertools::Itertools as _;
- let dataf32 = data
- .as_slice()
- .iter()
- .map(|d| *d as f32 / u16::MAX as f32)
+ if !matches!(
+ depth_cloud.depth_texture.format().sample_type(None),
+ Some(wgpu::TextureSampleType::Float { filterable: _ })
+ ) {
+ return Err(DepthCloudDrawDataError::InvalidDepthTextureFormat(
+ depth_cloud.depth_texture.format(),
+ ));
+ }
+ let albedo_texture = depth_cloud
+ .albedo_data
+ .as_ref()
+ .map_or_else(|| {
+ create_and_upload_texture(
+ ctx,
+ (1, 1).into(),
+ wgpu::TextureFormat::Rgba8Unorm,
+ [0u8; 4].as_slice(),
+ )
+ }, |data| match data {
+ DepthCloudAlbedoData::Rgba8(data) => create_and_upload_texture(
+ ctx,
+ depth_cloud.albedo_dimensions,
+ wgpu::TextureFormat::Rgba8Unorm,
+ data.as_slice(),
+ ),
+ DepthCloudAlbedoData::Rgba8Srgb(data) => create_and_upload_texture(
+ ctx,
+ depth_cloud.albedo_dimensions,
+ wgpu::TextureFormat::Rgba8UnormSrgb,
+ data.as_slice(),
+ ),
+ DepthCloudAlbedoData::Rgb8(data) => {
+ let data = data
+ .chunks(3)
+ .into_iter()
+ .flat_map(|c| [c[0], c[1], c[2], 255])
.collect_vec();
create_and_upload_texture(
ctx,
- depth_cloud,
- dataf32.as_slice(),
- wgpu::TextureFormat::R32Float,
+ depth_cloud.albedo_dimensions,
+ wgpu::TextureFormat::Rgba8Unorm,
+ data.as_slice(),
)
- } else {
- // Native: We use Depth16Unorm over R16Unorm because the latter is behind a feature flag and doesn't work on WebGPU.
+ }
+ DepthCloudAlbedoData::Rgb8Srgb(data) => {
+ let data = data
+ .chunks(3)
+ .into_iter()
+ .flat_map(|c| [c[0], c[1], c[2], 255])
+ .collect_vec();
create_and_upload_texture(
ctx,
- depth_cloud,
+ depth_cloud.albedo_dimensions,
+ wgpu::TextureFormat::Rgba8UnormSrgb,
data.as_slice(),
- wgpu::TextureFormat::Depth16Unorm,
)
}
- }
- DepthCloudDepthData::F32(data) => create_and_upload_texture(
- ctx,
- depth_cloud,
- data.as_slice(),
- wgpu::TextureFormat::R32Float,
- ),
- };
+ DepthCloudAlbedoData::Mono8(data) => create_and_upload_texture(
+ ctx,
+ depth_cloud.albedo_dimensions,
+ wgpu::TextureFormat::R8Unorm,
+ data.as_slice(),
+ ),
+ });
let mk_bind_group = |label, ubo: BindGroupEntry| {
ctx.gpu_resources.bind_groups.alloc(
&ctx.device,
&ctx.gpu_resources,
- &BindGroupDesc {
+ &(BindGroupDesc {
label,
entries: smallvec![
ubo,
- BindGroupEntry::DefaultTextureView(depth_texture.handle),
+ BindGroupEntry::DefaultTextureView(depth_cloud.depth_texture.handle),
+ BindGroupEntry::DefaultTextureView(albedo_texture.handle)
],
layout: bg_layout,
- },
+ }),
)
};
@@ -345,78 +394,51 @@ impl DepthCloudDrawData {
}
fn create_and_upload_texture(
- ctx: &mut RenderContext,
- depth_cloud: &DepthCloud,
+ ctx: &RenderContext,
+ dimensions: glam::UVec2,
+ format: wgpu::TextureFormat,
data: &[T],
- depth_format: wgpu::TextureFormat,
) -> GpuTexture {
crate::profile_function!();
- let depth_texture_size = wgpu::Extent3d {
- width: depth_cloud.depth_dimensions.x,
- height: depth_cloud.depth_dimensions.y,
+ let texture_size = wgpu::Extent3d {
+ width: dimensions.x,
+ height: dimensions.y,
depth_or_array_layers: 1,
};
- let depth_texture_desc = TextureDesc {
- label: "depth_texture".into(),
- size: depth_texture_size,
+ let texture_desc = TextureDesc {
+ label: "texture".into(),
+ size: texture_size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
- format: depth_format,
+ format,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
};
- let depth_texture = ctx
- .gpu_resources
- .textures
- .alloc(&ctx.device, &depth_texture_desc);
-
- // Not supporting compressed formats here.
- debug_assert!(depth_texture_desc.format.describe().block_dimensions == (1, 1));
-
- let buffer_info =
- Texture2DBufferInfo::new(depth_texture_desc.format, depth_cloud.depth_dimensions);
-
- // TODO(andreas): CpuGpuWriteBelt should make it easier to do this.
- let bytes_padding_per_row =
- (buffer_info.bytes_per_row_padded - buffer_info.bytes_per_row_unpadded) as usize;
- // Sanity check the padding size. If this happens something is seriously wrong, as it would imply
- // that we can't express the required alignment with the block size.
- debug_assert!(
- bytes_padding_per_row % std::mem::size_of::() == 0,
- "Padding is not a multiple of pixel size. Can't correctly pad the texture data"
- );
+ let texture = ctx.gpu_resources.textures.alloc(&ctx.device, &texture_desc);
+
+ let format_info = texture_desc.format;
+ let width_blocks = dimensions.x / (format_info.block_dimensions().0 as u32);
- let mut depth_texture_staging = ctx.cpu_write_gpu_read_belt.lock().allocate::(
+ let mut texture_staging = ctx.cpu_write_gpu_read_belt.lock().allocate::(
&ctx.device,
&ctx.gpu_resources.buffers,
- buffer_info.buffer_size_padded as usize / std::mem::size_of::(),
+ data.len(),
);
+ texture_staging.extend_from_slice(data);
- // Fill with a single copy if possible, otherwise do multiple, filling in padding.
- if bytes_padding_per_row == 0 {
- depth_texture_staging.extend_from_slice(data);
- } else {
- let num_pixel_padding_per_row = bytes_padding_per_row / std::mem::size_of::();
- for row in data.chunks(depth_texture_desc.size.width as usize) {
- depth_texture_staging.extend_from_slice(row);
- depth_texture_staging
- .extend(std::iter::repeat(T::zeroed()).take(num_pixel_padding_per_row));
- }
- }
-
- depth_texture_staging.copy_to_texture2d(
+ texture_staging.copy_to_texture2d(
ctx.active_frame.before_view_builder_encoder.lock().get(),
wgpu::ImageCopyTexture {
- texture: &depth_texture.inner.texture,
+ texture: &texture.inner.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
- depth_cloud.depth_dimensions,
+ glam::UVec2::new(texture_size.width, texture_size.height),
);
- depth_texture
+ texture
}
pub struct DepthCloudRenderer {
@@ -447,7 +469,7 @@ impl Renderer for DepthCloudRenderer {
let bind_group_layout = pools.bind_group_layouts.get_or_create(
device,
- &BindGroupLayoutDesc {
+ &(BindGroupLayoutDesc {
label: "depth_cloud_bg_layout".into(),
entries: vec![
wgpu::BindGroupLayoutEntry {
@@ -473,16 +495,26 @@ impl Renderer for DepthCloudRenderer {
},
count: None,
},
+ wgpu::BindGroupLayoutEntry {
+ binding: 2,
+ visibility: wgpu::ShaderStages::VERTEX,
+ ty: wgpu::BindingType::Texture {
+ sample_type: wgpu::TextureSampleType::Float { filterable: true },
+ view_dimension: wgpu::TextureViewDimension::D2,
+ multisampled: false,
+ },
+ count: None,
+ },
],
- },
+ }),
);
let pipeline_layout = pools.pipeline_layouts.get_or_create(
device,
- &PipelineLayoutDesc {
+ &(PipelineLayoutDesc {
label: "depth_cloud_rp_layout".into(),
entries: vec![shared_data.global_bindings.layout, bind_group_layout],
- },
+ }),
&pools.bind_group_layouts,
);
@@ -521,20 +553,20 @@ impl Renderer for DepthCloudRenderer {
);
let render_pipeline_picking_layer = pools.render_pipelines.get_or_create(
device,
- &RenderPipelineDesc {
+ &(RenderPipelineDesc {
label: "DepthCloudRenderer::render_pipeline_picking_layer".into(),
fragment_entrypoint: "fs_main_picking_layer".into(),
render_targets: smallvec![Some(PickingLayerProcessor::PICKING_LAYER_FORMAT.into())],
depth_stencil: PickingLayerProcessor::PICKING_LAYER_DEPTH_STATE,
multisample: PickingLayerProcessor::PICKING_LAYER_MSAA_STATE,
..render_pipeline_desc_color.clone()
- },
+ }),
&pools.pipeline_layouts,
&pools.shader_modules,
);
let render_pipeline_outline_mask = pools.render_pipelines.get_or_create(
device,
- &RenderPipelineDesc {
+ &(RenderPipelineDesc {
label: "DepthCloudRenderer::render_pipeline_outline_mask".into(),
fragment_entrypoint: "fs_main_outline_mask".into(),
render_targets: smallvec![Some(OutlineMaskProcessor::MASK_FORMAT.into())],
@@ -544,7 +576,7 @@ impl Renderer for DepthCloudRenderer {
shared_data.config.hardware_tier,
),
..render_pipeline_desc_color
- },
+ }),
&pools.pipeline_layouts,
&pools.shader_modules,
);
diff --git a/crates/re_renderer/src/renderer/lines.rs b/crates/re_renderer/src/renderer/lines.rs
index cf1222acfe1b..a46571928c63 100644
--- a/crates/re_renderer/src/renderer/lines.rs
+++ b/crates/re_renderer/src/renderer/lines.rs
@@ -104,10 +104,7 @@
//! * note that this would let us remove the degenerated quads between lines, making the approach cleaner and removing the "restart bit"
//!
-use std::{
- num::{NonZeroU32, NonZeroU64},
- ops::Range,
-};
+use std::{num::NonZeroU64, ops::Range};
use bitflags::bitflags;
use bytemuck::Zeroable;
@@ -479,7 +476,7 @@ impl LineDrawData {
bytemuck::cast_slice(&position_data_staging),
wgpu::ImageDataLayout {
offset: 0,
- bytes_per_row: NonZeroU32::new(
+ bytes_per_row: Some(
POSITION_TEXTURE_SIZE * std::mem::size_of::() as u32,
),
rows_per_image: None,
@@ -529,7 +526,7 @@ impl LineDrawData {
bytemuck::cast_slice(&line_strip_info_staging),
wgpu::ImageDataLayout {
offset: 0,
- bytes_per_row: NonZeroU32::new(
+ bytes_per_row: Some(
LINE_STRIP_TEXTURE_SIZE
* std::mem::size_of::() as u32,
),
diff --git a/crates/re_renderer/src/renderer/mod.rs b/crates/re_renderer/src/renderer/mod.rs
index 3b4284bc2a6d..b8b2bc508968 100644
--- a/crates/re_renderer/src/renderer/mod.rs
+++ b/crates/re_renderer/src/renderer/mod.rs
@@ -15,7 +15,7 @@ pub use point_cloud::{
mod depth_cloud;
pub use self::depth_cloud::{
- DepthCloud, DepthCloudDepthData, DepthCloudDrawData, DepthCloudRenderer, DepthClouds,
+ DepthCloud, DepthCloudAlbedoData, DepthCloudDrawData, DepthCloudRenderer, DepthClouds,
};
mod test_triangle;
@@ -35,7 +35,7 @@ mod compositor;
pub(crate) use compositor::CompositorDrawData;
mod debug_overlay;
-pub use debug_overlay::{DebugOverlayDrawData, DebugOverlayRenderer};
+pub use debug_overlay::{DebugOverlayDrawData, DebugOverlayError, DebugOverlayRenderer};
use crate::{
context::{RenderContext, SharedRendererData},
diff --git a/crates/re_renderer/src/renderer/point_cloud.rs b/crates/re_renderer/src/renderer/point_cloud.rs
index 639db5a17ceb..03611b80a256 100644
--- a/crates/re_renderer/src/renderer/point_cloud.rs
+++ b/crates/re_renderer/src/renderer/point_cloud.rs
@@ -130,7 +130,7 @@ pub struct PointCloudBatchInfo {
/// Defines an outline mask for an individual vertex ranges.
///
- /// Vertex ranges are *not* relative within the current batch, but relates to the draw data vertex buffer.
+ /// Vertex ranges are relative within the current batch.
///
/// Having many of these individual outline masks can be slow as they require each their own uniform buffer & draw call.
/// This feature is meant for a limited number of "extra selections"
@@ -455,6 +455,8 @@ impl PointCloudDrawData {
));
for (range, _) in &batch_info.additional_outline_mask_ids_vertex_ranges {
+ let range = (range.start + start_point_for_next_batch)
+ ..(range.end + start_point_for_next_batch);
batches_internal.push(point_renderer.create_point_cloud_batch(
ctx,
format!("{:?} strip-only {:?}", batch_info.label, range).into(),
diff --git a/crates/re_renderer/src/renderer/rectangles.rs b/crates/re_renderer/src/renderer/rectangles.rs
index 799655ecf78c..8b380a30a2bd 100644
--- a/crates/re_renderer/src/renderer/rectangles.rs
+++ b/crates/re_renderer/src/renderer/rectangles.rs
@@ -19,6 +19,7 @@ use crate::{
draw_phases::{DrawPhase, OutlineMaskProcessor},
include_shader_module,
resource_managers::{GpuTexture2D, ResourceManagerError},
+ texture_info,
view_builder::ViewBuilder,
wgpu_resources::{
BindGroupDesc, BindGroupEntry, BindGroupLayoutDesc, GpuBindGroup, GpuBindGroupLayoutHandle,
@@ -33,7 +34,7 @@ use super::{
};
/// Texture filter setting for magnification (a texel covers several pixels).
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
pub enum TextureFilterMag {
Linear,
Nearest,
@@ -41,7 +42,7 @@ pub enum TextureFilterMag {
}
/// Texture filter setting for minification (several texels fall to one pixel).
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
pub enum TextureFilterMin {
Linear,
Nearest,
@@ -98,6 +99,7 @@ impl ColormappedTexture {
}
}
+#[derive(Clone)]
pub struct TexturedRect {
/// Top left corner position in world space.
pub top_left_corner_position: glam::Vec3,
@@ -114,6 +116,7 @@ pub struct TexturedRect {
pub options: RectangleOptions,
}
+#[derive(Clone)]
pub struct RectangleOptions {
pub texture_filter_magnification: TextureFilterMag,
pub texture_filter_minification: TextureFilterMin,
@@ -166,7 +169,7 @@ pub enum RectangleError {
}
mod gpu_data {
- use crate::wgpu_buffer_types;
+ use crate::{texture_info, wgpu_buffer_types};
use super::{ColorMapper, RectangleError, TexturedRect};
@@ -214,9 +217,11 @@ mod gpu_data {
}
impl UniformBuffer {
- pub fn from_textured_rect(rectangle: &super::TexturedRect) -> Result {
+ pub fn from_textured_rect(
+ rectangle: &super::TexturedRect,
+ device_features: wgpu::Features,
+ ) -> Result {
let texture_format = rectangle.colormapped_texture.texture.format();
- let texture_info = texture_format.describe();
let TexturedRect {
top_left_corner_position,
@@ -241,25 +246,25 @@ mod gpu_data {
outline_mask,
} = options;
- let sample_type = match texture_info.sample_type {
- wgpu::TextureSampleType::Float { .. } => {
- if super::is_float_filterable(&texture_format) {
+ let sample_type = match texture_format.sample_type(None) {
+ Some(wgpu::TextureSampleType::Float { .. }) => {
+ if texture_info::is_float_filterable(texture_format, device_features) {
SAMPLE_TYPE_FLOAT_FILTER
} else {
SAMPLE_TYPE_FLOAT_NOFILTER
}
}
- wgpu::TextureSampleType::Depth => {
+ Some(wgpu::TextureSampleType::Sint) => SAMPLE_TYPE_SINT_NOFILTER,
+ Some(wgpu::TextureSampleType::Uint) => SAMPLE_TYPE_UINT_NOFILTER,
+ _ => {
return Err(RectangleError::DepthTexturesNotSupported);
}
- wgpu::TextureSampleType::Sint => SAMPLE_TYPE_SINT_NOFILTER,
- wgpu::TextureSampleType::Uint => SAMPLE_TYPE_UINT_NOFILTER,
};
let mut colormap_function = 0;
let color_mapper_int;
- match texture_info.components {
+ match texture_info::num_texture_components(texture_format) {
1 => match color_mapper {
Some(ColorMapper::Function(colormap)) => {
color_mapper_int = COLOR_MAPPER_FUNCTION;
@@ -352,7 +357,7 @@ impl RectangleDrawData {
// TODO(emilk): continue on error (skipping just that rectangle)?
let uniform_buffers: Vec<_> = rectangles
.iter()
- .map(gpu_data::UniformBuffer::from_textured_rect)
+ .map(|rect| gpu_data::UniformBuffer::from_textured_rect(rect, ctx.device.features()))
.try_collect()?;
let uniform_buffer_bindings = create_and_fill_uniform_buffer_batch(
@@ -387,10 +392,9 @@ impl RectangleDrawData {
let texture = &rectangle.colormapped_texture.texture;
let texture_format = texture.creation_desc.format;
- let texture_description = texture_format.describe();
- if texture_description.required_features != Default::default() {
+ if texture_format.required_features() != Default::default() {
return Err(RectangleError::SpecialFeatures(
- texture_description.required_features,
+ texture_format.required_features(),
));
}
@@ -400,23 +404,23 @@ impl RectangleDrawData {
let mut texture_sint = ctx.texture_manager_2d.zeroed_texture_sint().handle;
let mut texture_uint = ctx.texture_manager_2d.zeroed_texture_uint().handle;
- match texture_description.sample_type {
- wgpu::TextureSampleType::Float { .. } => {
- if is_float_filterable(&texture_format) {
+ match texture_format.sample_type(None) {
+ Some(wgpu::TextureSampleType::Float { .. }) => {
+ if texture_info::is_float_filterable(texture_format, ctx.device.features()) {
texture_float_filterable = texture.handle;
} else {
texture_float_nofilter = texture.handle;
}
}
- wgpu::TextureSampleType::Depth => {
- return Err(RectangleError::DepthTexturesNotSupported);
- }
- wgpu::TextureSampleType::Sint => {
+ Some(wgpu::TextureSampleType::Sint) => {
texture_sint = texture.handle;
}
- wgpu::TextureSampleType::Uint => {
+ Some(wgpu::TextureSampleType::Uint) => {
texture_uint = texture.handle;
}
+ _ => {
+ return Err(RectangleError::DepthTexturesNotSupported);
+ }
}
// We also set up an optional colormap texture.
@@ -689,11 +693,3 @@ impl Renderer for RectangleRenderer {
]
}
}
-
-fn is_float_filterable(format: &wgpu::TextureFormat) -> bool {
- format
- .describe()
- .guaranteed_format_features
- .flags
- .contains(wgpu::TextureFormatFeatureFlags::FILTERABLE)
-}
diff --git a/crates/re_renderer/src/resource_managers/mod.rs b/crates/re_renderer/src/resource_managers/mod.rs
index 7e1eff185a79..d9455d266071 100644
--- a/crates/re_renderer/src/resource_managers/mod.rs
+++ b/crates/re_renderer/src/resource_managers/mod.rs
@@ -10,7 +10,10 @@ mod mesh_manager;
pub use mesh_manager::{GpuMeshHandle, MeshManager};
mod texture_manager;
-pub use texture_manager::{GpuTexture2D, Texture2DCreationDesc, TextureManager2D};
+pub use texture_manager::{
+ GpuTexture2D, Texture2DCreationDesc, TextureCreationError, TextureManager2D,
+ TextureManager2DError,
+};
mod resource_manager;
pub use resource_manager::{ResourceHandle, ResourceLifeTime, ResourceManagerError};
diff --git a/crates/re_renderer/src/resource_managers/texture_manager.rs b/crates/re_renderer/src/resource_managers/texture_manager.rs
index 980918cb8a48..80da2c5768fe 100644
--- a/crates/re_renderer/src/resource_managers/texture_manager.rs
+++ b/crates/re_renderer/src/resource_managers/texture_manager.rs
@@ -1,4 +1,4 @@
-use std::{num::NonZeroU32, sync::Arc};
+use std::sync::Arc;
use ahash::{HashMap, HashSet};
@@ -75,7 +75,9 @@ pub struct Texture2DCreationDesc<'a> {
pub label: DebugLabel,
/// Data for the highest mipmap level.
- /// Must be padded according to wgpu rules and ready for upload.
+ ///
+ /// Data is expected to be tightly packed.
+ /// I.e. it is *not* padded according to wgpu buffer->texture transfer rules, padding will happen on the fly if necessary.
/// TODO(andreas): This should be a kind of factory function/builder instead which gets target memory passed in.
pub data: std::borrow::Cow<'a, [u8]>,
pub format: wgpu::TextureFormat,
@@ -93,6 +95,41 @@ impl<'a> Texture2DCreationDesc<'a> {
}
}
+// TODO(andreas): Move this to texture pool.
+#[derive(thiserror::Error, Debug)]
+pub enum TextureCreationError {
+ #[error("Texture with debug label {0:?} has zero width or height!")]
+ ZeroSize(DebugLabel),
+
+ #[error(
+ "Texture with debug label {label:?} has a format {format:?} that data can't be transferred to!"
+ )]
+ UnsupportedFormatForTransfer {
+ label: DebugLabel,
+ format: wgpu::TextureFormat,
+ },
+}
+
+#[derive(thiserror::Error, Debug)]
+pub enum TextureManager2DError {
+ /// Something went wrong when creating the GPU texture.
+ #[error(transparent)]
+ TextureCreation(#[from] TextureCreationError),
+
+ /// Something went wrong in a user-callback.
+ #[error(transparent)]
+ DataCreation(DataCreationError),
+}
+
+impl From> for TextureCreationError {
+ fn from(err: TextureManager2DError) -> Self {
+ match err {
+ TextureManager2DError::TextureCreation(texture_creation) => texture_creation,
+ TextureManager2DError::DataCreation(never) => match never {},
+ }
+ }
+}
+
/// Texture manager for 2D textures.
///
/// The scope is intentionally limited to particular kinds of textures that currently
@@ -144,7 +181,8 @@ impl TextureManager2D {
width: 1,
height: 1,
},
- );
+ )
+ .expect("Failed to create white pixel texture!");
let zeroed_texture_float =
create_zero_texture(texture_pool, &device, wgpu::TextureFormat::Rgba8Unorm);
@@ -174,7 +212,7 @@ impl TextureManager2D {
&mut self,
texture_pool: &mut GpuTexturePool,
creation_desc: &Texture2DCreationDesc<'_>,
- ) -> GpuTexture2D {
+ ) -> Result {
// TODO(andreas): Disabled the warning as we're moving towards using this texture manager for user-logged images.
// However, it's still very much a concern especially once we add mipmapping. Something we need to keep in mind.
//
@@ -201,39 +239,47 @@ impl TextureManager2D {
key: u64,
texture_pool: &mut GpuTexturePool,
texture_desc: Texture2DCreationDesc<'_>,
- ) -> GpuTexture2D {
- enum Never {}
- match self.get_or_create_with(key, texture_pool, || -> Result<_, Never> {
- Ok(texture_desc)
- }) {
- Ok(tex_handle) => tex_handle,
- Err(never) => match never {},
- }
+ ) -> Result {
+ self.get_or_create_with(key, texture_pool, || texture_desc)
}
/// Creates a new 2D texture resource and schedules data upload to the GPU if a texture
/// wasn't already created using the same key.
- pub fn get_or_create_with<'a, Err>(
+ pub fn get_or_create_with<'a>(
+ &mut self,
+ key: u64,
+ texture_pool: &mut GpuTexturePool,
+ create_texture_desc: impl FnOnce() -> Texture2DCreationDesc<'a>,
+ ) -> Result {
+ self.get_or_try_create_with(key, texture_pool, || -> Result<_, never::Never> {
+ Ok(create_texture_desc())
+ })
+ .map_err(|err| err.into())
+ }
+
+ /// Creates a new 2D texture resource and schedules data upload to the GPU if a texture
+ /// wasn't already created using the same key.
+ pub fn get_or_try_create_with<'a, Err: std::fmt::Display>(
&mut self,
key: u64,
texture_pool: &mut GpuTexturePool,
try_create_texture_desc: impl FnOnce() -> Result, Err>,
- ) -> Result {
+ ) -> Result> {
let texture_handle = match self.texture_cache.entry(key) {
std::collections::hash_map::Entry::Occupied(texture_handle) => {
texture_handle.get().clone() // already inserted
}
std::collections::hash_map::Entry::Vacant(entry) => {
// Run potentially expensive texture creation code:
- let tex_creation_desc = try_create_texture_desc()?;
- entry
- .insert(Self::create_and_upload_texture(
- &self.device,
- &self.queue,
- texture_pool,
- &tex_creation_desc,
- ))
- .clone()
+ let tex_creation_desc = try_create_texture_desc()
+ .map_err(|err| TextureManager2DError::DataCreation(err))?;
+ let texture = Self::create_and_upload_texture(
+ &self.device,
+ &self.queue,
+ texture_pool,
+ &tex_creation_desc,
+ )?;
+ entry.insert(texture).clone()
}
};
@@ -276,8 +322,13 @@ impl TextureManager2D {
queue: &wgpu::Queue,
texture_pool: &mut GpuTexturePool,
creation_desc: &Texture2DCreationDesc<'_>,
- ) -> GpuTexture2D {
+ ) -> Result {
crate::profile_function!();
+
+ if creation_desc.width == 0 || creation_desc.height == 0 {
+ return Err(TextureCreationError::ZeroSize(creation_desc.label.clone()));
+ }
+
let size = wgpu::Extent3d {
width: creation_desc.width,
height: creation_desc.height,
@@ -296,9 +347,15 @@ impl TextureManager2D {
},
);
- let format_info = creation_desc.format.describe();
- let width_blocks = creation_desc.width / format_info.block_dimensions.0 as u32;
- let bytes_per_row_unaligned = width_blocks * format_info.block_size as u32;
+ let width_blocks = creation_desc.width / creation_desc.format.block_dimensions().0;
+ let block_size = creation_desc
+ .format
+ .block_size(Some(wgpu::TextureAspect::All))
+ .ok_or_else(|| TextureCreationError::UnsupportedFormatForTransfer {
+ label: creation_desc.label.clone(),
+ format: creation_desc.format,
+ })?;
+ let bytes_per_row_unaligned = width_blocks * block_size;
// TODO(andreas): Once we have our own temp buffer for uploading, we can do the padding inplace
// I.e. the only difference will be if we do one memcopy or one memcopy per row, making row padding a nuisance!
@@ -317,9 +374,7 @@ impl TextureManager2D {
data,
wgpu::ImageDataLayout {
offset: 0,
- bytes_per_row: Some(
- NonZeroU32::new(bytes_per_row_unaligned).expect("invalid bytes per row"),
- ),
+ bytes_per_row: Some(bytes_per_row_unaligned),
rows_per_image: None,
},
size,
@@ -327,7 +382,7 @@ impl TextureManager2D {
// TODO(andreas): mipmap generation
- GpuTexture2D(texture)
+ Ok(GpuTexture2D(texture))
}
pub(crate) fn begin_frame(&mut self, _frame_index: u64) {
diff --git a/crates/re_renderer/src/texture_info.rs b/crates/re_renderer/src/texture_info.rs
new file mode 100644
index 000000000000..e18d447e62f8
--- /dev/null
+++ b/crates/re_renderer/src/texture_info.rs
@@ -0,0 +1,206 @@
+use std::borrow::Cow;
+
+/// Utility for dealing with buffers containing raw 2D texture data.
+#[derive(Clone)]
+pub struct Texture2DBufferInfo {
+ /// How many bytes per row contain actual data.
+ pub bytes_per_row_unpadded: u32,
+
+ /// How many bytes per row are required to be allocated in total.
+ ///
+ /// Padding bytes are always at the end of a row.
+ pub bytes_per_row_padded: u32,
+
+ /// Size required for an unpadded buffer.
+ pub buffer_size_unpadded: wgpu::BufferAddress,
+
+ /// Size required for a padded buffer as it is read/written from/to the GPU.
+ pub buffer_size_padded: wgpu::BufferAddress,
+}
+
+impl Texture2DBufferInfo {
+ /// Retrieves 2D texture buffer info for a given format & texture size.
+ ///
+ /// If a single buffer is not possible for all aspects of the texture format, all sizes will be zero.
+ #[inline]
+ pub fn new(format: wgpu::TextureFormat, extent: glam::UVec2) -> Self {
+ let block_dimensions = format.block_dimensions();
+ let width_blocks = extent.x / block_dimensions.0;
+ let height_blocks = extent.y / block_dimensions.1;
+
+ let block_size = format
+ .block_size(Some(wgpu::TextureAspect::All))
+ .unwrap_or(0); // This happens if we can't have a single buffer.
+ let bytes_per_row_unpadded = width_blocks * block_size;
+ let bytes_per_row_padded =
+ wgpu::util::align_to(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
+
+ Self {
+ bytes_per_row_unpadded,
+ bytes_per_row_padded,
+ buffer_size_unpadded: (bytes_per_row_unpadded * height_blocks) as wgpu::BufferAddress,
+ buffer_size_padded: (bytes_per_row_padded * height_blocks) as wgpu::BufferAddress,
+ }
+ }
+
+ #[inline]
+ pub fn num_rows(&self) -> u32 {
+ self.buffer_size_padded as u32 / self.bytes_per_row_padded
+ }
+
+ /// Removes the padding from a buffer containing gpu texture data.
+ ///
+ /// The passed in buffer is to be expected to be exactly of size [`Texture2DBufferInfo::buffer_size_padded`].
+ ///
+ /// Note that if you're passing in gpu data, there no alignment guarantees on the returned slice,
+ /// do NOT convert it using [`bytemuck`]. Use [`Texture2DBufferInfo::remove_padding_and_convert`] instead.
+ pub fn remove_padding<'a>(&self, buffer: &'a [u8]) -> Cow<'a, [u8]> {
+ crate::profile_function!();
+
+ assert_eq!(buffer.len() as wgpu::BufferAddress, self.buffer_size_padded);
+
+ if self.bytes_per_row_padded == self.bytes_per_row_unpadded {
+ return Cow::Borrowed(buffer);
+ }
+
+ let mut unpadded_buffer = Vec::with_capacity(self.buffer_size_unpadded as _);
+
+ for row in 0..self.num_rows() {
+ let offset = (self.bytes_per_row_padded * row) as usize;
+ unpadded_buffer.extend_from_slice(
+ &buffer[offset..(offset + self.bytes_per_row_unpadded as usize)],
+ );
+ }
+
+ unpadded_buffer.into()
+ }
+
+ /// Removes the padding from a buffer containing gpu texture data and remove convert to a given type.
+ ///
+ /// The passed in buffer is to be expected to be exactly of size [`Texture2DBufferInfo::buffer_size_padded`].
+ ///
+ /// The unpadded row size is expected to be a multiple of the size of the target type.
+ /// (Which means that, while uncommon, it technically doesn't need to be as big as a block in the pixel - this can be useful for e.g. packing wide bitfields)
+ pub fn remove_padding_and_convert(&self, buffer: &[u8]) -> Vec {
+ crate::profile_function!();
+
+ assert_eq!(buffer.len() as wgpu::BufferAddress, self.buffer_size_padded);
+ assert!(self.bytes_per_row_unpadded % std::mem::size_of::() as u32 == 0);
+
+ // Due to https://github.com/gfx-rs/wgpu/issues/3508 the data might be completely unaligned,
+ // so much, that we can't even interpret it as e.g. a u32 slice.
+ // Therefore, we have to do a copy of the data regardless of whether it's padded or not.
+
+ let mut unpadded_buffer: Vec = vec![
+ T::zeroed();
+ (self.num_rows() * self.bytes_per_row_unpadded / std::mem::size_of::() as u32)
+ as usize
+ ]; // TODO(andreas): Consider using unsafe set_len() instead of vec![] to avoid zeroing the memory.
+
+ // The copy has to happen on a u8 slice, because any other type would assume some alignment that we can't guarantee because of the above.
+ let unpadded_buffer_u8_view = bytemuck::cast_slice_mut(&mut unpadded_buffer);
+
+ for row in 0..self.num_rows() {
+ let offset_padded = (self.bytes_per_row_padded * row) as usize;
+ let offset_unpadded = (self.bytes_per_row_unpadded * row) as usize;
+ unpadded_buffer_u8_view
+ [offset_unpadded..(offset_unpadded + self.bytes_per_row_unpadded as usize)]
+ .copy_from_slice(
+ &buffer[offset_padded..(offset_padded + self.bytes_per_row_unpadded as usize)],
+ );
+ }
+
+ unpadded_buffer
+ }
+}
+
+pub fn is_float_filterable(format: wgpu::TextureFormat, device_features: wgpu::Features) -> bool {
+ format
+ .guaranteed_format_features(device_features)
+ .flags
+ .contains(wgpu::TextureFormatFeatureFlags::FILTERABLE)
+}
+
+pub fn num_texture_components(format: wgpu::TextureFormat) -> u8 {
+ #[allow(clippy::match_same_arms)]
+ match format {
+ wgpu::TextureFormat::R8Unorm
+ | wgpu::TextureFormat::R8Snorm
+ | wgpu::TextureFormat::R8Uint
+ | wgpu::TextureFormat::R8Sint
+ | wgpu::TextureFormat::R16Uint
+ | wgpu::TextureFormat::R16Sint
+ | wgpu::TextureFormat::R16Unorm
+ | wgpu::TextureFormat::R16Snorm
+ | wgpu::TextureFormat::R16Float
+ | wgpu::TextureFormat::R32Uint
+ | wgpu::TextureFormat::R32Sint
+ | wgpu::TextureFormat::R32Float => 1,
+
+ wgpu::TextureFormat::Rg8Unorm
+ | wgpu::TextureFormat::Rg8Snorm
+ | wgpu::TextureFormat::Rg8Uint
+ | wgpu::TextureFormat::Rg8Sint
+ | wgpu::TextureFormat::Rg16Uint
+ | wgpu::TextureFormat::Rg16Sint
+ | wgpu::TextureFormat::Rg16Unorm
+ | wgpu::TextureFormat::Rg16Snorm
+ | wgpu::TextureFormat::Rg16Float
+ | wgpu::TextureFormat::Rg32Uint
+ | wgpu::TextureFormat::Rg32Sint
+ | wgpu::TextureFormat::Rg32Float => 2,
+
+ wgpu::TextureFormat::Rgba8Unorm
+ | wgpu::TextureFormat::Rgba8UnormSrgb
+ | wgpu::TextureFormat::Rgba8Snorm
+ | wgpu::TextureFormat::Rgba8Uint
+ | wgpu::TextureFormat::Rgba8Sint
+ | wgpu::TextureFormat::Bgra8Unorm
+ | wgpu::TextureFormat::Bgra8UnormSrgb
+ | wgpu::TextureFormat::Rgba16Uint
+ | wgpu::TextureFormat::Rgba16Sint
+ | wgpu::TextureFormat::Rgba16Unorm
+ | wgpu::TextureFormat::Rgba16Snorm
+ | wgpu::TextureFormat::Rgba16Float
+ | wgpu::TextureFormat::Rgba32Uint
+ | wgpu::TextureFormat::Rgba32Sint
+ | wgpu::TextureFormat::Rgba32Float => 4,
+
+ wgpu::TextureFormat::Rgb9e5Ufloat | wgpu::TextureFormat::Rg11b10Float => 3,
+ wgpu::TextureFormat::Rgb10a2Unorm => 4,
+
+ wgpu::TextureFormat::Stencil8
+ | wgpu::TextureFormat::Depth16Unorm
+ | wgpu::TextureFormat::Depth24Plus
+ | wgpu::TextureFormat::Depth32Float => 1,
+
+ // It's complicated. Each aspect has actually only a single channel.
+ wgpu::TextureFormat::Depth24PlusStencil8 | wgpu::TextureFormat::Depth32FloatStencil8 => 2,
+
+ wgpu::TextureFormat::Bc1RgbaUnorm
+ | wgpu::TextureFormat::Bc1RgbaUnormSrgb
+ | wgpu::TextureFormat::Bc2RgbaUnorm
+ | wgpu::TextureFormat::Bc2RgbaUnormSrgb
+ | wgpu::TextureFormat::Bc3RgbaUnorm
+ | wgpu::TextureFormat::Bc3RgbaUnormSrgb
+ | wgpu::TextureFormat::Bc4RUnorm
+ | wgpu::TextureFormat::Bc4RSnorm
+ | wgpu::TextureFormat::Bc5RgUnorm
+ | wgpu::TextureFormat::Bc5RgSnorm
+ | wgpu::TextureFormat::Bc6hRgbUfloat
+ | wgpu::TextureFormat::Bc6hRgbFloat
+ | wgpu::TextureFormat::Bc7RgbaUnorm
+ | wgpu::TextureFormat::Bc7RgbaUnormSrgb
+ | wgpu::TextureFormat::Etc2Rgb8Unorm
+ | wgpu::TextureFormat::Etc2Rgb8UnormSrgb
+ | wgpu::TextureFormat::Etc2Rgb8A1Unorm
+ | wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb
+ | wgpu::TextureFormat::Etc2Rgba8Unorm
+ | wgpu::TextureFormat::Etc2Rgba8UnormSrgb
+ | wgpu::TextureFormat::EacR11Unorm
+ | wgpu::TextureFormat::EacR11Snorm
+ | wgpu::TextureFormat::EacRg11Unorm
+ | wgpu::TextureFormat::EacRg11Snorm
+ | wgpu::TextureFormat::Astc { .. } => 4,
+ }
+}
diff --git a/crates/re_renderer/src/view_builder.rs b/crates/re_renderer/src/view_builder.rs
index 3d57b21a6f1c..86d8fc817b59 100644
--- a/crates/re_renderer/src/view_builder.rs
+++ b/crates/re_renderer/src/view_builder.rs
@@ -6,7 +6,8 @@ use crate::{
allocator::{create_and_fill_uniform_buffer, GpuReadbackIdentifier},
context::RenderContext,
draw_phases::{
- DrawPhase, OutlineConfig, OutlineMaskProcessor, PickingLayerProcessor, ScreenshotProcessor,
+ DrawPhase, OutlineConfig, OutlineMaskProcessor, PickingLayerError, PickingLayerProcessor,
+ ScreenshotProcessor,
},
global_bindings::FrameUniformBuffer,
renderer::{CompositorDrawData, DebugOverlayDrawData, DrawData, Renderer},
@@ -37,6 +38,9 @@ pub enum ViewBuilderError {
#[error("Picking rectangle readback was already scheduled.")]
PickingRectAlreadyScheduled,
+
+ #[error(transparent)]
+ InvalidDebugOverlay(#[from] crate::renderer::DebugOverlayError),
}
/// The highest level rendering block in `re_renderer`.
@@ -562,7 +566,15 @@ impl ViewBuilder {
//pass.set_bind_group(0, &setup.bind_group_0, &[]);
self.draw_phase(ctx, DrawPhase::PickingLayer, &mut pass);
}
- picking_processor.end_render_pass(&mut encoder, &ctx.gpu_resources)?;
+ match picking_processor.end_render_pass(&mut encoder, &ctx.gpu_resources) {
+ Err(PickingLayerError::ResourcePoolError(err)) => {
+ return Err(err);
+ }
+ Err(PickingLayerError::ReadbackError(err)) => {
+ re_log::warn_once!("Failed to schedule picking data readback: {err}");
+ }
+ Ok(()) => {}
+ }
}
if let Some(outline_mask_processor) = self.outline_mask_processor.take() {
@@ -582,7 +594,12 @@ impl ViewBuilder {
pass.set_bind_group(0, &setup.bind_group_0, &[]);
self.draw_phase(ctx, DrawPhase::CompositingScreenshot, &mut pass);
}
- screenshot_processor.end_render_pass(&mut encoder);
+ match screenshot_processor.end_render_pass(&mut encoder) {
+ Ok(()) => {}
+ Err(err) => {
+ re_log::warn_once!("Failed to schedule screenshot data readback: {err}");
+ }
+ }
}
Ok(encoder.finish())
@@ -692,7 +709,7 @@ impl ViewBuilder {
&picking_processor.picking_target,
self.setup.resolution_in_pixel.into(),
picking_rect,
- ));
+ )?);
}
self.picking_processor = Some(picking_processor);
diff --git a/crates/re_renderer/src/wgpu_resources/dynamic_resource_pool.rs b/crates/re_renderer/src/wgpu_resources/dynamic_resource_pool.rs
index 6c873429db4b..5c71fe51ac28 100644
--- a/crates/re_renderer/src/wgpu_resources/dynamic_resource_pool.rs
+++ b/crates/re_renderer/src/wgpu_resources/dynamic_resource_pool.rs
@@ -148,6 +148,13 @@ where
self.current_frame_index = frame_index;
let state = self.state.get_mut();
+ let update_stats = |creation_desc: &Desc| {
+ self.total_resource_size_in_bytes.fetch_sub(
+ creation_desc.resource_size_in_bytes(),
+ std::sync::atomic::Ordering::Relaxed,
+ );
+ };
+
// Throw out any resources that we haven't reclaimed last frame.
for (desc, resources) in state.last_frame_deallocated.drain() {
re_log::trace!(
@@ -160,11 +167,8 @@ where
debug_assert!(false, "a resource was marked as destroyed last frame that we no longer kept track of");
continue;
};
+ update_stats(&desc);
on_destroy_resource(&removed_resource);
- self.total_resource_size_in_bytes.fetch_sub(
- desc.resource_size_in_bytes(),
- std::sync::atomic::Ordering::Relaxed,
- );
}
}
@@ -184,6 +188,7 @@ where
.push(resource.handle);
true
} else {
+ update_stats(&resource.creation_desc);
on_destroy_resource(&resource.inner);
false
}
diff --git a/crates/re_renderer/src/wgpu_resources/mod.rs b/crates/re_renderer/src/wgpu_resources/mod.rs
index 06f30b6292d3..e5e3a389f731 100644
--- a/crates/re_renderer/src/wgpu_resources/mod.rs
+++ b/crates/re_renderer/src/wgpu_resources/mod.rs
@@ -7,7 +7,6 @@
//! higher level resources that arise from processing user provided data.
mod bind_group_layout_pool;
-use std::borrow::Cow;
pub use bind_group_layout_pool::{
BindGroupLayoutDesc, GpuBindGroupLayoutHandle, GpuBindGroupLayoutPool,
@@ -116,112 +115,3 @@ impl WgpuResourcePools {
}
}
}
-
-/// Utility for dealing with buffers containing raw 2D texture data.
-#[derive(Clone)]
-pub struct Texture2DBufferInfo {
- /// How many bytes per row contain actual data.
- pub bytes_per_row_unpadded: u32,
-
- /// How many bytes per row are required to be allocated in total.
- ///
- /// Padding bytes are always at the end of a row.
- pub bytes_per_row_padded: u32,
-
- /// Size required for an unpadded buffer.
- pub buffer_size_unpadded: wgpu::BufferAddress,
-
- /// Size required for a padded buffer as it is read/written from/to the GPU.
- pub buffer_size_padded: wgpu::BufferAddress,
-}
-
-impl Texture2DBufferInfo {
- #[inline]
- pub fn new(format: wgpu::TextureFormat, extent: glam::UVec2) -> Self {
- let format_info = format.describe();
-
- let width_blocks = extent.x / format_info.block_dimensions.0 as u32;
- let height_blocks = extent.y / format_info.block_dimensions.1 as u32;
-
- let bytes_per_row_unpadded = width_blocks * format_info.block_size as u32;
- let bytes_per_row_padded =
- wgpu::util::align_to(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
-
- Self {
- bytes_per_row_unpadded,
- bytes_per_row_padded,
- buffer_size_unpadded: (bytes_per_row_unpadded * height_blocks) as wgpu::BufferAddress,
- buffer_size_padded: (bytes_per_row_padded * height_blocks) as wgpu::BufferAddress,
- }
- }
-
- #[inline]
- pub fn num_rows(&self) -> u32 {
- self.buffer_size_padded as u32 / self.bytes_per_row_padded
- }
-
- /// Removes the padding from a buffer containing gpu texture data.
- ///
- /// The passed in buffer is to be expected to be exactly of size [`Texture2DBufferInfo::buffer_size_padded`].
- ///
- /// Note that if you're passing in gpu data, there no alignment guarantees on the returned slice,
- /// do NOT convert it using [`bytemuck`]. Use [`Texture2DBufferInfo::remove_padding_and_convert`] instead.
- pub fn remove_padding<'a>(&self, buffer: &'a [u8]) -> Cow<'a, [u8]> {
- crate::profile_function!();
-
- assert_eq!(buffer.len() as wgpu::BufferAddress, self.buffer_size_padded);
-
- if self.bytes_per_row_padded == self.bytes_per_row_unpadded {
- return Cow::Borrowed(buffer);
- }
-
- let mut unpadded_buffer = Vec::with_capacity(self.buffer_size_unpadded as _);
-
- for row in 0..self.num_rows() {
- let offset = (self.bytes_per_row_padded * row) as usize;
- unpadded_buffer.extend_from_slice(
- &buffer[offset..(offset + self.bytes_per_row_unpadded as usize)],
- );
- }
-
- unpadded_buffer.into()
- }
-
- /// Removes the padding from a buffer containing gpu texture data and remove convert to a given type.
- ///
- /// The passed in buffer is to be expected to be exactly of size [`Texture2DBufferInfo::buffer_size_padded`].
- ///
- /// The unpadded row size is expected to be a multiple of the size of the target type.
- /// (Which means that, while uncommon, it technically doesn't need to be as big as a block in the pixel - this can be useful for e.g. packing wide bitfields)
- pub fn remove_padding_and_convert(&self, buffer: &[u8]) -> Vec {
- crate::profile_function!();
-
- assert_eq!(buffer.len() as wgpu::BufferAddress, self.buffer_size_padded);
- assert!(self.bytes_per_row_unpadded % std::mem::size_of::() as u32 == 0);
-
- // Due to https://github.com/gfx-rs/wgpu/issues/3508 the data might be completely unaligned,
- // so much, that we can't even interpret it as e.g. a u32 slice.
- // Therefore, we have to do a copy of the data regardless of whether it's padded or not.
-
- let mut unpadded_buffer: Vec = vec![
- T::zeroed();
- (self.num_rows() * self.bytes_per_row_unpadded / std::mem::size_of::() as u32)
- as usize
- ]; // TODO(andreas): Consider using unsafe set_len() instead of vec![] to avoid zeroing the memory.
-
- // The copy has to happen on a u8 slice, because any other type would assume some alignment that we can't guarantee because of the above.
- let unpadded_buffer_u8_view = bytemuck::cast_slice_mut(&mut unpadded_buffer);
-
- for row in 0..self.num_rows() {
- let offset_padded = (self.bytes_per_row_padded * row) as usize;
- let offset_unpadded = (self.bytes_per_row_unpadded * row) as usize;
- unpadded_buffer_u8_view
- [offset_unpadded..(offset_unpadded + self.bytes_per_row_unpadded as usize)]
- .copy_from_slice(
- &buffer[offset_padded..(offset_padded + self.bytes_per_row_unpadded as usize)],
- );
- }
-
- unpadded_buffer
- }
-}
diff --git a/crates/re_renderer/src/wgpu_resources/sampler_pool.rs b/crates/re_renderer/src/wgpu_resources/sampler_pool.rs
index 53eed49cdf73..2c03824cf1c9 100644
--- a/crates/re_renderer/src/wgpu_resources/sampler_pool.rs
+++ b/crates/re_renderer/src/wgpu_resources/sampler_pool.rs
@@ -1,4 +1,4 @@
-use std::{hash::Hash, num::NonZeroU8};
+use std::hash::Hash;
use super::{resource::PoolError, static_resource_pool::StaticResourcePool};
use crate::debug_label::DebugLabel;
@@ -33,9 +33,6 @@ pub struct SamplerDesc {
/// Maximum level of detail (i.e. mip level) to use
pub lod_max_clamp: ordered_float::NotNan,
-
- /// Valid values: 1, 2, 4, 8, and 16.
- pub anisotropy_clamp: Option,
}
#[derive(Default)]
@@ -56,11 +53,11 @@ impl GpuSamplerPool {
mipmap_filter: desc.mipmap_filter,
lod_min_clamp: desc.lod_min_clamp.into(),
lod_max_clamp: desc.lod_max_clamp.into(),
- anisotropy_clamp: desc.anisotropy_clamp,
// Unsupported
compare: None,
border_color: None,
+ anisotropy_clamp: 1,
})
})
}
diff --git a/crates/re_renderer/src/wgpu_resources/shader_module_pool.rs b/crates/re_renderer/src/wgpu_resources/shader_module_pool.rs
index 1fb291f47f05..9b42f87d1788 100644
--- a/crates/re_renderer/src/wgpu_resources/shader_module_pool.rs
+++ b/crates/re_renderer/src/wgpu_resources/shader_module_pool.rs
@@ -54,13 +54,18 @@ impl ShaderModuleDesc {
&self,
device: &wgpu::Device,
resolver: &mut FileResolver,
+ shader_text_workaround_replacements: &[(String, String)],
) -> wgpu::ShaderModule {
- let source_interpolated = resolver
+ let mut source_interpolated = resolver
.populate(&self.source)
.context("couldn't resolve shader module's contents")
.map_err(|err| re_log::error!(err=%re_error::format(err)))
.unwrap_or_default();
+ for (from, to) in shader_text_workaround_replacements {
+ source_interpolated.contents = source_interpolated.contents.replace(from, to);
+ }
+
// All wgpu errors come asynchronously: this call will succeed whether the given
// source is valid or not.
// Only when actually submitting passes that make use of this shader will we know if
@@ -78,6 +83,11 @@ impl ShaderModuleDesc {
#[derive(Default)]
pub struct GpuShaderModulePool {
pool: StaticResourcePool,
+
+ /// Workarounds via text replacement in shader source code.
+ ///
+ /// TODO(andreas): These should be solved with a pre-processor.
+ pub shader_text_workaround_replacements: Vec<(String, String)>,
}
impl GpuShaderModulePool {
@@ -87,8 +97,9 @@ impl GpuShaderModulePool {
resolver: &mut FileResolver,
desc: &ShaderModuleDesc,
) -> GpuShaderModuleHandle {
- self.pool
- .get_or_create(desc, |desc| desc.create_shader_module(device, resolver))
+ self.pool.get_or_create(desc, |desc| {
+ desc.create_shader_module(device, resolver, &self.shader_text_workaround_replacements)
+ })
}
pub fn begin_frame(
@@ -115,7 +126,11 @@ impl GpuShaderModulePool {
}
paths.iter().any(|p| updated_paths.contains(p)).then(|| {
- let shader_module = desc.create_shader_module(device, resolver);
+ let shader_module = desc.create_shader_module(
+ device,
+ resolver,
+ &self.shader_text_workaround_replacements,
+ );
re_log::debug!(?desc.source, label = desc.label.get(), "recompiled shader module");
shader_module
})
diff --git a/crates/re_renderer/src/wgpu_resources/texture_pool.rs b/crates/re_renderer/src/wgpu_resources/texture_pool.rs
index c113b4f603d8..d194b387f773 100644
--- a/crates/re_renderer/src/wgpu_resources/texture_pool.rs
+++ b/crates/re_renderer/src/wgpu_resources/texture_pool.rs
@@ -69,9 +69,20 @@ impl DynamicResourcesDesc for TextureDesc {
/// The actual number might be both bigger (padding) and lower (gpu sided compression).
fn resource_size_in_bytes(&self) -> u64 {
let mut size_in_bytes = 0;
- let format_desc = self.format.describe();
- let pixels_per_block =
- format_desc.block_dimensions.0 as u64 * format_desc.block_dimensions.1 as u64;
+ let block_size = self
+ .format
+ .block_size(Some(wgpu::TextureAspect::All))
+ .unwrap_or_else(|| {
+ self.format
+ .block_size(Some(wgpu::TextureAspect::DepthOnly))
+ .unwrap_or(0)
+ + self
+ .format
+ .block_size(Some(wgpu::TextureAspect::StencilOnly))
+ .unwrap_or(0)
+ });
+ let block_dimension = self.format.block_dimensions();
+ let pixels_per_block = block_dimension.0 as u64 * block_dimension.1 as u64;
for mip in 0..self.size.max_mips(self.dimension) {
let mip_size = self
@@ -80,7 +91,7 @@ impl DynamicResourcesDesc for TextureDesc {
.physical_size(self.format);
let num_pixels = mip_size.width * mip_size.height * mip_size.depth_or_array_layers;
let num_blocks = num_pixels as u64 / pixels_per_block;
- size_in_bytes += num_blocks * format_desc.block_size as u64;
+ size_in_bytes += num_blocks * block_size as u64;
}
size_in_bytes
diff --git a/crates/re_ui/Cargo.toml b/crates/re_ui/Cargo.toml
index cfa5732751ca..b9a59e8f9c0e 100644
--- a/crates/re_ui/Cargo.toml
+++ b/crates/re_ui/Cargo.toml
@@ -30,8 +30,8 @@ egui_dock = ["dep:egui_dock"]
[dependencies]
-egui = { workspace = true, features = ["extra_debug_asserts", "tracing"] }
-egui_extras = { workspace = true, features = ["tracing"] }
+egui.workspace = true
+egui_extras.workspace = true
image = { workspace = true, default-features = false, features = ["png"] }
parking_lot.workspace = true
serde = { version = "1", features = ["derive"] }
@@ -39,11 +39,10 @@ serde_json = "1"
strum = { version = "0.24", features = ["derive"] }
strum_macros = "0.24"
sublime_fuzzy = "0.7"
-
## Optional dependencies:
eframe = { workspace = true, optional = true, default-features = false }
egui_dock = { workspace = true, optional = true, features = ["serde"] }
+re_log.workspace = true
[dev-dependencies]
eframe = { workspace = true, default-features = false, features = ["wgpu"] }
-re_log.workspace = true
diff --git a/crates/re_ui/data/design_tokens.json b/crates/re_ui/data/design_tokens.json
index 9c8974d9a4c5..f8da200e1354 100644
--- a/crates/re_ui/data/design_tokens.json
+++ b/crates/re_ui/data/design_tokens.json
@@ -3,121 +3,126 @@
"Color": {
"Surface": {
"Default": {
- "description": "Background color for most UI surfaces in Rerun",
- "value": "{Global.Color.Grey.100}",
- "type": "color"
+ "value": "{Global.Color.White}",
+ "type": "color",
+ "description": "Background color for most UI surfaces in Rerun"
},
"Floating": {
- "description": "Background color for floating elements like menus, dropdown options, notifications etc.",
- "value": "{Global.Color.Grey.175}",
- "type": "color"
+ "value": "{Global.Color.Primary.25}",
+ "type": "color",
+ "description": "Background color for floating elements like menus, dropdown options, notifications etc."
}
},
"Action": {
"Default": {
- "description": "Background color for UI elements like buttons and selects",
- "value": "{Global.Color.Grey.200}",
- "type": "color"
+ "value": "{Global.Color.White}",
+ "type": "color",
+ "description": "Background color for UI elements like buttons and selects"
},
"Hovered": {
- "description": "Background color for hovered UI elements",
- "value": "{Global.Color.Grey.225}",
- "type": "color"
+ "value": "{Global.Color.Primary.Hover}",
+ "type": "color",
+ "description": "Background color for hovered UI elements"
},
"Active": {
- "description": "Background color for pressed UI elements",
- "value": "{Global.Color.Grey.250}",
- "type": "color"
+ "value": "{Global.Color.Primary.Hover}",
+ "type": "color",
+ "description": "Background color for pressed UI elements"
},
"Pressed": {
- "description": "Background color for suppressed UI elements, like a select that is currently showing a menu",
- "value": "{Global.Color.Grey.250}",
- "type": "color"
+ "value": "{Global.Color.Primary.Hover}",
+ "type": "color",
+ "description": "Background color for suppressed UI elements, like a select that is currently showing a menu"
+ },
+ "Inactive": {
+ "value": "{Global.Color.White}",
+ "type": "color",
+ "description": "Background color for inactive buttons and such"
}
},
"NotificationBadge": {
"Unread": {
- "description": "Used for unread notification indicators",
"value": "{Global.Color.Blue.500}",
- "type": "color"
+ "type": "color",
+ "description": "Used for unread notification indicators"
},
"Read": {
- "description": "Used for read notification indicators",
"value": "{Global.Color.Grey.250}",
- "type": "color"
+ "type": "color",
+ "description": "Used for read notification indicators"
}
},
"Text": {
"Default": {
- "description": "Default text color",
- "value": "{Global.Color.Grey.775}",
- "type": "color"
+ "value": "{Global.Color.Text.Primary}",
+ "type": "color",
+ "description": "Default text color"
},
"Subdued": {
- "description": "Used for less important text",
- "value": "{Global.Color.Grey.550}",
- "type": "color"
+ "value": "{Global.Color.Text.Secondary}",
+ "type": "color",
+ "description": "Used for less important text"
},
"Strong": {
- "description": "Used for highlighted or emphasized items, such as current navigation items",
- "value": "{Global.Color.Grey.1000}",
- "type": "color"
+ "value": "{Global.Color.Black}",
+ "type": "color",
+ "description": "Used for highlighted or emphasized items, such as current navigation items"
}
},
"Border": {
"Default": {
- "value": "{Global.Color.OpaqueGrey.Default}",
- "description": "Default color for borders",
- "type": "color"
+ "value": "{Global.Color.Gray.200}",
+ "type": "color",
+ "description": "Default color for borders"
}
},
"Icon": {
"Default": {
- "description": "Default icon color",
"value": "{Global.Color.Grey.775}",
- "type": "color"
+ "type": "color",
+ "description": "Default icon color"
},
"Subdued": {
- "description": "Used together with subdued text",
"value": "{Global.Color.Grey.550}",
- "type": "color"
+ "type": "color",
+ "description": "Used together with subdued text"
},
"Strong": {
- "description": "Used together width strong text",
"value": "{Global.Color.Grey.1000}",
- "type": "color"
+ "type": "color",
+ "description": "Used together width strong text"
}
},
"Highlight": {
"Default": {
- "value": "{Global.Color.Blue.350}",
- "description": "Default color for highlighted items, like hovered menu items",
- "type": "color"
+ "value": "{Global.Color.Primary.Hover}",
+ "type": "color",
+ "description": "Default color for highlighted items, like hovered menu items"
}
}
},
"Typography": {
"Default": {
"value": "{Global.Typography.200}",
- "description": "Default font in Rerun's UI",
- "type": "typography"
+ "type": "typography",
+ "description": "Default font in Rerun's UI"
}
},
"Shadow": {
"Menu": {
- "description": "Used for menus, such as selects",
"value": "{Global.Shadow.100}",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": "Used for menus, such as selects"
},
"Popover": {
- "description": "Used for popovers and other semi-modal elements",
"value": "{Global.Shadow.400}",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": "Used for popovers and other semi-modal elements"
},
"Modal": {
- "description": "Used for modal views",
"value": "{Global.Shadow.800}",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": "Used for modal views"
}
}
},
@@ -126,1042 +131,1280 @@
"Grey": {
"0": {
"value": "#000000",
- "description": "0 - 0",
- "type": "color"
+ "type": "color",
+ "description": "0 - 0"
},
"25": {
"value": "#020303",
- "description": "0.7405999850077014 - 0.025",
- "type": "color"
+ "type": "color",
+ "description": "0.7405999850077014 - 0.025"
},
"50": {
"value": "#050607",
- "description": "1.7116872276823336 - 0.05",
- "type": "color"
+ "type": "color",
+ "description": "1.7116872276823336 - 0.05"
},
"75": {
"value": "#090b0c",
- "description": "2.911127985087129 - 0.075",
- "type": "color"
+ "type": "color",
+ "description": "2.911127985087129 - 0.075"
},
"100": {
"value": "#0d1011",
- "description": "4.335380638931743 - 0.1",
- "type": "color"
+ "type": "color",
+ "description": "4.335380638931743 - 0.1"
},
"125": {
"value": "#111415",
- "description": "5.979517530826747 - 0.125",
- "type": "color"
+ "type": "color",
+ "description": "5.979517530826747 - 0.125"
},
"150": {
"value": "#141819",
- "description": "7.837255342936205 - 0.15",
- "type": "color"
+ "type": "color",
+ "description": "7.837255342936205 - 0.15"
},
"175": {
"value": "#181c1e",
- "description": "9.90099383672154 - 0.175",
- "type": "color"
+ "type": "color",
+ "description": "9.90099383672154 - 0.175"
},
"200": {
"value": "#1c2123",
- "description": "12.16186271093947 - 0.2",
- "type": "color"
+ "type": "color",
+ "description": "12.16186271093947 - 0.2"
},
"225": {
"value": "#212628",
- "description": "14.609776289998841 - 0.225",
- "type": "color"
+ "type": "color",
+ "description": "14.609776289998841 - 0.225"
},
"250": {
"value": "#262b2e",
- "description": "17.233495705504463 - 0.25",
- "type": "color"
+ "type": "color",
+ "description": "17.233495705504463 - 0.25"
},
"275": {
"value": "#2b3134",
- "description": "20.02069818761812 - 0.275",
- "type": "color"
+ "type": "color",
+ "description": "20.02069818761812 - 0.275"
},
"300": {
"value": "#31383b",
- "description": "22.958053039032254 - 0.3",
- "type": "color"
+ "type": "color",
+ "description": "22.958053039032254 - 0.3"
},
"325": {
"value": "#373f42",
- "description": "26.03130382315192 - 0.325",
- "type": "color"
+ "type": "color",
+ "description": "26.03130382315192 - 0.325"
},
"350": {
"value": "#3e464a",
- "description": "29.22535625976699 - 0.35",
- "type": "color"
+ "type": "color",
+ "description": "29.22535625976699 - 0.35"
},
"375": {
"value": "#454e52",
- "description": "32.524371286309126 - 0.375",
- "type": "color"
+ "type": "color",
+ "description": "32.524371286309126 - 0.375"
},
"400": {
"value": "#4c565a",
- "description": "35.91186271093947 - 0.4",
- "type": "color"
+ "type": "color",
+ "description": "35.91186271093947 - 0.4"
},
"425": {
"value": "#545e63",
- "description": "39.37079885540354 - 0.425",
- "type": "color"
+ "type": "color",
+ "description": "39.37079885540354 - 0.425"
},
"450": {
"value": "#5c676c",
- "description": "42.88370756099135 - 0.45",
- "type": "color"
+ "type": "color",
+ "description": "42.88370756099135 - 0.45"
},
"475": {
"value": "#647075",
- "description": "46.43278391020581 - 0.475",
- "type": "color"
+ "type": "color",
+ "description": "46.43278391020581 - 0.475"
},
"500": {
"value": "#6c797f",
- "description": "49.99999999999999 - 0.5",
- "type": "color"
+ "type": "color",
+ "description": "49.99999999999999 - 0.5"
},
"525": {
"value": "#748288",
- "description": "53.56721608979418 - 0.525",
- "type": "color"
+ "type": "color",
+ "description": "53.56721608979418 - 0.525"
},
"550": {
"value": "#7d8c92",
- "description": "57.11629243900867 - 0.55",
- "type": "color"
+ "type": "color",
+ "description": "57.11629243900867 - 0.55"
},
"575": {
"value": "#85959c",
- "description": "60.62920114459644 - 0.575",
- "type": "color"
+ "type": "color",
+ "description": "60.62920114459644 - 0.575"
},
"600": {
"value": "#8e9ea5",
- "description": "64.08813728906053 - 0.6",
- "type": "color"
+ "type": "color",
+ "description": "64.08813728906053 - 0.6"
},
"625": {
"value": "#96a7af",
- "description": "67.47562871369087 - 0.625",
- "type": "color"
+ "type": "color",
+ "description": "67.47562871369087 - 0.625"
},
"650": {
"value": "#9eb0b8",
- "description": "70.774643740233 - 0.65",
- "type": "color"
+ "type": "color",
+ "description": "70.774643740233 - 0.65"
},
"675": {
"value": "#a6b9c1",
- "description": "73.96869617684807 - 0.675",
- "type": "color"
+ "type": "color",
+ "description": "73.96869617684807 - 0.675"
},
"700": {
"value": "#aec2ca",
- "description": "77.04194696096773 - 0.7",
- "type": "color"
+ "type": "color",
+ "description": "77.04194696096773 - 0.7"
},
"725": {
"value": "#b6cad2",
- "description": "79.97930181238189 - 0.725",
- "type": "color"
+ "type": "color",
+ "description": "79.97930181238189 - 0.725"
},
"750": {
"value": "#c0d1d8",
- "description": "82.76650429449552 - 0.75",
- "type": "color"
+ "type": "color",
+ "description": "82.76650429449552 - 0.75"
},
"775": {
"value": "#cad8de",
- "description": "85.39022371000115 - 0.775",
- "type": "color"
+ "type": "color",
+ "description": "85.39022371000115 - 0.775"
},
"800": {
"value": "#d3dee3",
- "description": "87.83813728906054 - 0.8",
- "type": "color"
+ "type": "color",
+ "description": "87.83813728906054 - 0.8"
},
"825": {
"value": "#dbe4e8",
- "description": "90.09900616327847 - 0.825",
- "type": "color"
+ "type": "color",
+ "description": "90.09900616327847 - 0.825"
},
"850": {
"value": "#e3eaed",
- "description": "92.16274465706378 - 0.85",
- "type": "color"
+ "type": "color",
+ "description": "92.16274465706378 - 0.85"
},
"875": {
"value": "#e9eff1",
- "description": "94.02048246917325 - 0.875",
- "type": "color"
+ "type": "color",
+ "description": "94.02048246917325 - 0.875"
},
"900": {
"value": "#eff3f5",
- "description": "95.66461936106825 - 0.9",
- "type": "color"
+ "type": "color",
+ "description": "95.66461936106825 - 0.9"
},
"925": {
"value": "#f4f7f8",
- "description": "97.08887201491288 - 0.925",
- "type": "color"
+ "type": "color",
+ "description": "97.08887201491288 - 0.925"
},
"950": {
"value": "#f9fafb",
- "description": "98.28831277231767 - 0.95",
- "type": "color"
+ "type": "color",
+ "description": "98.28831277231767 - 0.95"
},
"975": {
"value": "#fcfdfd",
- "description": "99.25940001499231 - 0.975",
- "type": "color"
+ "type": "color",
+ "description": "99.25940001499231 - 0.975"
},
"1000": {
"value": "#ffffff",
- "description": "100 - 1",
- "type": "color"
+ "type": "color",
+ "description": "100 - 1"
}
},
"Green": {
"0": {
"value": "#000000",
- "description": "0 - 0",
- "type": "color"
+ "type": "color",
+ "description": "0 - 0"
},
"25": {
"value": "#000401",
- "description": "0.7405999850077014 - 0.025",
- "type": "color"
+ "type": "color",
+ "description": "0.7405999850077014 - 0.025"
},
"50": {
"value": "#000803",
- "description": "1.7116872276823336 - 0.05",
- "type": "color"
+ "type": "color",
+ "description": "1.7116872276823336 - 0.05"
},
"75": {
"value": "#000e04",
- "description": "2.911127985087129 - 0.075",
- "type": "color"
+ "type": "color",
+ "description": "2.911127985087129 - 0.075"
},
"100": {
"value": "#001306",
- "description": "4.335380638931743 - 0.1",
- "type": "color"
+ "type": "color",
+ "description": "4.335380638931743 - 0.1"
},
"125": {
"value": "#001809",
- "description": "5.979517530826747 - 0.125",
- "type": "color"
+ "type": "color",
+ "description": "5.979517530826747 - 0.125"
},
"150": {
"value": "#001c0b",
- "description": "7.837255342936205 - 0.15",
- "type": "color"
+ "type": "color",
+ "description": "7.837255342936205 - 0.15"
},
"175": {
"value": "#00210e",
- "description": "9.90099383672154 - 0.175",
- "type": "color"
+ "type": "color",
+ "description": "9.90099383672154 - 0.175"
},
"200": {
"value": "#002611",
- "description": "12.16186271093947 - 0.2",
- "type": "color"
+ "type": "color",
+ "description": "12.16186271093947 - 0.2"
},
"225": {
"value": "#002c15",
- "description": "14.609776289998841 - 0.225",
- "type": "color"
+ "type": "color",
+ "description": "14.609776289998841 - 0.225"
},
"250": {
"value": "#003219",
- "description": "17.233495705504463 - 0.25",
- "type": "color"
+ "type": "color",
+ "description": "17.233495705504463 - 0.25"
},
"275": {
"value": "#00391d",
- "description": "20.02069818761812 - 0.275",
- "type": "color"
+ "type": "color",
+ "description": "20.02069818761812 - 0.275"
},
"300": {
"value": "#004021",
- "description": "22.958053039032254 - 0.3",
- "type": "color"
+ "type": "color",
+ "description": "22.958053039032254 - 0.3"
},
"325": {
"value": "#004826",
- "description": "26.03130382315192 - 0.325",
- "type": "color"
+ "type": "color",
+ "description": "26.03130382315192 - 0.325"
},
"350": {
"value": "#00502b",
- "description": "29.22535625976699 - 0.35",
- "type": "color"
+ "type": "color",
+ "description": "29.22535625976699 - 0.35"
},
"375": {
"value": "#005930",
- "description": "32.524371286309126 - 0.375",
- "type": "color"
+ "type": "color",
+ "description": "32.524371286309126 - 0.375"
},
"400": {
"value": "#006236",
- "description": "35.91186271093947 - 0.4",
- "type": "color"
+ "type": "color",
+ "description": "35.91186271093947 - 0.4"
},
"425": {
"value": "#006b3b",
- "description": "39.37079885540354 - 0.425",
- "type": "color"
+ "type": "color",
+ "description": "39.37079885540354 - 0.425"
},
"450": {
"value": "#007541",
- "description": "42.88370756099135 - 0.45",
- "type": "color"
+ "type": "color",
+ "description": "42.88370756099135 - 0.45"
},
"475": {
"value": "#007f47",
- "description": "46.43278391020581 - 0.475",
- "type": "color"
+ "type": "color",
+ "description": "46.43278391020581 - 0.475"
},
"500": {
"value": "#00894d",
- "description": "49.99999999999999 - 0.5",
- "type": "color"
+ "type": "color",
+ "description": "49.99999999999999 - 0.5"
},
"525": {
"value": "#009353",
- "description": "53.56721608979418 - 0.525",
- "type": "color"
+ "type": "color",
+ "description": "53.56721608979418 - 0.525"
},
"550": {
"value": "#009e5a",
- "description": "57.11629243900867 - 0.55",
- "type": "color"
+ "type": "color",
+ "description": "57.11629243900867 - 0.55"
},
"575": {
"value": "#00a860",
- "description": "60.62920114459644 - 0.575",
- "type": "color"
+ "type": "color",
+ "description": "60.62920114459644 - 0.575"
},
"600": {
"value": "#00b266",
- "description": "64.08813728906053 - 0.6",
- "type": "color"
+ "type": "color",
+ "description": "64.08813728906053 - 0.6"
},
"625": {
"value": "#00bc6c",
- "description": "67.47562871369087 - 0.625",
- "type": "color"
+ "type": "color",
+ "description": "67.47562871369087 - 0.625"
},
"650": {
"value": "#00c772",
- "description": "70.774643740233 - 0.65",
- "type": "color"
+ "type": "color",
+ "description": "70.774643740233 - 0.65"
},
"675": {
"value": "#00d078",
- "description": "73.96869617684807 - 0.675",
- "type": "color"
+ "type": "color",
+ "description": "73.96869617684807 - 0.675"
},
"700": {
"value": "#00da7e",
- "description": "77.04194696096773 - 0.7",
- "type": "color"
+ "type": "color",
+ "description": "77.04194696096773 - 0.7"
},
"725": {
"value": "#00e384",
- "description": "79.97930181238189 - 0.725",
- "type": "color"
+ "type": "color",
+ "description": "79.97930181238189 - 0.725"
},
"750": {
"value": "#00ec89",
- "description": "82.76650429449552 - 0.75",
- "type": "color"
+ "type": "color",
+ "description": "82.76650429449552 - 0.75"
},
"775": {
"value": "#00f48e",
- "description": "85.39022371000115 - 0.775",
- "type": "color"
+ "type": "color",
+ "description": "85.39022371000115 - 0.775"
},
"800": {
"value": "#00fc93",
- "description": "87.83813728906054 - 0.8",
- "type": "color"
+ "type": "color",
+ "description": "87.83813728906054 - 0.8"
},
"825": {
"value": "#5cffa5",
- "description": "90.09900616327847 - 0.825",
- "type": "color"
+ "type": "color",
+ "description": "90.09900616327847 - 0.825"
},
"850": {
"value": "#91ffbb",
- "description": "92.16274465706378 - 0.85",
- "type": "color"
+ "type": "color",
+ "description": "92.16274465706378 - 0.85"
},
"875": {
"value": "#b2ffcd",
- "description": "94.02048246917325 - 0.875",
- "type": "color"
+ "type": "color",
+ "description": "94.02048246917325 - 0.875"
},
"900": {
"value": "#caffdc",
- "description": "95.66461936106825 - 0.9",
- "type": "color"
+ "type": "color",
+ "description": "95.66461936106825 - 0.9"
},
"925": {
"value": "#ddffe8",
- "description": "97.08887201491288 - 0.925",
- "type": "color"
+ "type": "color",
+ "description": "97.08887201491288 - 0.925"
},
"950": {
"value": "#ebfff1",
- "description": "98.28831277231767 - 0.95",
- "type": "color"
+ "type": "color",
+ "description": "98.28831277231767 - 0.95"
},
"975": {
"value": "#f7fff9",
- "description": "99.25940001499231 - 0.975",
- "type": "color"
+ "type": "color",
+ "description": "99.25940001499231 - 0.975"
},
"1000": {
"value": "#ffffff",
- "description": "100 - 1",
- "type": "color"
+ "type": "color",
+ "description": "100 - 1"
}
},
"Red": {
"0": {
"value": "#000000",
- "description": "0 - 0",
- "type": "color"
+ "type": "color",
+ "description": "0 - 0"
},
"25": {
"value": "#0c0001",
- "description": "0.7405999850077014 - 0.025",
- "type": "color"
+ "type": "color",
+ "description": "0.7405999850077014 - 0.025"
},
"50": {
"value": "#170003",
- "description": "1.7116872276823336 - 0.05",
- "type": "color"
+ "type": "color",
+ "description": "1.7116872276823336 - 0.05"
},
"75": {
"value": "#200005",
- "description": "2.911127985087129 - 0.075",
- "type": "color"
+ "type": "color",
+ "description": "2.911127985087129 - 0.075"
},
"100": {
"value": "#290007",
- "description": "4.335380638931743 - 0.1",
- "type": "color"
+ "type": "color",
+ "description": "4.335380638931743 - 0.1"
},
"125": {
"value": "#310009",
- "description": "5.979517530826747 - 0.125",
- "type": "color"
+ "type": "color",
+ "description": "5.979517530826747 - 0.125"
},
"150": {
"value": "#38000c",
- "description": "7.837255342936205 - 0.15",
- "type": "color"
+ "type": "color",
+ "description": "7.837255342936205 - 0.15"
},
"175": {
"value": "#40000f",
- "description": "9.90099383672154 - 0.175",
- "type": "color"
+ "type": "color",
+ "description": "9.90099383672154 - 0.175"
},
"200": {
"value": "#480012",
- "description": "12.16186271093947 - 0.2",
- "type": "color"
+ "type": "color",
+ "description": "12.16186271093947 - 0.2"
},
"225": {
"value": "#520016",
- "description": "14.609776289998841 - 0.225",
- "type": "color"
+ "type": "color",
+ "description": "14.609776289998841 - 0.225"
},
"250": {
"value": "#5c001a",
- "description": "17.233495705504463 - 0.25",
- "type": "color"
+ "type": "color",
+ "description": "17.233495705504463 - 0.25"
},
"275": {
"value": "#67001e",
- "description": "20.02069818761812 - 0.275",
- "type": "color"
+ "type": "color",
+ "description": "20.02069818761812 - 0.275"
},
"300": {
"value": "#730022",
- "description": "22.958053039032254 - 0.3",
- "type": "color"
+ "type": "color",
+ "description": "22.958053039032254 - 0.3"
},
"325": {
"value": "#800027",
- "description": "26.03130382315192 - 0.325",
- "type": "color"
+ "type": "color",
+ "description": "26.03130382315192 - 0.325"
},
"350": {
"value": "#8e002c",
- "description": "29.22535625976699 - 0.35",
- "type": "color"
+ "type": "color",
+ "description": "29.22535625976699 - 0.35"
},
"375": {
"value": "#9c0031",
- "description": "32.524371286309126 - 0.375",
- "type": "color"
+ "type": "color",
+ "description": "32.524371286309126 - 0.375"
},
"400": {
"value": "#ab0037",
- "description": "35.91186271093947 - 0.4",
- "type": "color"
+ "type": "color",
+ "description": "35.91186271093947 - 0.4"
},
"425": {
"value": "#bb003d",
- "description": "39.37079885540354 - 0.425",
- "type": "color"
+ "type": "color",
+ "description": "39.37079885540354 - 0.425"
},
"450": {
"value": "#cb0043",
- "description": "42.88370756099135 - 0.45",
- "type": "color"
+ "type": "color",
+ "description": "42.88370756099135 - 0.45"
},
"475": {
"value": "#db0049",
- "description": "46.43278391020581 - 0.475",
- "type": "color"
+ "type": "color",
+ "description": "46.43278391020581 - 0.475"
},
"500": {
"value": "#ec004f",
- "description": "49.99999999999999 - 0.5",
- "type": "color"
+ "type": "color",
+ "description": "49.99999999999999 - 0.5"
},
"525": {
"value": "#fd0056",
- "description": "53.56721608979418 - 0.525",
- "type": "color"
+ "type": "color",
+ "description": "53.56721608979418 - 0.525"
},
"550": {
"value": "#ff3865",
- "description": "57.11629243900867 - 0.55",
- "type": "color"
+ "type": "color",
+ "description": "57.11629243900867 - 0.55"
},
"575": {
"value": "#ff5474",
- "description": "60.62920114459644 - 0.575",
- "type": "color"
+ "type": "color",
+ "description": "60.62920114459644 - 0.575"
},
"600": {
"value": "#ff6981",
- "description": "64.08813728906053 - 0.6",
- "type": "color"
+ "type": "color",
+ "description": "64.08813728906053 - 0.6"
},
"625": {
"value": "#ff7a8e",
- "description": "67.47562871369087 - 0.625",
- "type": "color"
+ "type": "color",
+ "description": "67.47562871369087 - 0.625"
},
"650": {
"value": "#ff8a9a",
- "description": "70.774643740233 - 0.65",
- "type": "color"
+ "type": "color",
+ "description": "70.774643740233 - 0.65"
},
"675": {
"value": "#ff99a6",
- "description": "73.96869617684807 - 0.675",
- "type": "color"
+ "type": "color",
+ "description": "73.96869617684807 - 0.675"
},
"700": {
"value": "#ffa6b1",
- "description": "77.04194696096773 - 0.7",
- "type": "color"
+ "type": "color",
+ "description": "77.04194696096773 - 0.7"
},
"725": {
"value": "#ffb2bb",
- "description": "79.97930181238189 - 0.725",
- "type": "color"
+ "type": "color",
+ "description": "79.97930181238189 - 0.725"
},
"750": {
"value": "#ffbdc5",
- "description": "82.76650429449552 - 0.75",
- "type": "color"
+ "type": "color",
+ "description": "82.76650429449552 - 0.75"
},
"775": {
"value": "#ffc8ce",
- "description": "85.39022371000115 - 0.775",
- "type": "color"
+ "type": "color",
+ "description": "85.39022371000115 - 0.775"
},
"800": {
"value": "#ffd1d6",
- "description": "87.83813728906054 - 0.8",
- "type": "color"
+ "type": "color",
+ "description": "87.83813728906054 - 0.8"
},
"825": {
"value": "#ffdade",
- "description": "90.09900616327847 - 0.825",
- "type": "color"
+ "type": "color",
+ "description": "90.09900616327847 - 0.825"
},
"850": {
"value": "#ffe2e5",
- "description": "92.16274465706378 - 0.85",
- "type": "color"
+ "type": "color",
+ "description": "92.16274465706378 - 0.85"
},
"875": {
"value": "#ffe9eb",
- "description": "94.02048246917325 - 0.875",
- "type": "color"
+ "type": "color",
+ "description": "94.02048246917325 - 0.875"
},
"900": {
"value": "#ffeff0",
- "description": "95.66461936106825 - 0.9",
- "type": "color"
+ "type": "color",
+ "description": "95.66461936106825 - 0.9"
},
"925": {
"value": "#fff4f5",
- "description": "97.08887201491288 - 0.925",
- "type": "color"
+ "type": "color",
+ "description": "97.08887201491288 - 0.925"
},
"950": {
"value": "#fff9f9",
- "description": "98.28831277231767 - 0.95",
- "type": "color"
+ "type": "color",
+ "description": "98.28831277231767 - 0.95"
},
"975": {
"value": "#fffcfd",
- "description": "99.25940001499231 - 0.975",
- "type": "color"
+ "type": "color",
+ "description": "99.25940001499231 - 0.975"
},
"1000": {
"value": "#ffffff",
- "description": "100 - 1",
- "type": "color"
+ "type": "color",
+ "description": "100 - 1"
}
},
"Blue": {
"0": {
"value": "#000000",
- "description": "0 - 0",
- "type": "color"
+ "type": "color",
+ "description": "0 - 0"
},
"25": {
"value": "#00020f",
- "description": "0.7405999850077014 - 0.025",
- "type": "color"
+ "type": "color",
+ "description": "0.7405999850077014 - 0.025"
},
"50": {
"value": "#00051c",
- "description": "1.7116872276823336 - 0.05",
- "type": "color"
+ "type": "color",
+ "description": "1.7116872276823336 - 0.05"
},
"75": {
"value": "#000826",
- "description": "2.911127985087129 - 0.075",
- "type": "color"
+ "type": "color",
+ "description": "2.911127985087129 - 0.075"
},
"100": {
"value": "#000c30",
- "description": "4.335380638931743 - 0.1",
- "type": "color"
+ "type": "color",
+ "description": "4.335380638931743 - 0.1"
},
"125": {
"value": "#001038",
- "description": "5.979517530826747 - 0.125",
- "type": "color"
+ "type": "color",
+ "description": "5.979517530826747 - 0.125"
},
"150": {
"value": "#001441",
- "description": "7.837255342936205 - 0.15",
- "type": "color"
+ "type": "color",
+ "description": "7.837255342936205 - 0.15"
},
"175": {
"value": "#001749",
- "description": "9.90099383672154 - 0.175",
- "type": "color"
+ "type": "color",
+ "description": "9.90099383672154 - 0.175"
},
"200": {
"value": "#001b53",
- "description": "12.16186271093947 - 0.2",
- "type": "color"
+ "type": "color",
+ "description": "12.16186271093947 - 0.2"
},
"225": {
"value": "#00205e",
- "description": "14.609776289998841 - 0.225",
- "type": "color"
+ "type": "color",
+ "description": "14.609776289998841 - 0.225"
},
"250": {
"value": "#002569",
- "description": "17.233495705504463 - 0.25",
- "type": "color"
+ "type": "color",
+ "description": "17.233495705504463 - 0.25"
},
"275": {
"value": "#002a76",
- "description": "20.02069818761812 - 0.275",
- "type": "color"
+ "type": "color",
+ "description": "20.02069818761812 - 0.275"
},
"300": {
"value": "#003084",
- "description": "22.958053039032254 - 0.3",
- "type": "color"
+ "type": "color",
+ "description": "22.958053039032254 - 0.3"
},
"325": {
"value": "#003692",
- "description": "26.03130382315192 - 0.325",
- "type": "color"
+ "type": "color",
+ "description": "26.03130382315192 - 0.325"
},
"350": {
"value": "#003da1",
- "description": "29.22535625976699 - 0.35",
- "type": "color"
+ "type": "color",
+ "description": "29.22535625976699 - 0.35"
},
"375": {
"value": "#0044b2",
- "description": "32.524371286309126 - 0.375",
- "type": "color"
+ "type": "color",
+ "description": "32.524371286309126 - 0.375"
},
"400": {
"value": "#004bc2",
- "description": "35.91186271093947 - 0.4",
- "type": "color"
+ "type": "color",
+ "description": "35.91186271093947 - 0.4"
},
"425": {
"value": "#0053d4",
- "description": "39.37079885540354 - 0.425",
- "type": "color"
+ "type": "color",
+ "description": "39.37079885540354 - 0.425"
},
"450": {
"value": "#005ae6",
- "description": "42.88370756099135 - 0.45",
- "type": "color"
+ "type": "color",
+ "description": "42.88370756099135 - 0.45"
},
"475": {
"value": "#0062f9",
- "description": "46.43278391020581 - 0.475",
- "type": "color"
+ "type": "color",
+ "description": "46.43278391020581 - 0.475"
},
"500": {
"value": "#2a6cff",
- "description": "49.99999999999999 - 0.5",
- "type": "color"
+ "type": "color",
+ "description": "49.99999999999999 - 0.5"
},
"525": {
"value": "#4676ff",
- "description": "53.56721608979418 - 0.525",
- "type": "color"
+ "type": "color",
+ "description": "53.56721608979418 - 0.525"
},
"550": {
"value": "#5a81ff",
- "description": "57.11629243900867 - 0.55",
- "type": "color"
+ "type": "color",
+ "description": "57.11629243900867 - 0.55"
},
"575": {
"value": "#6b8bff",
- "description": "60.62920114459644 - 0.575",
- "type": "color"
+ "type": "color",
+ "description": "60.62920114459644 - 0.575"
},
"600": {
"value": "#7a95ff",
- "description": "64.08813728906053 - 0.6",
- "type": "color"
+ "type": "color",
+ "description": "64.08813728906053 - 0.6"
},
"625": {
"value": "#899fff",
- "description": "67.47562871369087 - 0.625",
- "type": "color"
+ "type": "color",
+ "description": "67.47562871369087 - 0.625"
},
"650": {
"value": "#96a8ff",
- "description": "70.774643740233 - 0.65",
- "type": "color"
+ "type": "color",
+ "description": "70.774643740233 - 0.65"
},
"675": {
"value": "#a2b2ff",
- "description": "73.96869617684807 - 0.675",
- "type": "color"
+ "type": "color",
+ "description": "73.96869617684807 - 0.675"
},
"700": {
"value": "#adbbff",
- "description": "77.04194696096773 - 0.7",
- "type": "color"
+ "type": "color",
+ "description": "77.04194696096773 - 0.7"
},
"725": {
"value": "#b8c3ff",
- "description": "79.97930181238189 - 0.725",
- "type": "color"
+ "type": "color",
+ "description": "79.97930181238189 - 0.725"
},
"750": {
"value": "#c2ccff",
- "description": "82.76650429449552 - 0.75",
- "type": "color"
+ "type": "color",
+ "description": "82.76650429449552 - 0.75"
},
"775": {
"value": "#ccd3ff",
- "description": "85.39022371000115 - 0.775",
- "type": "color"
+ "type": "color",
+ "description": "85.39022371000115 - 0.775"
},
"800": {
"value": "#d4dbff",
- "description": "87.83813728906054 - 0.8",
- "type": "color"
+ "type": "color",
+ "description": "87.83813728906054 - 0.8"
},
"825": {
"value": "#dce1ff",
- "description": "90.09900616327847 - 0.825",
- "type": "color"
+ "type": "color",
+ "description": "90.09900616327847 - 0.825"
},
"850": {
"value": "#e4e7ff",
- "description": "92.16274465706378 - 0.85",
- "type": "color"
+ "type": "color",
+ "description": "92.16274465706378 - 0.85"
},
"875": {
"value": "#eaedff",
- "description": "94.02048246917325 - 0.875",
- "type": "color"
+ "type": "color",
+ "description": "94.02048246917325 - 0.875"
},
"900": {
"value": "#f0f2ff",
- "description": "95.66461936106825 - 0.9",
- "type": "color"
+ "type": "color",
+ "description": "95.66461936106825 - 0.9"
},
"925": {
"value": "#f5f6ff",
- "description": "97.08887201491288 - 0.925",
- "type": "color"
+ "type": "color",
+ "description": "97.08887201491288 - 0.925"
},
"950": {
"value": "#f9faff",
- "description": "98.28831277231767 - 0.95",
- "type": "color"
+ "type": "color",
+ "description": "98.28831277231767 - 0.95"
},
"975": {
"value": "#fcfdff",
- "description": "99.25940001499231 - 0.975",
- "type": "color"
+ "type": "color",
+ "description": "99.25940001499231 - 0.975"
},
"1000": {
"value": "#ffffff",
- "description": "100 - 1",
- "type": "color"
+ "type": "color",
+ "description": "100 - 1"
}
},
"Purple": {
"0": {
"value": "#000000",
- "description": "0 - 0",
- "type": "color"
+ "type": "color",
+ "description": "0 - 0"
},
"25": {
"value": "#060011",
- "description": "0.7405999850077014 - 0.025",
- "type": "color"
+ "type": "color",
+ "description": "0.7405999850077014 - 0.025"
},
"50": {
"value": "#0e001e",
- "description": "1.7116872276823336 - 0.05",
- "type": "color"
+ "type": "color",
+ "description": "1.7116872276823336 - 0.05"
},
"75": {
"value": "#150029",
- "description": "2.911127985087129 - 0.075",
- "type": "color"
+ "type": "color",
+ "description": "2.911127985087129 - 0.075"
},
"100": {
"value": "#1b0033",
- "description": "4.335380638931743 - 0.1",
- "type": "color"
+ "type": "color",
+ "description": "4.335380638931743 - 0.1"
},
"125": {
"value": "#21003d",
- "description": "5.979517530826747 - 0.125",
- "type": "color"
+ "type": "color",
+ "description": "5.979517530826747 - 0.125"
},
"150": {
"value": "#270046",
- "description": "7.837255342936205 - 0.15",
- "type": "color"
+ "type": "color",
+ "description": "7.837255342936205 - 0.15"
},
"175": {
"value": "#2d004f",
- "description": "9.90099383672154 - 0.175",
- "type": "color"
+ "type": "color",
+ "description": "9.90099383672154 - 0.175"
},
"200": {
"value": "#330059",
- "description": "12.16186271093947 - 0.2",
- "type": "color"
+ "type": "color",
+ "description": "12.16186271093947 - 0.2"
},
"225": {
"value": "#3a0065",
- "description": "14.609776289998841 - 0.225",
- "type": "color"
+ "type": "color",
+ "description": "14.609776289998841 - 0.225"
},
"250": {
"value": "#420071",
- "description": "17.233495705504463 - 0.25",
- "type": "color"
+ "type": "color",
+ "description": "17.233495705504463 - 0.25"
},
"275": {
"value": "#4b007e",
- "description": "20.02069818761812 - 0.275",
- "type": "color"
+ "type": "color",
+ "description": "20.02069818761812 - 0.275"
},
"300": {
"value": "#54008d",
- "description": "22.958053039032254 - 0.3",
- "type": "color"
+ "type": "color",
+ "description": "22.958053039032254 - 0.3"
},
"325": {
"value": "#5d009c",
- "description": "26.03130382315192 - 0.325",
- "type": "color"
+ "type": "color",
+ "description": "26.03130382315192 - 0.325"
},
"350": {
"value": "#6800ad",
- "description": "29.22535625976699 - 0.35",
- "type": "color"
+ "type": "color",
+ "description": "29.22535625976699 - 0.35"
},
"375": {
"value": "#7200be",
- "description": "32.524371286309126 - 0.375",
- "type": "color"
+ "type": "color",
+ "description": "32.524371286309126 - 0.375"
},
"400": {
"value": "#7e00d0",
- "description": "35.91186271093947 - 0.4",
- "type": "color"
+ "type": "color",
+ "description": "35.91186271093947 - 0.4"
},
"425": {
"value": "#8a00e2",
- "description": "39.37079885540354 - 0.425",
- "type": "color"
+ "type": "color",
+ "description": "39.37079885540354 - 0.425"
},
"450": {
"value": "#9600f6",
- "description": "42.88370756099135 - 0.45",
- "type": "color"
+ "type": "color",
+ "description": "42.88370756099135 - 0.45"
},
"475": {
"value": "#9e22ff",
- "description": "46.43278391020581 - 0.475",
- "type": "color"
+ "type": "color",
+ "description": "46.43278391020581 - 0.475"
},
"500": {
"value": "#a23eff",
- "description": "49.99999999999999 - 0.5",
- "type": "color"
+ "type": "color",
+ "description": "49.99999999999999 - 0.5"
},
"525": {
"value": "#a752ff",
- "description": "53.56721608979418 - 0.525",
- "type": "color"
+ "type": "color",
+ "description": "53.56721608979418 - 0.525"
},
"550": {
"value": "#ac63ff",
- "description": "57.11629243900867 - 0.55",
- "type": "color"
+ "type": "color",
+ "description": "57.11629243900867 - 0.55"
},
"575": {
"value": "#b273ff",
- "description": "60.62920114459644 - 0.575",
- "type": "color"
+ "type": "color",
+ "description": "60.62920114459644 - 0.575"
},
"600": {
"value": "#b780ff",
- "description": "64.08813728906053 - 0.6",
- "type": "color"
+ "type": "color",
+ "description": "64.08813728906053 - 0.6"
},
"625": {
"value": "#bd8eff",
- "description": "67.47562871369087 - 0.625",
- "type": "color"
+ "type": "color",
+ "description": "67.47562871369087 - 0.625"
},
"650": {
"value": "#c39aff",
- "description": "70.774643740233 - 0.65",
- "type": "color"
+ "type": "color",
+ "description": "70.774643740233 - 0.65"
},
"675": {
"value": "#c9a5ff",
- "description": "73.96869617684807 - 0.675",
- "type": "color"
+ "type": "color",
+ "description": "73.96869617684807 - 0.675"
},
"700": {
"value": "#cfb0ff",
- "description": "77.04194696096773 - 0.7",
- "type": "color"
+ "type": "color",
+ "description": "77.04194696096773 - 0.7"
},
"725": {
"value": "#d4bbff",
- "description": "79.97930181238189 - 0.725",
- "type": "color"
+ "type": "color",
+ "description": "79.97930181238189 - 0.725"
},
"750": {
"value": "#dac4ff",
- "description": "82.76650429449552 - 0.75",
- "type": "color"
+ "type": "color",
+ "description": "82.76650429449552 - 0.75"
},
"775": {
"value": "#dfcdff",
- "description": "85.39022371000115 - 0.775",
- "type": "color"
+ "type": "color",
+ "description": "85.39022371000115 - 0.775"
},
"800": {
"value": "#e4d6ff",
- "description": "87.83813728906054 - 0.8",
- "type": "color"
+ "type": "color",
+ "description": "87.83813728906054 - 0.8"
},
"825": {
"value": "#e9ddff",
- "description": "90.09900616327847 - 0.825",
- "type": "color"
+ "type": "color",
+ "description": "90.09900616327847 - 0.825"
},
"850": {
"value": "#eee4ff",
- "description": "92.16274465706378 - 0.85",
- "type": "color"
+ "type": "color",
+ "description": "92.16274465706378 - 0.85"
},
"875": {
"value": "#f2ebff",
- "description": "94.02048246917325 - 0.875",
- "type": "color"
+ "type": "color",
+ "description": "94.02048246917325 - 0.875"
},
"900": {
"value": "#f5f0ff",
- "description": "95.66461936106825 - 0.9",
- "type": "color"
+ "type": "color",
+ "description": "95.66461936106825 - 0.9"
},
"925": {
"value": "#f8f5ff",
- "description": "97.08887201491288 - 0.925",
- "type": "color"
+ "type": "color",
+ "description": "97.08887201491288 - 0.925"
},
"950": {
"value": "#fbf9ff",
- "description": "98.28831277231767 - 0.95",
- "type": "color"
+ "type": "color",
+ "description": "98.28831277231767 - 0.95"
},
"975": {
"value": "#fdfcff",
- "description": "99.25940001499231 - 0.975",
- "type": "color"
+ "type": "color",
+ "description": "99.25940001499231 - 0.975"
},
"1000": {
"value": "#ffffff",
- "description": "100 - 1",
- "type": "color"
+ "type": "color",
+ "description": "100 - 1"
}
},
"OpaqueGrey": {
"Default": {
"value": "#7c7c7c20",
- "description": "An opaque grey that picks up some, but not all, of the colors behind it",
+ "type": "color",
+ "description": "An opaque grey that picks up some, but not all, of the colors behind it"
+ }
+ },
+ "Gray": {
+ "25": {
+ "value": "#fcfcfd",
+ "type": "color"
+ },
+ "50": {
+ "value": "#f9fafb",
+ "type": "color"
+ },
+ "100": {
+ "value": "#f2f4f7",
+ "type": "color"
+ },
+ "200": {
+ "value": "#eaecf0",
+ "type": "color"
+ },
+ "300": {
+ "value": "#d0d5dd",
+ "type": "color"
+ },
+ "400": {
+ "value": "#98a2b3",
+ "type": "color"
+ },
+ "500": {
+ "value": "#667085",
+ "type": "color"
+ },
+ "600": {
+ "value": "#475467",
+ "type": "color"
+ },
+ "700": {
+ "value": "#344054",
+ "type": "color"
+ },
+ "800": {
+ "value": "#1d2939",
+ "type": "color"
+ },
+ "900": {
+ "value": "#101828",
+ "type": "color"
+ }
+ },
+ "White": {
+ "value": "#ffffff",
+ "type": "color"
+ },
+ "Black": {
+ "value": "#000000",
+ "type": "color"
+ },
+ "Primary": {
+ "25": {
+ "value": "#f6fcfe",
+ "type": "color"
+ },
+ "50": {
+ "value": "#dceffc",
+ "type": "color"
+ },
+ "100": {
+ "value": "#c1dcf9",
+ "type": "color"
+ },
+ "200": {
+ "value": "#a7c3f6",
+ "type": "color"
+ },
+ "300": {
+ "value": "#8da4f4",
+ "type": "color"
+ },
+ "400": {
+ "value": "#6178f4",
+ "type": "color"
+ },
+ "500": {
+ "value": "#4c4ff1",
+ "type": "color"
+ },
+ "600": {
+ "value": "#4e38ed",
+ "type": "color"
+ },
+ "700": {
+ "value": "#5724e8",
+ "type": "color"
+ },
+ "800": {
+ "value": "#441bb6",
+ "type": "color"
+ },
+ "900": {
+ "value": "#301383",
+ "type": "color"
+ },
+ "Default": {
+ "value": "#4e38ed",
+ "type": "color"
+ },
+ "Hover": {
+ "value": "#eaecf0",
+ "type": "color"
+ }
+ },
+ "Warning": {
+ "25": {
+ "value": "#fffcf5",
+ "type": "color"
+ },
+ "50": {
+ "value": "#fffaeb",
+ "type": "color"
+ },
+ "100": {
+ "value": "#fef0c7",
+ "type": "color"
+ },
+ "200": {
+ "value": "#fedf89",
+ "type": "color"
+ },
+ "300": {
+ "value": "#fec84b",
+ "type": "color"
+ },
+ "400": {
+ "value": "#fdb022",
+ "type": "color"
+ },
+ "500": {
+ "value": "#f79009",
+ "type": "color"
+ },
+ "600": {
+ "value": "#dc6803",
+ "type": "color"
+ },
+ "700": {
+ "value": "#b54708",
+ "type": "color"
+ }
+ },
+ "Error": {
+ "25": {
+ "value": "#fffbfa",
+ "type": "color"
+ },
+ "50": {
+ "value": "#fef3f2",
+ "type": "color"
+ },
+ "100": {
+ "value": "#fee4ef",
+ "type": "color"
+ },
+ "200": {
+ "value": "#fecdca",
+ "type": "color"
+ },
+ "300": {
+ "value": "#fda29b",
+ "type": "color"
+ },
+ "400": {
+ "value": "#f97066",
+ "type": "color"
+ },
+ "500": {
+ "value": "#f04438",
+ "type": "color"
+ },
+ "600": {
+ "value": "#d92d20",
+ "type": "color"
+ },
+ "700": {
+ "value": "#b42318",
+ "type": "color"
+ }
+ },
+ "Success": {
+ "25": {
+ "value": "#f6fef9",
+ "type": "color"
+ },
+ "50": {
+ "value": "#ecfdf3",
+ "type": "color"
+ },
+ "100": {
+ "value": "#d1fadf",
+ "type": "color"
+ },
+ "200": {
+ "value": "#a6f4c5",
+ "type": "color"
+ },
+ "300": {
+ "value": "#6ce9a6",
+ "type": "color"
+ },
+ "400": {
+ "value": "#32d583",
+ "type": "color"
+ },
+ "500": {
+ "value": "#12b76a",
+ "type": "color"
+ },
+ "600": {
+ "value": "#039855",
+ "type": "color"
+ },
+ "700": {
+ "value": "#027a48",
+ "type": "color"
+ }
+ },
+ "Pink": {
+ "700": {
+ "value": "#c11574",
+ "type": "color"
+ }
+ },
+ "Text": {
+ "Primary": {
+ "value": "#101828",
+ "type": "color"
+ },
+ "Secondary": {
+ "value": "#667085",
"type": "color"
}
}
@@ -1175,8 +1418,8 @@
"lineHeight": "12px",
"letterSpacing": "-0.12px"
},
- "description": "",
- "type": "typography"
+ "type": "typography",
+ "description": ""
},
"200": {
"value": {
@@ -1186,8 +1429,8 @@
"lineHeight": "16px",
"letterSpacing": "-0.15px"
},
- "description": "",
- "type": "typography"
+ "type": "typography",
+ "description": ""
}
},
"Shadow": {
@@ -1199,8 +1442,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"200": {
"value": {
@@ -1210,8 +1453,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"300": {
"value": {
@@ -1221,8 +1464,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"400": {
"value": {
@@ -1232,8 +1475,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"500": {
"value": {
@@ -1243,8 +1486,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"600": {
"value": {
@@ -1254,8 +1497,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"700": {
"value": {
@@ -1265,8 +1508,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"800": {
"value": {
@@ -1276,8 +1519,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"900": {
"value": {
@@ -1287,8 +1530,8 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
},
"1000": {
"value": {
@@ -1298,98 +1541,102 @@
"spread": "0px",
"color": "rgba(0, 0, 0, .2)"
},
- "description": "",
- "type": "boxShadow"
+ "type": "boxShadow",
+ "description": ""
}
},
"Radius": {
"0": {
"value": "0",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"100": {
"value": "2",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"200": {
"value": "6",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"300": {
"value": "14",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"400": {
"value": "30",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"500": {
"value": "62",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
}
},
"Spacing": {
"0": {
"value": "0",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"100": {
"value": "2",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"200": {
"value": "4",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"300": {
"value": "8",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"400": {
"value": "12",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"500": {
"value": "16",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"600": {
"value": "32",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"700": {
"value": "48",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"800": {
"value": "64",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"900": {
"value": "96",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
},
"1000": {
"value": "128",
- "description": "",
- "type": "dimension"
+ "type": "dimension",
+ "description": ""
}
}
+ },
+ "$themes": [],
+ "$metadata": {
+ "tokenSetOrder": ["Alias", "Global"]
}
}
diff --git a/crates/re_ui/data/icons/gear.png b/crates/re_ui/data/icons/gear.png
new file mode 100644
index 000000000000..24f732003b8d
Binary files /dev/null and b/crates/re_ui/data/icons/gear.png differ
diff --git a/crates/re_ui/data/icons/rerun_menu.png b/crates/re_ui/data/icons/rerun_menu.png
index e0326623c56e..b74059bf3dfa 100644
Binary files a/crates/re_ui/data/icons/rerun_menu.png and b/crates/re_ui/data/icons/rerun_menu.png differ
diff --git a/crates/re_ui/data/logo_dark_mode.png b/crates/re_ui/data/logo_dark_mode.png
index 2eaf405d82e9..1b951089cf2a 100644
Binary files a/crates/re_ui/data/logo_dark_mode.png and b/crates/re_ui/data/logo_dark_mode.png differ
diff --git a/crates/re_ui/data/logo_light_mode.png b/crates/re_ui/data/logo_light_mode.png
index 4675fec247da..1b951089cf2a 100644
Binary files a/crates/re_ui/data/logo_light_mode.png and b/crates/re_ui/data/logo_light_mode.png differ
diff --git a/crates/re_ui/src/command.rs b/crates/re_ui/src/command.rs
index 55f1aa2a430a..f41bf4fa43b0 100644
--- a/crates/re_ui/src/command.rs
+++ b/crates/re_ui/src/command.rs
@@ -72,7 +72,7 @@ impl Command {
Command::Open => ("Openβ¦", "Open a Rerun Data File (.rrd)"),
#[cfg(not(target_arch = "wasm32"))]
- Command::Quit => ("Quit", "Close the Rerun Viewer"),
+ Command::Quit => ("Quit", "Close the Depthai Viewer"),
Command::ResetViewer => (
"Reset viewer",
@@ -87,7 +87,7 @@ impl Command {
Command::ToggleMemoryPanel => (
"Toggle memory panel",
- "Investigate what is using up RAM in Rerun Viewer",
+ "Investigate what is using up RAM in Depthai Viewer",
),
Command::ToggleBlueprintPanel => ("Toggle blueprint panel", "Toggle the left panel"),
Command::ToggleSelectionPanel => ("Toggle selection panel", "Toggle the right panel"),
diff --git a/crates/re_ui/src/design_tokens.rs b/crates/re_ui/src/design_tokens.rs
index 4a3642695e87..dbcfdd66af40 100644
--- a/crates/re_ui/src/design_tokens.rs
+++ b/crates/re_ui/src/design_tokens.rs
@@ -11,6 +11,17 @@ pub struct DesignTokens {
pub bottom_bar_stroke: egui::Stroke,
pub bottom_bar_rounding: egui::Rounding,
pub shadow_gradient_dark_start: egui::Color32,
+ pub success_bg_color: egui::Color32,
+ pub success_hover_bg_color: egui::Color32,
+ pub warning_bg_color: egui::Color32,
+ pub warning_hover_bg_color: egui::Color32,
+ pub error_bg_color: egui::Color32,
+ pub error_hover_bg_color: egui::Color32,
+ pub primary_bg_color: egui::Color32,
+ pub primary_hover_bg_color: egui::Color32,
+ pub gray_50: egui::Color32,
+ pub gray_900: egui::Color32,
+ pub primary_700: egui::Color32,
}
impl DesignTokens {
@@ -46,7 +57,7 @@ fn apply_design_tokens(ctx: &egui::Context) -> DesignTokens {
}
let mut egui_style = egui::Style {
- visuals: egui::Visuals::dark(),
+ visuals: egui::Visuals::light(),
..Default::default()
};
@@ -71,26 +82,25 @@ fn apply_design_tokens(ctx: &egui::Context) -> DesignTokens {
}
let panel_bg_color = get_aliased_color(&json, "{Alias.Color.Surface.Default.value}");
- // let floating_color = get_aliased_color(&json, "{Alias.Color.Surface.Floating.value}");
- let floating_color = Color32::from_gray(38); // TODO(emilk): change the content of the design_tokens.json origin instead
+ let floating_color = get_aliased_color(&json, "{Alias.Color.Surface.Floating.value}");
// Used as the background of text edits, scroll bars and others things
// that needs to look different from other interactive stuff.
// We need this very dark, since the theme overall is very, very dark.
- egui_style.visuals.extreme_bg_color = egui::Color32::BLACK;
+ egui_style.visuals.extreme_bg_color = egui::Color32::WHITE;
egui_style.visuals.widgets.noninteractive.weak_bg_fill = panel_bg_color;
egui_style.visuals.widgets.noninteractive.bg_fill = panel_bg_color;
egui_style.visuals.button_frame = true;
- egui_style.visuals.widgets.inactive.weak_bg_fill = Default::default(); // Buttons have no background color when inactive
- egui_style.visuals.widgets.inactive.bg_fill = Color32::from_gray(40);
- // get_aliased_color(&json, "{Alias.Color.Action.Default.value}"); // too dark to see, especially for scroll bars
+ egui_style.visuals.widgets.inactive.weak_bg_fill =
+ get_aliased_color(&json, "{Alias.Color.Action.Inactive.value}"); // Buttons have no background color when inactive
+ egui_style.visuals.widgets.inactive.bg_fill =
+ get_aliased_color(&json, "{Alias.Color.Action.Default.value}");
{
// Background colors for buttons (menu buttons, blueprint buttons, etc) when hovered or clicked:
- // let hovered_color = get_aliased_color(&json, "{Alias.Color.Action.Hovered.value}");
- let hovered_color = Color32::from_gray(64); // TODO(emilk): change the content of the design_tokens.json origin instead
+ let hovered_color = get_aliased_color(&json, "{Alias.Color.Action.Hovered.value}");
egui_style.visuals.widgets.hovered.weak_bg_fill = hovered_color;
egui_style.visuals.widgets.hovered.bg_fill = hovered_color;
egui_style.visuals.widgets.active.weak_bg_fill = hovered_color;
@@ -100,11 +110,11 @@ fn apply_design_tokens(ctx: &egui::Context) -> DesignTokens {
}
{
- // Turn off strokes around buttons:
- egui_style.visuals.widgets.inactive.bg_stroke = Default::default();
- egui_style.visuals.widgets.hovered.bg_stroke = Default::default();
- egui_style.visuals.widgets.active.bg_stroke = Default::default();
- egui_style.visuals.widgets.open.bg_stroke = Default::default();
+ let border_color = get_global_color(&json, "{Global.Color.Gray.200}");
+ egui_style.visuals.widgets.inactive.bg_stroke = egui::Stroke::new(1.0, border_color);
+ egui_style.visuals.widgets.hovered.bg_stroke = egui::Stroke::new(1.0, border_color);
+ egui_style.visuals.widgets.active.bg_stroke = egui::Stroke::new(1.0, border_color);
+ egui_style.visuals.widgets.open.bg_stroke = egui::Stroke::new(1.0, border_color);
}
{
@@ -149,13 +159,14 @@ fn apply_design_tokens(ctx: &egui::Context) -> DesignTokens {
// Add stripes to grids and tables?
egui_style.visuals.striped = false;
egui_style.visuals.indent_has_left_vline = false;
- egui_style.spacing.button_padding = egui::Vec2::new(1.0, 0.0); // Makes the icons in the blueprint panel align
+ egui_style.spacing.button_padding = egui::Vec2::new(12.0, 4.0); // Makes the icons in the blueprint panel align
egui_style.spacing.indent = 14.0; // From figma
egui_style.debug.show_blocking_widget = false; // turn this on to debug interaction problems
egui_style.spacing.combo_width = 8.0; // minimum width of ComboBox - keep them small, with the down-arrow close.
+ egui_style.spacing.icon_width = 18.0; // Checkbox width and height
egui_style.spacing.scroll_bar_inner_margin = 2.0;
egui_style.spacing.scroll_bar_width = 6.0;
egui_style.spacing.scroll_bar_outer_margin = 2.0;
@@ -163,26 +174,42 @@ fn apply_design_tokens(ctx: &egui::Context) -> DesignTokens {
ctx.set_style(egui_style);
DesignTokens {
- top_bar_color: Color32::from_gray(20), // copied from figma
- bottom_bar_color: get_global_color(&json, "{Global.Color.Grey.150}"),
- bottom_bar_stroke: egui::Stroke::new(1.0, egui::Color32::from_gray(47)), // copied from figma
+ top_bar_color: get_global_color(&json, "{Global.Color.Gray.50}"), // copied from figma
+ bottom_bar_color: get_global_color(&json, "{Global.Color.Gray.100}"),
+ bottom_bar_stroke: egui::Stroke::new(
+ 1.0,
+ Color32::TRANSPARENT, // Transparent because it doesn't look good in light mode
+ ), // copied from figma
bottom_bar_rounding: egui::Rounding {
nw: 6.0,
ne: 6.0,
sw: 0.0,
se: 0.0,
}, // copied from figma, should be top only
- shadow_gradient_dark_start: egui::Color32::from_black_alpha(77),
+ shadow_gradient_dark_start: Color32::TRANSPARENT,
+ success_bg_color: get_global_color(&json, "{Global.Color.Success.200}"),
+ success_hover_bg_color: get_global_color(&json, "{Global.Color.Success.300}"),
+ warning_bg_color: get_global_color(&json, "{Global.Color.Warning.200}"),
+ warning_hover_bg_color: get_global_color(&json, "{Global.Color.Warning.300}"),
+ error_bg_color: get_global_color(&json, "{Global.Color.Error.200}"),
+ error_hover_bg_color: get_global_color(&json, "{Global.Color.Error.300}"),
+ primary_bg_color: get_global_color(&json, "{Global.Color.Primary.Default}"),
+ primary_hover_bg_color: get_global_color(&json, "{Global.Color.Primary.500}"),
+ gray_50: get_global_color(&json, "{Global.Color.Gray.50}"),
+ gray_900: get_global_color(&json, "{Global.Color.Gray.900}"),
+ primary_700: get_global_color(&json, "{Global.Color.Primary.700}"),
}
}
// ----------------------------------------------------------------------------
fn get_aliased_color(json: &serde_json::Value, alias_path: &str) -> egui::Color32 {
+ re_log::debug!("Alias path: {alias_path}");
parse_color(get_alias_str(json, alias_path))
}
fn get_global_color(json: &serde_json::Value, global_path: &str) -> egui::Color32 {
+ re_log::debug!("Global path: {global_path}");
parse_color(global_path_value(json, global_path).as_str().unwrap())
}
diff --git a/crates/re_ui/src/icons.rs b/crates/re_ui/src/icons.rs
index ecd81778f537..b453030bf320 100644
--- a/crates/re_ui/src/icons.rs
+++ b/crates/re_ui/src/icons.rs
@@ -63,6 +63,8 @@ pub const RESET: Icon = Icon::new("reset", include_bytes!("../data/icons/reset.p
pub const CLOSE: Icon = Icon::new("close", include_bytes!("../data/icons/close.png"));
+pub const GEAR: Icon = Icon::new("gear", include_bytes!("../data/icons/gear.png"));
+
pub const SPACE_VIEW_TEXT: Icon = Icon::new(
"spaceview_text",
include_bytes!("../data/icons/spaceview_text.png"),
diff --git a/crates/re_ui/src/lib.rs b/crates/re_ui/src/lib.rs
index d5d9569cf7b2..d8a9d782db95 100644
--- a/crates/re_ui/src/lib.rs
+++ b/crates/re_ui/src/lib.rs
@@ -14,6 +14,7 @@ pub use command_palette::CommandPalette;
pub use design_tokens::DesignTokens;
pub use icons::Icon;
pub use static_image_cache::StaticImageCache;
+use std::ops::RangeInclusive;
pub use toggle_switch::toggle_switch;
// ---------------------------------------------------------------------------
@@ -128,6 +129,99 @@ impl ReUi {
10.0
}
+ #[inline]
+ pub const fn box_width() -> f32 {
+ 139.0
+ }
+
+ #[inline]
+ pub const fn box_height() -> f32 {
+ 22.0
+ }
+
+ pub fn labeled_combo_box(
+ &self,
+ ui: &mut egui::Ui,
+ label: &str,
+ selected_text: String,
+ left_to_right: bool,
+ menu_contents: impl FnOnce(&mut egui::Ui) -> R,
+ ) {
+ let align = egui::Align::Center;
+ let layout = if left_to_right {
+ egui::Layout::left_to_right(align)
+ } else {
+ egui::Layout::right_to_left(align)
+ };
+
+ ui.with_layout(layout, |ui| {
+ if left_to_right {
+ ui.label(egui::RichText::new(label).color(self.design_tokens.gray_900));
+ }
+ ui.add_sized(
+ [Self::box_width(), Self::box_height() + 1.0],
+ |ui: &mut egui::Ui| {
+ egui::ComboBox::from_id_source(label)
+ .selected_text(selected_text)
+ .width(Self::box_width())
+ .show_ui(ui, menu_contents)
+ .response
+ },
+ );
+ if !left_to_right {
+ ui.label(egui::RichText::new(label).color(self.design_tokens.gray_900));
+ }
+ });
+ }
+
+ pub fn labeled_checkbox(&self, ui: &mut egui::Ui, label: &str, value: &mut bool) {
+ ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
+ ui.add_sized(
+ [Self::box_width(), Self::box_height()],
+ |ui: &mut egui::Ui| {
+ ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
+ ui.checkbox(value, "");
+ })
+ .response
+ },
+ );
+ ui.label(egui::RichText::new(label).color(self.design_tokens.gray_900));
+ });
+ }
+
+ pub fn labeled_dragvalue(
+ &self,
+ ui: &mut egui::Ui,
+ label: &str,
+ value: &mut Num,
+ range: RangeInclusive,
+ ) where
+ Num: egui::emath::Numeric,
+ {
+ ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
+ ui.add_sized(
+ [Self::box_width(), Self::box_height()],
+ egui::DragValue::new(value).clamp_range(range),
+ );
+ ui.label(egui::RichText::new(label).color(self.design_tokens.gray_900));
+ });
+ }
+
+ pub fn labeled_toggle_switch(&self, ui: &mut egui::Ui, label: &str, value: &mut bool) {
+ ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
+ ui.add_sized(
+ [Self::box_width(), Self::box_height()],
+ |ui: &mut egui::Ui| {
+ ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| {
+ ui.add(toggle_switch(value));
+ })
+ .response
+ },
+ );
+ ui.label(egui::RichText::new(label).color(self.design_tokens.gray_900));
+ });
+ }
+
pub fn top_panel_frame(&self) -> egui::Frame {
let mut frame = egui::Frame {
inner_margin: Self::top_bar_margin(),
@@ -290,6 +384,9 @@ impl ReUi {
let texture_id = image.texture_id(ui.ctx());
// TODO(emilk): change color and size on hover
let tint = ui.visuals().widgets.inactive.fg_stroke.color;
+ let mut style = ui.style_mut().clone();
+ style.spacing.button_padding = egui::Vec2::new(2.0, 2.0);
+ ui.set_style(style);
ui.add(egui::ImageButton::new(texture_id, size_points).tint(tint))
}
diff --git a/crates/re_ui/src/toggle_switch.rs b/crates/re_ui/src/toggle_switch.rs
index ba52c4dd3e50..3185f49f3082 100644
--- a/crates/re_ui/src/toggle_switch.rs
+++ b/crates/re_ui/src/toggle_switch.rs
@@ -1,39 +1,55 @@
//! Adapted from `egui_demo_lib/src/demo/toggle_switch.rs`
+pub fn toggle_ui(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
+ // Widget code can be broken up in four steps:
+ // 1. Decide a size for the widget
+ // 2. Allocate space for it
+ // 3. Handle interactions with the widget (if any)
+ // 4. Paint the widget
-fn toggle_switch_ui(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
- let interactive_size = egui::vec2(12.0, ui.spacing().interact_size.y);
- let (interact_rect, mut response) =
- ui.allocate_exact_size(interactive_size, egui::Sense::click());
- let visual_size = egui::vec2(12.0, 8.0); // 12x7 in figma, but 12x8 looks _much_ better in epaint
- let visual_rect =
- egui::Align2::CENTER_CENTER.align_size_within_rect(visual_size, interact_rect);
+ // 1. Deciding widget size:
+ // You can query the `ui` how much space is available,
+ // but in this example we have a fixed size widget based on the height of a standard button:
+ let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0, 1.0);
+ // 2. Allocating space:
+ // This is where we get a region of the screen assigned.
+ // We also tell the Ui to sense clicks in the allocated region.
+ let (rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
+
+ // 3. Interact: Time to check for clicks!
if response.clicked() {
*on = !*on;
- response.mark_changed();
+ response.mark_changed(); // report back that the value changed
}
+
+ // Attach some meta-data to the response which can be used by screen readers:
response.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::Checkbox, *on, ""));
- if ui.is_rect_visible(visual_rect) {
+ // 4. Paint!
+ // Make sure we need to paint:
+ if ui.is_rect_visible(rect) {
+ // Let's ask for a simple animation from egui.
+ // egui keeps track of changes in the boolean associated with the id and
+ // returns an animated value in the 0-1 range for how much "on" we are.
let how_on = ui.ctx().animate_bool(response.id, *on);
- let visuals = ui.style().interact(&response);
- let expanded_rect = visual_rect.expand(visuals.expansion);
- let fg_fill = visuals.bg_fill;
- let bg_fill = visuals.text_color();
- let rounding = 0.5 * expanded_rect.height();
+ // We will follow the current style by asking
+ // "how should something that is being interacted with be painted?".
+ // This will, for instance, give us different colors when the widget is hovered or clicked.
+ let visuals = ui.style().interact_selectable(&response, *on);
+ // All coordinates are in absolute screen coordinates so we use `rect` to place the elements.
+ let rect = rect.expand(visuals.expansion);
+ let radius = 0.5 * rect.height();
ui.painter()
- .rect(expanded_rect, rounding, bg_fill, egui::Stroke::NONE);
- let circle_x = egui::lerp(
- (expanded_rect.left() + rounding)..=(expanded_rect.right() - rounding),
- how_on,
- );
-
- let circle_center = egui::pos2(circle_x, expanded_rect.center().y);
- let circle_radius = 2.5 * expanded_rect.height() / visual_size.y;
+ .rect(rect, radius, visuals.bg_fill, visuals.bg_stroke);
+ // Paint the circle, animating it from left to right with `how_on`:
+ let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on);
+ let center = egui::pos2(circle_x, rect.center().y);
ui.painter()
- .circle(circle_center, circle_radius, fg_fill, egui::Stroke::NONE);
+ .circle(center, 0.75 * radius, visuals.bg_fill, visuals.fg_stroke);
}
+ // All done! Return the interaction response so the user can check what happened
+ // (hovered, clicked, ...) and maybe show a tooltip:
response
}
@@ -45,5 +61,5 @@ fn toggle_switch_ui(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
/// ui.add(toggle_switch(&mut my_bool));
/// ```
pub fn toggle_switch(on: &mut bool) -> impl egui::Widget + '_ {
- move |ui: &mut egui::Ui| toggle_switch_ui(ui, on)
+ move |ui: &mut egui::Ui| toggle_ui(ui, on)
}
diff --git a/crates/re_viewer/Cargo.toml b/crates/re_viewer/Cargo.toml
index 8ec7e70ddc1b..645f75e77749 100644
--- a/crates/re_viewer/Cargo.toml
+++ b/crates/re_viewer/Cargo.toml
@@ -17,6 +17,7 @@ include = [
"Cargo.toml",
"data/*",
]
+resolver = "2"
[package.metadata.docs.rs]
all-features = true
@@ -28,14 +29,15 @@ crate-type = ["cdylib", "rlib"]
[features]
-default = ["analytics"]
+default = ["analytics", "webgl"]
## Enable telemetry using our analytics SDK.
analytics = ["dep:re_analytics"]
+## Render using webgl instead of webgpu on wasm builds.
+webgl = ["re_renderer/webgl"]
-[dependencies]
-# Internal:
+[dependencies] # Internal:
re_arrow_store.workspace = true
re_build_info.workspace = true
re_data_store = { workspace = true, features = ["serde"] }
@@ -46,11 +48,21 @@ re_log_types = { workspace = true, features = ["ecolor", "glam", "image"] }
re_log.workspace = true
re_memory.workspace = true
re_query.workspace = true
-re_renderer = { workspace = true, features = ["arrow", "serde"] }
+re_renderer = { workspace = true, default-features = false, features = [
+ "arrow",
+ "import-gltf",
+ "import-obj",
+ "serde",
+] }
re_smart_channel.workspace = true
re_tensor_ops.workspace = true
re_ui.workspace = true
re_ws_comms = { workspace = true, features = ["client"] }
+serde_json = "1"
+tokio = { workspace = true, default-features = false, features = ["rt"] }
+ewebsock = { version = "0.2", optional = false }
+strum = { version = "0.24", features = ["derive"] }
+strum_macros = "0.24"
# Internal (optional):
re_analytics = { workspace = true, optional = true }
@@ -60,15 +72,16 @@ re_analytics = { workspace = true, optional = true }
ahash.workspace = true
anyhow.workspace = true
bytemuck = { version = "1.11", features = ["extern_crate_alloc"] }
-eframe = { workspace = true, default-features = false, features = [
+crossbeam-channel = "0.5.7"
+eframe = { workspace = true, features = [
"default_fonts",
"persistence",
"puffin",
"wgpu",
] }
-egui = { workspace = true, features = ["extra_debug_asserts", "tracing"] }
+egui.workspace = true
egui_dock = { workspace = true, features = ["serde"] }
-egui_extras = { workspace = true, features = ["tracing"] }
+egui_extras.workspace = true
egui-wgpu.workspace = true
enumset.workspace = true
glam = { workspace = true, features = [
@@ -93,16 +106,24 @@ serde = { version = "1", features = ["derive"] }
slotmap = { version = "1.0.6", features = ["serde"] }
smallvec = { workspace = true, features = ["serde"] }
thiserror.workspace = true
-time = { workspace = true, default-features = false, features = ["formatting"] }
+time = { workspace = true, default-features = false, features = [
+ "formatting",
+ "wasm-bindgen",
+] }
uuid = { version = "1.1", features = ["serde", "v4", "js"] }
vec1 = "1.8"
wgpu.workspace = true
+url = "2.3.1"
# native dependencies:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
arboard = { version = "3.2", default-features = false, features = [
"image-data",
] }
+pyo3 = { version = "0.18.0", features = ["auto-initialize"] }
+pyo3-asyncio = { version = "0.18", features = ["attributes", "tokio-runtime"] }
+async-std = "1.9"
+
puffin_http = "0.11"
puffin.workspace = true
diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs
index c374cc5a1aa3..1702312cc594 100644
--- a/crates/re_viewer/src/app.rs
+++ b/crates/re_viewer/src/app.rs
@@ -17,6 +17,7 @@ use re_ui::{toasts, Command};
use crate::{
app_icon::setup_app_icon,
+ depthai::depthai,
misc::{AppOptions, Caches, RecordingConfig, ViewerContext},
ui::{data_ui::ComponentUiRegistry, Blueprint},
viewer_analytics::ViewerAnalytics,
@@ -99,9 +100,47 @@ pub struct App {
analytics: ViewerAnalytics,
icon_status: AppIconStatus,
+
+ #[cfg(not(target_arch = "wasm32"))]
+ backend_handle: Option,
}
impl App {
+ #[cfg(not(target_arch = "wasm32"))]
+ fn spawn_backend() -> Option {
+ // TODO(filip): Is there some way I can know for sure where depthai_viewer_backend is?
+ let backend_handle = match std::process::Command::new("python")
+ .args(["-m", "depthai_viewer._backend.main"])
+ .spawn()
+ {
+ Ok(child) => {
+ println!("Backend started successfully.");
+ Some(child)
+ }
+ Err(err) => {
+ eprintln!("Failed to start depthai viewer: {err}");
+ match std::process::Command::new("python3")
+ .args(["-m", "depthai_viewer._backend.main"])
+ .spawn()
+ {
+ Ok(child) => {
+ println!("Backend started successfully.");
+ Some(child)
+ }
+ Err(err) => {
+ eprintln!("Failed to start depthai_viewer {err}");
+ None
+ }
+ }
+ }
+ };
+ // assert!(
+ // backend_handle.is_some(),
+ // "Couldn't start backend, exiting..."
+ // );
+ backend_handle
+ }
+
/// Create a viewer that receives new log messages over time
pub fn from_receiver(
build_info: re_build_info::BuildInfo,
@@ -157,6 +196,8 @@ impl App {
analytics,
icon_status: AppIconStatus::NotSetTryAgain,
+ #[cfg(not(target_arch = "wasm32"))]
+ backend_handle: App::spawn_backend(),
}
}
@@ -246,8 +287,6 @@ impl App {
}
fn run_command(&mut self, cmd: Command, _frame: &mut eframe::Frame, egui_ctx: &egui::Context) {
- let is_narrow_screen = egui_ctx.screen_rect().width() < 600.0; // responsive ui for mobiles etc
-
match cmd {
#[cfg(not(target_arch = "wasm32"))]
Command::Save => {
@@ -263,6 +302,10 @@ impl App {
}
#[cfg(not(target_arch = "wasm32"))]
Command::Quit => {
+ self.state.depthai_state.shutdown();
+ if let Some(backend_handle) = &mut self.backend_handle {
+ backend_handle.kill();
+ }
_frame.close();
}
@@ -281,20 +324,10 @@ impl App {
Command::ToggleBlueprintPanel => {
let blueprint = self.blueprint_mut(egui_ctx);
blueprint.blueprint_panel_expanded ^= true;
-
- // Only one of blueprint or selection panel can be open at a time on mobile:
- if is_narrow_screen && blueprint.blueprint_panel_expanded {
- blueprint.selection_panel_expanded = false;
- }
}
Command::ToggleSelectionPanel => {
let blueprint = self.blueprint_mut(egui_ctx);
blueprint.selection_panel_expanded ^= true;
-
- // Only one of blueprint or selection panel can be open at a time on mobile:
- if is_narrow_screen && blueprint.selection_panel_expanded {
- blueprint.blueprint_panel_expanded = false;
- }
}
Command::ToggleTimePanel => {
self.blueprint_mut(egui_ctx).time_panel_expanded ^= true;
@@ -425,6 +458,15 @@ impl eframe::App for App {
[0.0; 4] // transparent so we can get rounded corners when doing [`re_ui::CUSTOM_WINDOW_DECORATIONS`]
}
+ #[cfg(not(target_arch = "wasm32"))]
+ fn on_close_event(&mut self) -> bool {
+ self.state.depthai_state.shutdown();
+ if let Some(backend_handle) = &mut self.backend_handle {
+ backend_handle.kill();
+ }
+ true
+ }
+
fn save(&mut self, storage: &mut dyn eframe::Storage) {
if self.startup_options.persist_state {
eframe::set_value(storage, eframe::APP_KEY, &self.state);
@@ -433,6 +475,27 @@ impl eframe::App for App {
fn update(&mut self, egui_ctx: &egui::Context, frame: &mut eframe::Frame) {
let frame_start = Instant::now();
+ self.state.depthai_state.update(); // Always update depthai state
+ #[cfg(not(target_arch = "wasm32"))]
+ {
+ match &mut self.backend_handle {
+ Some(handle) => match handle.try_wait() {
+ Ok(status) => {
+ if status.is_some() {
+ handle.kill();
+ re_log::debug!("Backend process has exited, restarting!");
+ self.backend_handle = App::spawn_backend();
+ }
+ }
+ Err(_) => {}
+ },
+ None => self.backend_handle = App::spawn_backend(),
+ };
+ }
+
+ if self.backend_handle.is_none() {
+ self.backend_handle = App::spawn_backend();
+ };
if self.startup_options.memory_limit.limit.is_none() {
// we only warn about high memory usage if the user hasn't specified a limit
@@ -444,8 +507,14 @@ impl eframe::App for App {
}
if self.shutdown.load(std::sync::atomic::Ordering::Relaxed) {
+ self.state.depthai_state.shutdown();
#[cfg(not(target_arch = "wasm32"))]
- frame.close();
+ {
+ if let Some(backend_handle) = &mut self.backend_handle {
+ backend_handle.kill();
+ }
+ frame.close();
+ }
return;
}
@@ -548,18 +617,14 @@ impl eframe::App for App {
.unwrap();
render_ctx.begin_frame();
- if log_db.is_default() {
- wait_screen_ui(ui, &self.rx);
- } else {
- self.state.show(
- ui,
- render_ctx,
- log_db,
- &self.re_ui,
- &self.component_ui_registry,
- self.rx.source(),
- );
- }
+ self.state.show(
+ ui,
+ render_ctx,
+ log_db,
+ &self.re_ui,
+ &self.component_ui_registry,
+ self.rx.source(),
+ );
render_ctx.before_submit();
}
@@ -583,6 +648,7 @@ impl eframe::App for App {
egui_ctx.input(|i| i.time),
frame_start.elapsed().as_secs_f32(),
);
+ egui_ctx.request_repaint(); // Force repaint even when out of focus
}
}
@@ -943,14 +1009,19 @@ struct AppState {
/// Configuration for the current recording (found in [`LogDb`]).
recording_configs: IntMap,
+ #[serde(skip)] // Quick fix for subscriptions setting, just don't remembet space views
blueprints: HashMap,
/// Which view panel is currently being shown
panel_selection: PanelSelection,
selection_panel: crate::selection_panel::SelectionPanel,
+
time_panel: crate::time_panel::TimePanel,
+ selected_device: depthai::DeviceId,
+ depthai_state: depthai::State,
+
#[cfg(not(target_arch = "wasm32"))]
#[serde(skip)]
profiler: crate::Profiler,
@@ -976,8 +1047,10 @@ impl AppState {
recording_configs,
panel_selection,
blueprints,
- selection_panel,
- time_panel,
+ selection_panel: _,
+ time_panel: _,
+ selected_device: _,
+ depthai_state,
#[cfg(not(target_arch = "wasm32"))]
profiler: _,
} = self;
@@ -998,13 +1071,11 @@ impl AppState {
rec_cfg,
re_ui,
render_ctx,
+ depthai_state,
};
- let blueprint = blueprints
- .entry(selected_app_id.clone())
- .or_insert_with(|| Blueprint::new(ui.ctx()));
- time_panel.show_panel(&mut ctx, blueprint, ui);
- selection_panel.show_panel(&mut ctx, ui, blueprint);
+ // Hide time panel for now, reuse for recordings in the future
+ // time_panel.show_panel(&mut ctx, blueprint, ui);
let central_panel_frame = egui::Frame {
fill: ui.style().visuals.panel_fill,
@@ -1345,7 +1416,7 @@ fn frame_time_label_ui(ui: &mut egui::Ui, app: &mut App) {
// we use monospace so the width doesn't fluctuate as the numbers change.
let text = format!("{ms:.1} ms");
ui.label(egui::RichText::new(text).monospace().color(color))
- .on_hover_text("CPU time used by Rerun Viewer each frame. Lower is better.");
+ .on_hover_text("CPU time used by Depthai Viewer each frame. Lower is better.");
}
}
@@ -1360,7 +1431,7 @@ fn memory_use_label_ui(ui: &mut egui::Ui, gpu_resource_stats: &WgpuResourcePoolS
.color(ui.visuals().weak_text_color()),
)
.on_hover_text(format!(
- "Rerun Viewer is using {} of RAM in {} separate allocations,\n\
+ "Depthai Viewer is using {} of RAM in {} separate allocations,\n\
plus {} of GPU memory in {} textures and {} buffers.",
bytes_used_text,
format_number(count.count),
@@ -1395,7 +1466,7 @@ fn input_latency_label_ui(ui: &mut egui::Ui, app: &mut App) {
format_number(queue_len),
);
let hover_text =
- "When more data is arriving over network than the Rerun Viewer can index, a queue starts building up, leading to latency and increased RAM use.\n\
+ "When more data is arriving over network than the Depthai Viewer can index, a queue starts building up, leading to latency and increased RAM use.\n\
This latency does NOT include network latency.";
if latency_sec < app.state.app_options.warn_latency {
diff --git a/crates/re_viewer/src/depthai/api.rs b/crates/re_viewer/src/depthai/api.rs
new file mode 100644
index 000000000000..cea17653b1a8
--- /dev/null
+++ b/crates/re_viewer/src/depthai/api.rs
@@ -0,0 +1,81 @@
+use super::depthai;
+use super::ws::{BackWsMessage as WsMessage, WebSocket, WsMessageData, WsMessageType};
+
+#[derive(Clone, serde::Serialize, serde::Deserialize)]
+pub struct ApiError {
+ pub detail: String,
+}
+
+impl Default for ApiError {
+ fn default() -> Self {
+ Self {
+ detail: "ApiError".to_string(),
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct BackendCommChannel {
+ pub ws: WebSocket,
+}
+
+impl BackendCommChannel {
+ pub fn shutdown(&mut self) {
+ self.ws.shutdown();
+ }
+
+ pub fn set_subscriptions(&mut self, subscriptions: &Vec) {
+ self.ws.send(
+ serde_json::to_string(
+ &(WsMessage {
+ kind: WsMessageType::Subscriptions,
+ data: WsMessageData::Subscriptions(subscriptions.clone()),
+ }),
+ )
+ .unwrap(),
+ );
+ }
+
+ pub fn set_pipeline(&mut self, config: &depthai::DeviceConfig, runtime_only: bool) {
+ self.ws.send(
+ serde_json::to_string(
+ &(WsMessage {
+ kind: WsMessageType::Pipeline,
+ data: WsMessageData::Pipeline((config.clone(), runtime_only)),
+ }),
+ )
+ .unwrap(),
+ );
+ }
+
+ pub fn receive(&mut self) -> Option {
+ self.ws.receive()
+ }
+
+ pub fn get_devices(&mut self) {
+ self.ws.send(
+ serde_json::to_string(
+ &(WsMessage {
+ kind: WsMessageType::Devices,
+ data: WsMessageData::Devices(Vec::new()),
+ }),
+ )
+ .unwrap(),
+ );
+ }
+
+ pub fn set_device(&mut self, device_id: depthai::DeviceId) {
+ self.ws.send(
+ serde_json::to_string(
+ &(WsMessage {
+ kind: WsMessageType::Device,
+ data: WsMessageData::Device(depthai::Device {
+ id: device_id,
+ ..Default::default()
+ }),
+ }),
+ )
+ .unwrap(),
+ );
+ }
+}
diff --git a/crates/re_viewer/src/depthai/depthai.rs b/crates/re_viewer/src/depthai/depthai.rs
new file mode 100644
index 000000000000..8323ffc873e1
--- /dev/null
+++ b/crates/re_viewer/src/depthai/depthai.rs
@@ -0,0 +1,822 @@
+use itertools::Itertools;
+use re_log_types::EntityPath;
+
+use super::api::BackendCommChannel;
+use super::ws::WsMessageData;
+use instant::Instant;
+use std::fmt;
+
+use strum::EnumIter;
+use strum::IntoEnumIterator;
+
+#[derive(serde::Deserialize, serde::Serialize, fmt::Debug, PartialEq, Clone, Copy, EnumIter)]
+#[allow(non_camel_case_types)]
+pub enum ColorCameraResolution {
+ THE_720_P,
+ THE_800_P,
+ THE_1440X1080,
+ THE_1080_P,
+ THE_1200_P,
+ THE_5_MP,
+ THE_4_K,
+ THE_12_MP,
+ THE_4000X3000,
+ THE_13_MP,
+ THE_48_MP,
+}
+
+#[derive(serde::Deserialize, serde::Serialize, fmt::Debug, PartialEq, Clone, Copy, EnumIter)]
+#[allow(non_camel_case_types)]
+pub enum MonoCameraResolution {
+ THE_400_P,
+ THE_480_P,
+ THE_720_P,
+ THE_800_P,
+ THE_1200_P,
+}
+
+// fmt::Display is used in UI while fmt::Debug is used with the depthai backend api
+impl fmt::Display for ColorCameraResolution {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::THE_1080_P => write!(f, "1080p"),
+ Self::THE_4_K => write!(f, "4k"),
+ Self::THE_720_P => write!(f, "720p"),
+ Self::THE_800_P => write!(f, "800p"),
+ Self::THE_1200_P => write!(f, "1200p"),
+ Self::THE_5_MP => write!(f, "5MP"),
+ Self::THE_12_MP => write!(f, "12MP"),
+ Self::THE_13_MP => write!(f, "13MP"),
+ Self::THE_4000X3000 => write!(f, "4000x3000"),
+ Self::THE_48_MP => write!(f, "48MP"),
+ Self::THE_1440X1080 => write!(f, "1440x1080"),
+ }
+ }
+}
+
+impl fmt::Display for MonoCameraResolution {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::THE_400_P => write!(f, "400p"),
+ Self::THE_480_P => write!(f, "480p"),
+ Self::THE_720_P => write!(f, "720p"),
+ Self::THE_800_P => write!(f, "800p"),
+ Self::THE_1200_P => write!(f, "1200p"),
+ }
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
+pub struct ColorCameraConfig {
+ pub fps: u8,
+ pub resolution: ColorCameraResolution,
+ #[serde(rename = "xout_video")]
+ pub stream_enabled: bool,
+}
+
+impl Default for ColorCameraConfig {
+ fn default() -> Self {
+ Self {
+ fps: 30,
+ resolution: ColorCameraResolution::THE_1080_P,
+ stream_enabled: true,
+ }
+ }
+}
+
+impl fmt::Debug for ColorCameraConfig {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "Color camera config: fps: {}, resolution: {:?}",
+ self.fps, self.resolution
+ )
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq, EnumIter, Debug)]
+#[allow(non_camel_case_types)]
+pub enum CameraBoardSocket {
+ AUTO,
+ RGB,
+ LEFT,
+ RIGHT,
+ CENTER,
+ CAM_A,
+ CAM_B,
+ CAM_C,
+ CAM_D,
+ CAM_E,
+ CAM_F,
+ CAM_G,
+ CAM_H,
+}
+
+impl Default for CameraBoardSocket {
+ fn default() -> Self {
+ Self::AUTO
+ }
+}
+
+impl CameraBoardSocket {
+ pub fn depth_align_options() -> Vec {
+ return vec![Self::RGB, Self::LEFT, Self::RIGHT];
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
+pub struct MonoCameraConfig {
+ pub fps: u8,
+ pub resolution: MonoCameraResolution,
+ pub board_socket: CameraBoardSocket,
+ #[serde(rename = "xout")]
+ pub stream_enabled: bool,
+}
+
+impl Default for MonoCameraConfig {
+ fn default() -> Self {
+ Self {
+ fps: 30,
+ resolution: MonoCameraResolution::THE_800_P,
+ board_socket: CameraBoardSocket::AUTO,
+ stream_enabled: false,
+ }
+ }
+}
+
+impl fmt::Debug for MonoCameraConfig {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "Mono camera config: fps: {}, resolution: {:?}",
+ self.fps, self.resolution
+ )
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)]
+#[allow(non_camel_case_types)]
+pub enum DepthProfilePreset {
+ HIGH_DENSITY,
+ HIGH_ACCURACY,
+}
+
+impl Default for DepthProfilePreset {
+ fn default() -> Self {
+ Self::HIGH_DENSITY
+ }
+}
+
+impl fmt::Display for DepthProfilePreset {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::HIGH_DENSITY => write!(f, "High Density"),
+ Self::HIGH_ACCURACY => write!(f, "High Accuracy"),
+ }
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq, Debug, EnumIter)]
+#[allow(non_camel_case_types)]
+pub enum DepthMedianFilter {
+ MEDIAN_OFF,
+ KERNEL_3x3,
+ KERNEL_5x5,
+ KERNEL_7x7,
+}
+
+impl Default for DepthMedianFilter {
+ fn default() -> Self {
+ Self::KERNEL_7x7
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq, Debug)]
+pub struct DepthConfig {
+ pub median: DepthMedianFilter,
+ pub lr_check: bool,
+ pub lrc_threshold: u64,
+ pub extended_disparity: bool,
+ pub subpixel_disparity: bool,
+ pub sigma: i64,
+ pub confidence: i64,
+ pub align: CameraBoardSocket,
+}
+
+impl Default for DepthConfig {
+ fn default() -> Self {
+ Self {
+ median: DepthMedianFilter::default(),
+ lr_check: true,
+ lrc_threshold: 5,
+ extended_disparity: false,
+ subpixel_disparity: true,
+ sigma: 0,
+ confidence: 230,
+ align: CameraBoardSocket::RGB,
+ }
+ }
+}
+
+impl DepthConfig {
+ pub fn default_as_option() -> Option {
+ Some(Self::default())
+ }
+
+ pub fn only_runtime_configs_differ(&self, other: &DepthConfig) -> bool {
+ self.lr_check == other.lr_check
+ && self.align == other.align
+ && self.extended_disparity == other.extended_disparity
+ && self.subpixel_disparity == other.subpixel_disparity
+ && self != other
+ }
+}
+
+#[derive(Default, serde::Deserialize, serde::Serialize, Clone)]
+pub struct DeviceConfig {
+ pub color_camera: ColorCameraConfig,
+ pub left_camera: MonoCameraConfig,
+ pub right_camera: MonoCameraConfig,
+ #[serde(default = "bool_true")]
+ pub depth_enabled: bool, // Much easier to have an explicit bool for checkbox
+ #[serde(default = "DepthConfig::default_as_option")]
+ pub depth: Option,
+ pub ai_model: AiModel,
+}
+
+#[derive(serde::Serialize, serde::Deserialize)]
+#[allow(non_camel_case_types)]
+pub enum CameraSensorType {
+ COLOR,
+ MONO,
+ THERMAL,
+ TOF,
+}
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub struct CameraSensorConfig {
+ height: i64,
+ #[serde(rename = "maxFps")]
+ max_fps: i64,
+ #[serde(rename = "minFps")]
+ min_fps: i64,
+ #[serde(rename = "type")]
+ kind: CameraSensorType,
+ width: i64,
+}
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub struct CameraFeatures {
+ configs: Vec,
+ #[serde(rename = "hasAutofocus")]
+ has_autofocus: bool,
+ height: i64,
+ name: String,
+ orientation: CameraImageOrientation,
+ #[serde(rename = "sensorName")]
+ sensor_name: String,
+ socket: CameraBoardSocket,
+ #[serde(rename = "supportedTypes")]
+ supported_types: Vec,
+ width: i64,
+}
+
+#[derive(serde::Serialize, serde::Deserialize)]
+#[allow(non_camel_case_types)]
+pub enum CameraImageOrientation {
+ AUTO,
+ HORIZONTAL_MIRROR,
+ NORMAL,
+ ROTATE_180_DEG,
+ VERTICAL_FLIP,
+}
+
+impl PartialEq for DeviceConfig {
+ fn eq(&self, other: &Self) -> bool {
+ let depth_eq = match (&self.depth, &other.depth) {
+ (Some(a), Some(b)) => a == b,
+ _ => true, // If one is None, it's only different if depth_enabled is different
+ };
+ self.color_camera == other.color_camera
+ && self.left_camera == other.left_camera
+ && self.right_camera == other.right_camera
+ && depth_eq
+ && self.depth_enabled == other.depth_enabled
+ && self.ai_model == other.ai_model
+ }
+}
+
+#[inline]
+const fn bool_true() -> bool {
+ true
+}
+
+#[derive(Default, serde::Deserialize, serde::Serialize)]
+pub struct DeviceConfigState {
+ pub config: DeviceConfig,
+ #[serde(skip)]
+ pub update_in_progress: bool,
+}
+
+impl fmt::Debug for DeviceConfig {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "Device config: {:?} {:?} {:?}, depth: {:?}, ai_model: {:?}, depth_enabled: {:?}",
+ self.color_camera,
+ self.left_camera,
+ self.right_camera,
+ self.depth,
+ self.ai_model,
+ self.depth_enabled
+ )
+ }
+}
+
+#[derive(serde::Deserialize)]
+struct PipelineResponse {
+ message: String,
+}
+
+impl Default for PipelineResponse {
+ fn default() -> Self {
+ Self {
+ message: "Pipeline not started".to_string(),
+ }
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq, fmt::Debug)]
+pub enum ErrorAction {
+ None,
+ FullReset,
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, PartialEq, fmt::Debug)]
+pub struct Error {
+ pub action: ErrorAction,
+ pub message: String,
+}
+
+impl Default for Error {
+ fn default() -> Self {
+ Self {
+ action: ErrorAction::None,
+ message: String::from("Invalid message"),
+ }
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, PartialEq, fmt::Debug)]
+pub struct Device {
+ pub id: DeviceId,
+ pub supported_color_resolutions: Vec,
+ pub supported_left_mono_resolutions: Vec,
+ pub supported_right_mono_resolutions: Vec,
+}
+
+// Sensible default for when no device is connected
+impl Default for Device {
+ fn default() -> Self {
+ Self {
+ id: DeviceId::default(),
+ supported_color_resolutions: vec![ColorCameraResolution::THE_1080_P],
+ supported_left_mono_resolutions: vec![MonoCameraResolution::THE_400_P],
+ supported_right_mono_resolutions: vec![MonoCameraResolution::THE_400_P],
+ }
+ }
+}
+
+#[derive(serde::Deserialize, serde::Serialize, Clone, fmt::Debug)]
+pub struct AiModel {
+ pub path: String,
+ pub display_name: String,
+}
+
+impl Default for AiModel {
+ fn default() -> Self {
+ Self {
+ path: String::new(),
+ display_name: String::from("No model selected"),
+ }
+ }
+}
+
+impl PartialEq for AiModel {
+ fn eq(&self, other: &Self) -> bool {
+ self.path == other.path
+ }
+}
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub struct State {
+ #[serde(skip)]
+ devices_available: Option>,
+ #[serde(skip)]
+ pub selected_device: Device,
+ pub applied_device_config: DeviceConfigState,
+ pub modified_device_config: DeviceConfigState,
+ #[serde(skip, default = "all_subscriptions")]
+ // Want to resubscribe to api when app is reloaded
+ pub subscriptions: Vec, // Shown in ui
+ #[serde(skip)]
+ setting_subscriptions: bool,
+ #[serde(skip)]
+ pub backend_comms: BackendCommChannel,
+ #[serde(skip)]
+ poll_instant: Option,
+ #[serde(default = "default_neural_networks")]
+ pub neural_networks: Vec,
+}
+
+#[inline]
+fn all_subscriptions() -> Vec {
+ ChannelId::iter().collect_vec()
+}
+
+#[inline]
+fn default_neural_networks() -> Vec {
+ vec![
+ AiModel::default(),
+ AiModel {
+ path: String::from("yolov8n_coco_640x352"),
+ display_name: String::from("Yolo V8"),
+ },
+ AiModel {
+ path: String::from("mobilenet-ssd"),
+ display_name: String::from("MobileNet SSD"),
+ },
+ AiModel {
+ path: String::from("face-detection-retail-0004"),
+ display_name: String::from("Face Detection"),
+ },
+ AiModel {
+ path: String::from("age-gender-recognition-retail-0013"),
+ display_name: String::from("Age gender recognition"),
+ },
+ ]
+}
+
+impl Default for State {
+ fn default() -> Self {
+ Self {
+ devices_available: None,
+ selected_device: Device::default(),
+ applied_device_config: DeviceConfigState::default(),
+ modified_device_config: DeviceConfigState::default(),
+ subscriptions: ChannelId::iter().collect(),
+ setting_subscriptions: false,
+ backend_comms: BackendCommChannel::default(),
+ poll_instant: Some(Instant::now()), // No default for Instant
+ neural_networks: default_neural_networks(),
+ }
+ }
+}
+
+#[repr(u8)]
+#[derive(
+ serde::Serialize, serde::Deserialize, Copy, Clone, PartialEq, Eq, fmt::Debug, Hash, EnumIter,
+)]
+pub enum ChannelId {
+ ColorImage,
+ LeftMono,
+ RightMono,
+ DepthImage,
+ PinholeCamera,
+ ImuData,
+}
+
+/// Entity paths for depthai-viewer space views
+/// !---- These have to match with EntityPath in rerun_py/rerun_sdk/depthai_viewer_backend/sdk_callbacks.py ----!
+pub mod entity_paths {
+ use lazy_static::lazy_static;
+ use re_log_types::EntityPath;
+
+ lazy_static! {
+ pub static ref RGB_PINHOLE_CAMERA: EntityPath = EntityPath::from("color/camera/rgb");
+ pub static ref LEFT_PINHOLE_CAMERA: EntityPath = EntityPath::from("mono/camera/left_mono");
+ pub static ref LEFT_CAMERA_IMAGE: EntityPath =
+ EntityPath::from("mono/camera/left_mono/Left mono");
+ pub static ref RIGHT_PINHOLE_CAMERA: EntityPath =
+ EntityPath::from("mono/camera/right_mono");
+ pub static ref RIGHT_CAMERA_IMAGE: EntityPath =
+ EntityPath::from("mono/camera/right_mono/Right mono");
+ pub static ref RGB_CAMERA_IMAGE: EntityPath =
+ EntityPath::from("color/camera/rgb/Color camera");
+ pub static ref DETECTIONS: EntityPath = EntityPath::from("color/camera/rgb/Detections");
+ pub static ref DETECTION: EntityPath = EntityPath::from("color/camera/rgb/Detection");
+ pub static ref RGB_CAMERA_TRANSFORM: EntityPath = EntityPath::from("color/camera");
+ pub static ref MONO_CAMERA_TRANSFORM: EntityPath = EntityPath::from("mono/camera");
+
+ // --- These are extra for the depthai-viewer ---
+ pub static ref COLOR_CAM_3D: EntityPath = EntityPath::from("color");
+ pub static ref MONO_CAM_3D: EntityPath = EntityPath::from("mono");
+ pub static ref DEPTH_RGB: EntityPath = EntityPath::from("color/camera/rgb/Depth");
+ pub static ref DEPTH_LEFT_MONO: EntityPath =
+ EntityPath::from("mono/camera/left_mono/Depth");
+ pub static ref DEPTH_RIGHT_MONO: EntityPath =
+ EntityPath::from("mono/camera/right_mono/Depth");
+ }
+}
+
+impl State {
+ pub fn only_runtime_configs_changed(
+ old_config: &DeviceConfig,
+ new_config: &DeviceConfig,
+ ) -> bool {
+ let any_runtime_conf_changed = old_config.depth.is_some()
+ && new_config.depth.is_some()
+ && old_config
+ .depth
+ .unwrap()
+ .only_runtime_configs_differ(&new_config.depth.unwrap()); // || others to be added
+ any_runtime_conf_changed
+ && old_config.left_camera == new_config.left_camera
+ && old_config.right_camera == new_config.right_camera
+ && old_config.color_camera == new_config.color_camera
+ && old_config.ai_model == new_config.ai_model
+ }
+
+ /// Should the space view be added to the UI based on the new subscriptions (a subscription change occurred)
+ fn create_entity_paths_from_subscriptions(
+ &mut self,
+ new_subscriptions: &Vec,
+ ) -> Vec {
+ let mut new_entity_paths = Vec::new();
+ for channel in new_subscriptions.iter() {
+ match channel {
+ ChannelId::ColorImage => {
+ new_entity_paths.push(EntityPath::from("color/camera/rgb/Color camera"));
+ }
+ ChannelId::LeftMono => {
+ new_entity_paths.push(EntityPath::from("mono/camera/left_mono/Left mono"));
+ }
+ ChannelId::RightMono => {
+ new_entity_paths.push(EntityPath::from("mono/camera/right_mono/Right mono"));
+ }
+ ChannelId::DepthImage => {
+ new_entity_paths.push(EntityPath::from("color/camera/rgb/Depth"));
+ new_entity_paths.push(EntityPath::from("mono/camera/right_mono/Depth"));
+ new_entity_paths.push(EntityPath::from("mono/camera/left_mono/Depth"));
+ }
+ _ => {}
+ }
+ }
+ new_entity_paths
+ }
+
+ /// Get the entities that should be removed based on UI (e.g. if depth is disabled, remove the depth image)
+ pub fn get_entities_to_remove(&mut self) -> Vec {
+ let mut remove_entities = Vec::new();
+ if self.applied_device_config.config.depth.is_none() {
+ remove_entities.push(entity_paths::DEPTH_LEFT_MONO.clone());
+ remove_entities.push(entity_paths::DEPTH_RIGHT_MONO.clone());
+ remove_entities.push(entity_paths::DEPTH_RGB.clone());
+ }
+ if !self
+ .applied_device_config
+ .config
+ .right_camera
+ .stream_enabled
+ {
+ remove_entities.push(entity_paths::RIGHT_PINHOLE_CAMERA.clone());
+ remove_entities.push(entity_paths::RIGHT_CAMERA_IMAGE.clone());
+ // Both cams disabled -> remove 3D view
+ if !self.applied_device_config.config.left_camera.stream_enabled {
+ remove_entities.push(entity_paths::MONO_CAM_3D.clone());
+ remove_entities.push(entity_paths::MONO_CAMERA_TRANSFORM.clone());
+ }
+ }
+ if !self.applied_device_config.config.left_camera.stream_enabled {
+ remove_entities.push(entity_paths::LEFT_PINHOLE_CAMERA.clone());
+ remove_entities.push(entity_paths::LEFT_CAMERA_IMAGE.clone());
+ }
+ if !self
+ .applied_device_config
+ .config
+ .color_camera
+ .stream_enabled
+ {
+ remove_entities.push(entity_paths::RGB_PINHOLE_CAMERA.clone());
+ remove_entities.push(entity_paths::RGB_CAMERA_IMAGE.clone());
+ remove_entities.push(entity_paths::COLOR_CAM_3D.clone());
+ remove_entities.push(entity_paths::RGB_CAMERA_TRANSFORM.clone());
+ }
+ if self.applied_device_config.config.ai_model.path.is_empty() {
+ remove_entities.push(entity_paths::DETECTIONS.clone());
+ remove_entities.push(entity_paths::DETECTION.clone());
+ }
+ remove_entities
+ }
+
+ /// DEPRECATED! Just keep it in the code as a reminder of how to do it
+ /// Probably won't be needed when we make the move away from log_db in the future, will likely be implemented (much) differently.
+ /// Until then we just loose a bit of performance if a user isn't viewing all of the channels
+ /// Set subscriptions based on the visible UI
+ // pub fn set_subscriptions_from_space_views(&mut self, visible_space_views: Vec<&SpaceView>) {
+ // // If any bool in the vec is true, the channel is currently visible in the ui somewhere
+ // let mut visibilities = HashMap::>::from([
+ // (ChannelId::ColorImage, Vec::new()),
+ // (ChannelId::LeftMono, Vec::new()),
+ // (ChannelId::RightMono, Vec::new()),
+ // (ChannelId::DepthImage, Vec::new()),
+ // ]);
+ // // Fill in visibilities
+ // for space_view in visible_space_views.iter() {
+ // let property_map = space_view.data_blueprint.data_blueprints_projected();
+ // for entity_path in space_view.data_blueprint.entity_paths().iter() {
+ // if let Some(channel_id) = DEPTHAI_ENTITY_HASHES.get(&entity_path.hash()) {
+ // if let Some(visibility) = visibilities.get_mut(channel_id) {
+ // visibility.push(property_map.get(entity_path).visible);
+ // }
+ // }
+ // }
+ // }
+
+ // // First add subscriptions that don't have explicit enable disable buttons in the ui
+ // let mut possible_subscriptions = Vec::::from([ChannelId::ImuData]);
+ // // Now add subscriptions that should be possible in terms of ui
+ // if self.applied_device_config.config.depth_enabled {
+ // possible_subscriptions.push(ChannelId::DepthImage);
+ // }
+ // if self
+ // .applied_device_config
+ // .config
+ // .color_camera
+ // .stream_enabled
+ // {
+ // possible_subscriptions.push(ChannelId::ColorImage);
+ // }
+
+ // if self.applied_device_config.config.left_camera.stream_enabled {
+ // possible_subscriptions.push(ChannelId::LeftMono);
+ // }
+ // if self
+ // .applied_device_config
+ // .config
+ // .right_camera
+ // .stream_enabled
+ // {
+ // possible_subscriptions.push(ChannelId::RightMono);
+ // }
+
+ // // Filter visibilities, include those that are currently visible and also possible (example pointcloud enabled == pointcloud possible)
+ // let mut subscriptions = visibilities
+ // .iter()
+ // .filter_map(|(channel, vis)| {
+ // if vis.iter().any(|x| *x) {
+ // if possible_subscriptions.contains(channel) {
+ // return Some(*channel);
+ // }
+ // }
+ // None
+ // })
+ // .collect_vec();
+
+ // // Keep subscriptions that should be visible but have not yet been sent by the backend
+ // for channel in ChannelId::iter() {
+ // if !subscriptions.contains(&channel)
+ // && possible_subscriptions.contains(&channel)
+ // && self.subscriptions.contains(&channel)
+ // {
+ // subscriptions.push(channel);
+ // }
+ // }
+
+ // self.set_subscriptions(&subscriptions);
+ // }
+
+ pub fn set_subscriptions(&mut self, subscriptions: &Vec) {
+ if self.subscriptions.len() == subscriptions.len()
+ && self
+ .subscriptions
+ .iter()
+ .all(|channel_id| subscriptions.contains(channel_id))
+ {
+ return;
+ }
+ self.backend_comms.set_subscriptions(subscriptions);
+ self.subscriptions = subscriptions.clone();
+ }
+
+ pub fn get_devices(&mut self) -> Vec {
+ // Return stored available devices or fetch them from the api (they get fetched every 30s via poller)
+ if let Some(devices) = self.devices_available.clone() {
+ return devices;
+ }
+ Vec::new()
+ }
+
+ pub fn shutdown(&mut self) {
+ self.backend_comms.shutdown();
+ }
+
+ pub fn update(&mut self) {
+ if let Some(ws_message) = self.backend_comms.receive() {
+ re_log::debug!("Received message: {:?}", ws_message);
+ match ws_message.data {
+ WsMessageData::Subscriptions(subscriptions) => {
+ re_log::debug!("Setting subscriptions");
+ self.subscriptions = subscriptions;
+ }
+ WsMessageData::Devices(devices) => {
+ re_log::debug!("Setting devices...");
+ self.devices_available = Some(devices);
+ }
+ WsMessageData::Pipeline((config, _)) => {
+ let mut subs = self.subscriptions.clone();
+ if config.depth.is_some() {
+ subs.push(ChannelId::DepthImage);
+ }
+ if config.color_camera.stream_enabled {
+ subs.push(ChannelId::ColorImage);
+ }
+ if config.left_camera.stream_enabled {
+ subs.push(ChannelId::LeftMono);
+ }
+ if config.right_camera.stream_enabled {
+ subs.push(ChannelId::RightMono);
+ }
+ self.applied_device_config.config = config.clone();
+ self.modified_device_config.config = config;
+ self.applied_device_config.config.depth_enabled =
+ self.applied_device_config.config.depth.is_some();
+ self.modified_device_config.config.depth_enabled =
+ self.modified_device_config.config.depth.is_some();
+ self.set_subscriptions(&subs);
+ self.applied_device_config.update_in_progress = false;
+ }
+ WsMessageData::Device(device) => {
+ re_log::debug!("Setting device: {device:?}");
+ self.selected_device = device;
+ self.backend_comms.set_subscriptions(&self.subscriptions);
+ self.backend_comms
+ .set_pipeline(&self.applied_device_config.config, false);
+ self.applied_device_config.update_in_progress = true;
+ }
+ WsMessageData::Error(error) => {
+ re_log::error!("Error: {:}", error.message);
+ self.applied_device_config.update_in_progress = false;
+ match error.action {
+ ErrorAction::None => (),
+ ErrorAction::FullReset => {
+ self.set_device(String::new());
+ }
+ }
+ }
+ }
+ }
+
+ if let Some(poll_instant) = self.poll_instant {
+ if poll_instant.elapsed().as_secs() < 2 {
+ return;
+ }
+ if self.selected_device.id.is_empty() {
+ self.backend_comms.get_devices();
+ }
+ self.poll_instant = Some(Instant::now());
+ } else {
+ self.poll_instant = Some(Instant::now());
+ }
+ }
+
+ pub fn set_device(&mut self, device_id: DeviceId) {
+ if self.selected_device.id == device_id {
+ return;
+ }
+ re_log::debug!("Setting device: {:?}", device_id);
+ self.backend_comms.set_device(device_id);
+ self.applied_device_config.update_in_progress = true;
+ }
+
+ pub fn set_device_config(&mut self, config: &mut DeviceConfig, runtime_only: bool) {
+ // Don't try to set pipeline in ws not connected
+ if !self
+ .backend_comms
+ .ws
+ .connected
+ .load(std::sync::atomic::Ordering::SeqCst)
+ {
+ return;
+ }
+ config.left_camera.board_socket = CameraBoardSocket::LEFT;
+ config.right_camera.board_socket = CameraBoardSocket::RIGHT;
+ if !config.depth_enabled {
+ config.depth = None;
+ }
+ if self.selected_device.id.is_empty() {
+ self.applied_device_config.config = config.clone();
+ return;
+ }
+ self.backend_comms.set_pipeline(config, runtime_only);
+ if runtime_only {
+ self.applied_device_config.config = config.clone();
+ self.applied_device_config.update_in_progress = false;
+ } else {
+ re_log::info!("Creating pipeline...");
+ self.applied_device_config.update_in_progress = true;
+ }
+ }
+}
+
+pub type DeviceId = String;
diff --git a/crates/re_viewer/src/depthai/mod.rs b/crates/re_viewer/src/depthai/mod.rs
new file mode 100644
index 000000000000..e20c7a80a1dc
--- /dev/null
+++ b/crates/re_viewer/src/depthai/mod.rs
@@ -0,0 +1,3 @@
+mod api;
+pub mod depthai;
+mod ws;
diff --git a/crates/re_viewer/src/depthai/ws.rs b/crates/re_viewer/src/depthai/ws.rs
new file mode 100644
index 000000000000..c6570deba36e
--- /dev/null
+++ b/crates/re_viewer/src/depthai/ws.rs
@@ -0,0 +1,247 @@
+use crossbeam_channel;
+use ewebsock::{WsEvent, WsMessage};
+use serde::{Deserialize, Serialize};
+use std::fmt;
+use std::ops::ControlFlow;
+use std::process::exit;
+use std::sync::atomic::AtomicBool;
+use std::sync::Arc;
+
+use super::depthai;
+
+async fn spawn_ws_client(
+ recv_tx: crossbeam_channel::Sender,
+ send_rx: crossbeam_channel::Receiver,
+ shutdown: Arc,
+ connected: Arc,
+) {
+ let (error_tx, error_rx) = crossbeam_channel::unbounded();
+ // Retry connection until successful
+ loop {
+ let recv_tx = recv_tx.clone();
+ let error_tx = error_tx.clone();
+ let connected = connected.clone();
+ if let Ok(sender) = ewebsock::ws_connect(
+ String::from("ws://localhost:9001"),
+ Box::new(move |event| {
+ match event {
+ WsEvent::Opened => {
+ re_log::info!("Websocket opened");
+ connected.store(true, std::sync::atomic::Ordering::SeqCst);
+ ControlFlow::Continue(())
+ }
+ WsEvent::Message(message) => {
+ // re_log::debug!("Websocket message");
+ recv_tx.send(message);
+ ControlFlow::Continue(())
+ }
+ WsEvent::Error(e) => {
+ // re_log::info!("Websocket Error: {:?}", e);
+ connected.store(false, std::sync::atomic::Ordering::SeqCst);
+ error_tx.send(e);
+ ControlFlow::Break(())
+ }
+ WsEvent::Closed => {
+ // re_log::info!("Websocket Closed");
+ error_tx.send(String::from("Websocket Closed"));
+ ControlFlow::Break(())
+ }
+ }
+ }),
+ )
+ .as_mut()
+ {
+ while error_rx.is_empty() {
+ if shutdown.load(std::sync::atomic::Ordering::SeqCst) {
+ re_log::debug!("Shutting down websocket client");
+ exit(0);
+ }
+ if let Ok(message) = send_rx.recv_timeout(std::time::Duration::from_millis(100)) {
+ re_log::debug!("Sending message: {:?}", message);
+ sender.send(message);
+ }
+ }
+ for error in error_rx.try_iter() {
+ re_log::debug!("Websocket error: {:}", error);
+ }
+ } else {
+ re_log::error!("Coudln't create websocket");
+ }
+ if shutdown.load(std::sync::atomic::Ordering::SeqCst) {
+ re_log::debug!("Shutting down websocket client");
+ exit(0);
+ }
+ std::thread::sleep(std::time::Duration::from_secs(1));
+ }
+}
+
+type RuntimeOnly = bool;
+
+#[derive(Serialize, Deserialize, fmt::Debug)]
+pub enum WsMessageData {
+ Subscriptions(Vec),
+ Devices(Vec),
+ Device(depthai::Device),
+ Pipeline((depthai::DeviceConfig, RuntimeOnly)),
+ Error(depthai::Error),
+}
+
+#[derive(Deserialize, Serialize, fmt::Debug)]
+pub enum WsMessageType {
+ Subscriptions,
+ Devices,
+ Device,
+ Pipeline,
+ Error,
+}
+
+impl Default for WsMessageType {
+ fn default() -> Self {
+ Self::Error
+ }
+}
+
+// TODO(filip): Perhaps add a "message" field to all messages to display toasts
+#[derive(Serialize, fmt::Debug)]
+pub struct BackWsMessage {
+ #[serde(rename = "type")]
+ pub kind: WsMessageType,
+ pub data: WsMessageData,
+}
+
+impl<'de> Deserialize<'de> for BackWsMessage {
+ fn deserialize(deserializer: D) -> Result
+ where
+ D: serde::Deserializer<'de>,
+ {
+ #[derive(Deserialize)]
+ pub struct Message {
+ #[serde(rename = "type")]
+ pub kind: WsMessageType,
+ pub data: serde_json::Value,
+ }
+
+ let message = Message::deserialize(deserializer)?;
+ let data = match message.kind {
+ WsMessageType::Subscriptions => WsMessageData::Subscriptions(
+ serde_json::from_value(message.data).unwrap_or_default(),
+ ),
+ WsMessageType::Devices => {
+ WsMessageData::Devices(serde_json::from_value(message.data).unwrap_or_default())
+ }
+ WsMessageType::Device => {
+ WsMessageData::Device(serde_json::from_value(message.data).unwrap_or_default())
+ }
+ WsMessageType::Pipeline => {
+ WsMessageData::Pipeline(serde_json::from_value(message.data).unwrap())
+ // TODO(filip) change to unwrap_or_default when pipeline config api is more stable
+ }
+ WsMessageType::Error => {
+ WsMessageData::Error(serde_json::from_value(message.data).unwrap_or_default())
+ }
+ };
+ Ok(Self {
+ kind: message.kind,
+ data,
+ })
+ }
+}
+
+impl Default for BackWsMessage {
+ fn default() -> Self {
+ Self {
+ kind: WsMessageType::Error.into(),
+ data: WsMessageData::Error(depthai::Error::default()),
+ }
+ }
+}
+
+pub struct WebSocket {
+ receiver: crossbeam_channel::Receiver,
+ sender: crossbeam_channel::Sender,
+ shutdown: Arc,
+ task: tokio::task::JoinHandle<()>,
+ pub connected: Arc,
+}
+
+impl Default for WebSocket {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl WebSocket {
+ pub fn new() -> Self {
+ re_log::debug!("Creating websocket client");
+ let (recv_tx, recv_rx) = crossbeam_channel::unbounded();
+ let (send_tx, send_rx) = crossbeam_channel::unbounded();
+ let shutdown = Arc::new(AtomicBool::new(false));
+ let shutdown_clone = shutdown.clone();
+ let connected = Arc::new(AtomicBool::new(false));
+ let connected_clone = connected.clone();
+ let task;
+ if let Ok(handle) = tokio::runtime::Handle::try_current() {
+ re_log::debug!("Using current tokio runtime");
+ task = handle.spawn(spawn_ws_client(
+ recv_tx,
+ send_rx,
+ shutdown_clone,
+ connected_clone,
+ ));
+ } else {
+ re_log::debug!("Creating new tokio runtime");
+ task = tokio::runtime::Builder::new_current_thread()
+ .build()
+ .unwrap()
+ .spawn(spawn_ws_client(
+ recv_tx,
+ send_rx,
+ shutdown_clone,
+ connected_clone,
+ ));
+ }
+ Self {
+ receiver: recv_rx,
+ sender: send_tx,
+ shutdown,
+ task,
+ connected,
+ }
+ }
+
+ pub fn shutdown(&mut self) {
+ self.shutdown
+ .store(true, std::sync::atomic::Ordering::SeqCst);
+ }
+
+ pub fn receive(&self) -> Option {
+ if let Ok(message) = self.receiver.try_recv() {
+ match message {
+ WsMessage::Text(text) => {
+ re_log::debug!("Received: {:?}", text);
+ match serde_json::from_str::(&text.as_str()) {
+ Ok(back_message) => {
+ return Some(back_message);
+ }
+ Err(err) => {
+ re_log::error!("Error: {:}", err);
+ return None;
+ }
+ }
+ }
+ _ => {
+ return None;
+ }
+ }
+ }
+ None
+ }
+
+ pub fn send(&self, message: String) {
+ self.sender.send(WsMessage::Text(message));
+ // TODO(filip): This is a hotfix for the websocket not sending the message
+ // This doesn't actually send any message, but it makes the websocket actually send the message previous msg
+ // It has to be something related to tokio::spawn, because it works fine when just running in the current thread
+ self.sender.send(WsMessage::Text("".to_string()));
+ }
+}
diff --git a/crates/re_viewer/src/gpu_bridge/mod.rs b/crates/re_viewer/src/gpu_bridge/mod.rs
index 176f28149dee..9cf35e1d4c01 100644
--- a/crates/re_viewer/src/gpu_bridge/mod.rs
+++ b/crates/re_viewer/src/gpu_bridge/mod.rs
@@ -9,7 +9,9 @@ use egui::mutex::Mutex;
use re_renderer::{
renderer::{ColormappedTexture, RectangleOptions},
- resource_managers::{GpuTexture2D, Texture2DCreationDesc},
+ resource_managers::{
+ GpuTexture2D, Texture2DCreationDesc, TextureCreationError, TextureManager2DError,
+ },
RenderContext, ViewBuilder,
};
@@ -52,12 +54,12 @@ pub fn viewport_resolution_in_pixels(clip_rect: egui::Rect, pixels_from_point: f
[resolution.x as u32, resolution.y as u32]
}
-pub fn try_get_or_create_texture<'a, Err>(
+pub fn try_get_or_create_texture<'a, Err: std::fmt::Display>(
render_ctx: &mut RenderContext,
texture_key: u64,
try_create_texture_desc: impl FnOnce() -> Result, Err>,
-) -> Result {
- render_ctx.texture_manager_2d.get_or_create_with(
+) -> Result> {
+ render_ctx.texture_manager_2d.get_or_try_create_with(
texture_key,
&mut render_ctx.gpu_resources.textures,
try_create_texture_desc,
@@ -68,17 +70,12 @@ pub fn get_or_create_texture<'a>(
render_ctx: &mut RenderContext,
texture_key: u64,
create_texture_desc: impl FnOnce() -> Texture2DCreationDesc<'a>,
-) -> GpuTexture2D {
- enum Never {}
- let result: Result = render_ctx.texture_manager_2d.get_or_create_with(
+) -> Result {
+ render_ctx.texture_manager_2d.get_or_create_with(
texture_key,
&mut render_ctx.gpu_resources.textures,
- || Ok(create_texture_desc()),
- );
- match result {
- Ok(handle) => handle,
- Err(never) => match never {},
- }
+ create_texture_desc,
+ )
}
/// Render a `re_render` view using the given clip rectangle.
diff --git a/crates/re_viewer/src/gpu_bridge/tensor_to_gpu.rs b/crates/re_viewer/src/gpu_bridge/tensor_to_gpu.rs
index adcd47dd0922..7787f6c50f15 100644
--- a/crates/re_viewer/src/gpu_bridge/tensor_to_gpu.rs
+++ b/crates/re_viewer/src/gpu_bridge/tensor_to_gpu.rs
@@ -1,5 +1,6 @@
//! Upload [`Tensor`] to [`re_renderer`].
+use anyhow::Context;
use std::borrow::Cow;
use bytemuck::{allocation::pod_collect_to_vec, cast_slice, Pod};
@@ -94,7 +95,8 @@ fn color_tensor_to_gpu(
width,
height,
})
- })?;
+ })
+ .map_err(|err| anyhow::anyhow!("Failed to create texture for color tensor: {err}"))?;
let texture_format = texture_handle.format();
@@ -110,7 +112,7 @@ fn color_tensor_to_gpu(
crate::gpu_bridge::range(tensor_stats)?
};
- let color_mapper = if texture_format.describe().components == 1 {
+ let color_mapper = if re_renderer::texture_info::num_texture_components(texture_format) == 1 {
// Single-channel images = luminance = grayscale
Some(ColorMapper::Function(re_renderer::Colormap::Grayscale))
} else {
@@ -151,14 +153,13 @@ fn class_id_tensor_to_gpu(
.ok_or_else(|| anyhow::anyhow!("compressed_tensor!?"))?;
anyhow::ensure!(0.0 <= min, "Negative class id");
- // create a lookup texture for the colors that's 256 wide,
- // and as many rows as needed to fit all the classes.
- anyhow::ensure!(max <= 65535.0, "Too many class ids");
+ anyhow::ensure!(max <= 65535.0, "Too many class ids"); // we only support u8 and u16 tensors
// We pack the colormap into a 2D texture so we don't go over the max texture size.
// We only support u8 and u16 class ids, so 256^2 is the biggest texture we need.
+ let num_colors = (max + 1.0) as usize;
let colormap_width = 256;
- let colormap_height = (max as usize + colormap_width - 1) / colormap_width;
+ let colormap_height = (num_colors + colormap_width - 1) / colormap_width;
let colormap_texture_handle =
get_or_create_texture(render_ctx, hash(annotations.row_id), || {
@@ -179,11 +180,13 @@ fn class_id_tensor_to_gpu(
width: colormap_width as u32,
height: colormap_height as u32,
}
- });
+ })
+ .context("Failed to create class_id_colormap.")?;
let main_texture_handle = try_get_or_create_texture(render_ctx, hash(tensor.id()), || {
general_texture_creation_desc_from_tensor(debug_name, tensor)
- })?;
+ })
+ .map_err(|err| anyhow::anyhow!("Failed to create texture for class id tensor: {err}"))?;
Ok(ColormappedTexture {
texture: main_texture_handle,
@@ -212,7 +215,8 @@ fn depth_tensor_to_gpu(
let texture = try_get_or_create_texture(render_ctx, hash(tensor.id()), || {
general_texture_creation_desc_from_tensor(debug_name, tensor)
- })?;
+ })
+ .map_err(|err| anyhow::anyhow!("Failed to create depth tensor texture: {err}"))?;
Ok(ColormappedTexture {
texture,
diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs
index 85a2939b3160..25618fe852be 100644
--- a/crates/re_viewer/src/lib.rs
+++ b/crates/re_viewer/src/lib.rs
@@ -1,9 +1,10 @@
-//! Rerun Viewer GUI.
+//! Depthai Viewer GUI.
//!
-//! This crate contains all the GUI code for the Rerun Viewer,
+//! This crate contains all the GUI code for the Depthai Viewer,
//! including all 2D and 3D visualization code.
mod app;
+pub mod depthai;
pub mod env_vars;
pub(crate) mod gpu_bridge;
pub mod math;
@@ -14,7 +15,7 @@ mod viewer_analytics;
pub(crate) use misc::{mesh_loader, Item, TimeControl, TimeView, ViewerContext};
use re_log_types::PythonVersion;
-pub(crate) use ui::{memory_panel, selection_panel, time_panel, UiVerbosity};
+pub(crate) use ui::{bottom_panel, memory_panel, selection_panel, time_panel, UiVerbosity};
pub use app::{App, StartupOptions};
pub use remote_viewer_app::RemoteViewerApp;
@@ -115,11 +116,7 @@ impl AppEnvironment {
// ---------------------------------------------------------------------------
#[allow(dead_code)]
-const APPLICATION_NAME: &str = "Rerun Viewer";
-
-pub(crate) fn hardware_tier() -> re_renderer::config::HardwareTier {
- re_renderer::config::HardwareTier::default()
-}
+const APPLICATION_NAME: &str = "Depthai Viewer";
pub(crate) fn wgpu_options() -> egui_wgpu::WgpuConfiguration {
egui_wgpu::WgpuConfiguration {
@@ -141,10 +138,8 @@ pub(crate) fn wgpu_options() -> egui_wgpu::WgpuConfiguration {
egui_wgpu::SurfaceErrorAction::SkipFrame
}
}),
- backends: re_renderer::config::supported_backends(),
- device_descriptor: crate::hardware_tier().device_descriptor(),
- // TODO(andreas): This should be the default for egui-wgpu.
- power_preference: wgpu::util::power_preference_from_env().unwrap_or(wgpu::PowerPreference::HighPerformance),
+ supported_backends: re_renderer::config::supported_backends(),
+ device_descriptor: std::sync::Arc::new(|adapter| re_renderer::config::HardwareTier::from_adapter(adapter).device_descriptor()),
..Default::default()
}
}
@@ -157,11 +152,14 @@ pub(crate) fn customize_eframe(cc: &eframe::CreationContext<'_>) -> re_ui::ReUi
let paint_callback_resources = &mut render_state.renderer.write().paint_callback_resources;
paint_callback_resources.insert(RenderContext::new(
+ &render_state.adapter,
render_state.device.clone(),
render_state.queue.clone(),
RenderContextConfig {
output_format_color: render_state.target_format,
- hardware_tier: crate::hardware_tier(),
+ hardware_tier: re_renderer::config::HardwareTier::from_adapter(
+ &render_state.adapter,
+ ),
},
));
}
@@ -188,6 +186,11 @@ pub fn wake_up_ui_thread_on_each_msg(
.name("ui_waker".to_owned())
.spawn(move || {
while let Ok((sent_at, msg)) = rx.recv_with_send_time() {
+ // TODO(filip): Improve this code to be more smart, maybe we have 100 legit messages and ui is in focus?
+ if tx.len() > 100 {
+ re_log::trace!("Dropping messages: Most likely the app is not in focus!");
+ continue;
+ }
if tx.send_at(sent_at, msg).is_ok() {
ctx.request_repaint();
} else {
diff --git a/crates/re_viewer/src/misc/selection_state.rs b/crates/re_viewer/src/misc/selection_state.rs
index 9372ea91dc0a..9068b00a977f 100644
--- a/crates/re_viewer/src/misc/selection_state.rs
+++ b/crates/re_viewer/src/misc/selection_state.rs
@@ -7,7 +7,7 @@ use re_data_store::EntityPath;
use re_log_types::{component_types::InstanceKey, EntityPathHash};
use re_renderer::OutlineMaskPreference;
-use crate::ui::{Blueprint, SelectionHistory, SpaceView, SpaceViewId};
+use crate::ui::{Blueprint, SelectionHistory, SpaceView, SpaceViewId, Viewport};
use super::{Item, ItemCollection};
@@ -288,9 +288,9 @@ impl SelectionState {
&mut self,
re_ui: &re_ui::ReUi,
ui: &mut egui::Ui,
- blueprint: &mut Blueprint,
+ viewport: &mut Viewport,
) -> Option {
- self.history.selection_ui(re_ui, ui, blueprint)
+ self.history.selection_ui(re_ui, ui, viewport)
}
pub fn highlight_for_ui_element(&self, test: &Item) -> HoverHighlight {
diff --git a/crates/re_viewer/src/misc/viewer_context.rs b/crates/re_viewer/src/misc/viewer_context.rs
index 90fb0b40ea25..93584b9c254a 100644
--- a/crates/re_viewer/src/misc/viewer_context.rs
+++ b/crates/re_viewer/src/misc/viewer_context.rs
@@ -11,6 +11,8 @@ use super::{
HoverHighlight,
};
+use crate::depthai::depthai;
+
/// Common things needed by many parts of the viewer.
pub struct ViewerContext<'a> {
/// Global options for the whole viewer.
@@ -32,6 +34,7 @@ pub struct ViewerContext<'a> {
pub re_ui: &'a re_ui::ReUi,
pub render_ctx: &'a mut re_renderer::RenderContext,
+ pub depthai_state: &'a mut depthai::State,
}
impl<'a> ViewerContext<'a> {
diff --git a/crates/re_viewer/src/native.rs b/crates/re_viewer/src/native.rs
index c365c5829dc3..37edb5399def 100644
--- a/crates/re_viewer/src/native.rs
+++ b/crates/re_viewer/src/native.rs
@@ -1,9 +1,9 @@
use re_log_types::LogMsg;
-
use crate::APPLICATION_NAME;
-type AppCreator =
- Box, re_ui::ReUi) -> Box>;
+type AppCreator = Box<
+ dyn FnOnce(&eframe::CreationContext<'_>, re_ui::ReUi) -> Box
+>;
// NOTE: the name of this function is hard-coded in `crates/rerun/src/crash_handler.rs`!
pub fn run_native_app(app_creator: AppCreator) -> eframe::Result<()> {
@@ -20,7 +20,7 @@ pub fn run_native_app(app_creator: AppCreator) -> eframe::Result<()> {
transparent: re_ui::CUSTOM_WINDOW_DECORATIONS,
follow_system_theme: false,
- default_theme: eframe::Theme::Dark,
+ default_theme: eframe::Theme::Light,
renderer: eframe::Renderer::Wgpu,
wgpu_options: crate::wgpu_options(),
@@ -36,7 +36,7 @@ pub fn run_native_app(app_creator: AppCreator) -> eframe::Result<()> {
Box::new(move |cc| {
let re_ui = crate::customize_eframe(cc);
app_creator(cc, re_ui)
- }),
+ })
)
}
@@ -44,21 +44,25 @@ pub fn run_native_viewer_with_messages(
build_info: re_build_info::BuildInfo,
app_env: crate::AppEnvironment,
startup_options: crate::StartupOptions,
- log_messages: Vec,
+ log_messages: Vec
) -> eframe::Result<()> {
let (tx, rx) = re_smart_channel::smart_channel(re_smart_channel::Source::Sdk);
for log_msg in log_messages {
tx.send(log_msg).ok();
}
- run_native_app(Box::new(move |cc, re_ui| {
- Box::new(crate::App::from_receiver(
- build_info,
- &app_env,
- startup_options,
- re_ui,
- cc.storage,
- rx,
- std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
- ))
- }))
+ run_native_app(
+ Box::new(move |cc, re_ui| {
+ Box::new(
+ crate::App::from_receiver(
+ build_info,
+ &app_env,
+ startup_options,
+ re_ui,
+ cc.storage,
+ rx,
+ std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false))
+ )
+ )
+ })
+ )
}
diff --git a/crates/re_viewer/src/ui/auto_layout.rs b/crates/re_viewer/src/ui/auto_layout.rs
index 36778aa689f3..7af2a61b8ee1 100644
--- a/crates/re_viewer/src/ui/auto_layout.rs
+++ b/crates/re_viewer/src/ui/auto_layout.rs
@@ -9,33 +9,45 @@
//! * We also want to pick aspect ratios that fit the data pretty well
// TODO(emilk): fix O(N^2) execution time (where N = number of spaces)
-use std::collections::BTreeMap;
+use core::panic;
+use std::collections::{BTreeMap, BTreeSet};
use ahash::HashMap;
use egui::Vec2;
+use egui_dock::NodeIndex;
use itertools::Itertools as _;
+use lazy_static::lazy_static;
use re_data_store::{EntityPath, EntityPathPart};
-use super::{space_view::SpaceView, view_category::ViewCategory, SpaceViewId};
+use crate::depthai::depthai;
+
+use super::{
+ space_view::{SpaceView, SpaceViewKind},
+ view_category::ViewCategory,
+ viewport::Tab,
+ SpaceViewId,
+};
#[derive(Clone, Debug)]
pub struct SpaceMakeInfo {
pub id: SpaceViewId,
/// Some path we use to group the views by
- pub path: EntityPath,
+ pub path: Option,
- pub category: ViewCategory,
+ pub category: Option,
/// Desired aspect ratio, if any.
pub aspect_ratio: Option,
+
+ pub kind: SpaceViewKind,
}
enum LayoutSplit {
LeftRight(Box, f32, Box),
TopBottom(Box, f32, Box),
- Leaf(SpaceMakeInfo),
+ Leaf(Vec),
}
enum SplitDirection {
@@ -43,14 +55,346 @@ enum SplitDirection {
TopBottom { top: Vec2, t: f32, bottom: Vec2 },
}
-pub(crate) fn tree_from_space_views(
+fn right_panel_split() -> LayoutSplit {
+ LayoutSplit::TopBottom(
+ LayoutSplit::Leaf(vec![CONFIG_SPACE_VIEW.clone(), STATS_SPACE_VIEW.clone()]).into(),
+ 0.7,
+ LayoutSplit::Leaf(vec![SELECTION_SPACE_VIEW.clone()]).into(),
+ )
+}
+
+// Creates space make infos for constant space views.
+// This is needed to be able to search for these views in the tree later, based on the SpaceViewId
+lazy_static! {
+ static ref CONFIG_SPACE_VIEW: SpaceMakeInfo = SpaceMakeInfo {
+ id: SpaceViewId::random(),
+ path: None,
+ category: None,
+ aspect_ratio: None,
+ kind: SpaceViewKind::Config,
+ };
+ static ref STATS_SPACE_VIEW: SpaceMakeInfo = SpaceMakeInfo {
+ id: SpaceViewId::random(),
+ path: None,
+ category: None,
+ aspect_ratio: None,
+ kind: SpaceViewKind::Stats,
+ };
+ static ref SELECTION_SPACE_VIEW: SpaceMakeInfo = SpaceMakeInfo {
+ id: SpaceViewId::random(),
+ path: None,
+ category: None,
+ aspect_ratio: None,
+ kind: SpaceViewKind::Selection,
+ };
+ static ref CONSTANT_SPACE_VIEWS: Vec = vec![
+ CONFIG_SPACE_VIEW.id,
+ STATS_SPACE_VIEW.id,
+ SELECTION_SPACE_VIEW.id,
+ ];
+}
+
+fn push_space_view_to_leaf(
+ tree: &mut egui_dock::Tree,
+ leaf: NodeIndex,
+ space_view: &SpaceView,
+) {
+ tree.set_focused_node(leaf);
+ tree.push_to_focused_leaf(space_view.into());
+}
+
+fn find_space_path_in_tree(
+ tree: &egui_dock::Tree,
+ space_view_path: &EntityPath,
+) -> Option {
+ tree.tabs()
+ .find(|tab| {
+ let Some(path) = &tab.space_path else {
+ return false;
+ };
+ path == space_view_path
+ })
+ .cloned()
+}
+
+fn find_top_left_leaf(tree: &egui_dock::Tree) -> NodeIndex {
+ let mut node = NodeIndex::root();
+ loop {
+ if tree[node].is_leaf() {
+ println!("Node: {node:?}");
+ return node;
+ }
+ node = node.right();
+ }
+}
+
+/// Is it possible to create a quad of left top 3d color left bottom 2d color
+/// right top 3d mono right bottom 2d mono, based on the current tree
+fn can_create_color_mono_quad(tree: &egui_dock::Tree, space_views: Vec) -> bool {
+ let Some(color3d_tab) = find_space_path_in_tree(tree, &depthai::entity_paths::COLOR_CAM_3D) else {
+ return false;
+ };
+ let Some((color3d_node_index, _)) = tree.find_tab(&color3d_tab) else {
+ return false;
+ };
+ let Some(mono3d_tab) = find_space_path_in_tree(tree, &depthai::entity_paths::MONO_CAM_3D) else {
+ return false;
+ };
+ let Some((mono3d_node_index, mono3d_tab_index)) = tree.find_tab(&mono3d_tab) else {
+ return false;
+ };
+ mono3d_node_index == color3d_node_index.right()
+}
+
+/// Insert new space views and remove space views that aren't available anymore.
+/// Tries to layout the viewport as intuitively as possible
+/// TODO(filip): Reduce the size of this code. A lot of it is repetitive and can be refactored
+/// TODO(filip): Improve code functionally: detect when you can group mono and color 3d + 2d views into a 4 way split
+pub(crate) fn update_tree(
+ tree: &mut egui_dock::Tree,
+ visible_space_views: &BTreeSet,
+ space_views: &HashMap,
+ is_maximized: bool,
+) {
+ // One view is maximized
+ if is_maximized {
+ let tab: Tab;
+ let space_view_id = visible_space_views.first().unwrap();
+ if let Some(space_view) = space_views.get(space_view_id) {
+ tab = space_view.into();
+ } else {
+ tab = if space_view_id == &STATS_SPACE_VIEW.id {
+ Tab {
+ space_path: None,
+ space_view_id: *space_view_id,
+ space_view_kind: SpaceViewKind::Stats,
+ }
+ } else {
+ re_log::warn_once!("Can't maximize this space view");
+ return;
+ }
+ }
+ *tree = egui_dock::Tree::new(vec![tab]);
+ return;
+ }
+
+ for tab in tree.clone().tabs().filter(|tab| {
+ !CONSTANT_SPACE_VIEWS.contains(&tab.space_view_id)
+ && !visible_space_views
+ .iter()
+ .any(|sv_id| sv_id == &tab.space_view_id)
+ }) {
+ tree.remove_tab(tree.find_tab(tab).unwrap());
+ }
+
+ // If there aren't any "data" space views, we show the config, stats and selection panel on the right.
+ // With an empty leaf on the left (aka middle if you take into account the blueprint panel)
+ if visible_space_views.is_empty() {
+ *tree = egui_dock::Tree::new(vec![]);
+
+ tree_from_split(
+ tree,
+ NodeIndex::root(),
+ &LayoutSplit::LeftRight(
+ LayoutSplit::Leaf(Vec::new()).into(),
+ 0.5,
+ right_panel_split().into(),
+ ),
+ );
+ let (config_node, config_tab) = tree
+ .find_tab(
+ tree.tabs()
+ .find(|tab| tab.space_view_id == CONFIG_SPACE_VIEW.id)
+ .unwrap(), // CONFIG_SPACE_VIEW is always present
+ )
+ .unwrap();
+ tree.set_active_tab(config_node, config_tab);
+
+ return;
+ }
+
+ let visible_space_views = visible_space_views
+ .iter()
+ .map(|sv| space_views.get(sv).unwrap());
+ // Insert new space views
+ for space_view in visible_space_views {
+ // println!("Space view: {:?}", space_view.space_path.clone());
+ if tree
+ .find_tab(&Tab {
+ space_view_id: space_view.id,
+ space_view_kind: SpaceViewKind::Data,
+ space_path: Some(space_view.space_path.clone()),
+ })
+ .is_none()
+ {
+ // Insert space view into the tree, taking into account the following:
+ // * If the space view is a 3d view, try to find the corresponding 2d view and place the 3d on top of the 2d view
+ // * If the space view is a 2d view, try to find the corresponding 3d view and place the 2d view on top of the 3d view
+ // * If the space view is a duplicate of an existing view (entity path is the same space_view_id differs), place it within the same leaf as the existing view
+ // * else if none of the above, just place the view in the top left corner as a new tab, (don't insert it into a leaf, create a new leaf)
+ // println!("Space view getting inserted: {:?}", space_view.space_path);
+
+ match space_view.space_path {
+ ref space_path
+ if space_path.hash() == depthai::entity_paths::COLOR_CAM_3D.hash() =>
+ {
+ if let Some(existing_3d) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::COLOR_CAM_3D)
+ {
+ let (leaf, _) = tree.find_tab(&existing_3d).unwrap();
+ push_space_view_to_leaf(tree, leaf, space_view);
+ } else if let Some(existing_2d) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RGB_PINHOLE_CAMERA)
+ {
+ let (node_index, _) = tree.find_tab(&existing_2d).unwrap();
+ tree.split_above(node_index, 0.5, vec![space_view.into()]);
+ } else if let Some(existing_mono3d) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::MONO_CAM_3D)
+ {
+ let (leaf, _) = tree.find_tab(&existing_mono3d).unwrap();
+ tree.split_left(leaf, 0.5, vec![space_view.into()]);
+ } else {
+ let top_left = find_top_left_leaf(tree);
+ push_space_view_to_leaf(tree, top_left, space_view);
+ }
+ }
+ ref space_path
+ if space_path.hash() == depthai::entity_paths::RGB_PINHOLE_CAMERA.hash() =>
+ {
+ if let Some(existing_2d) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RGB_PINHOLE_CAMERA)
+ {
+ let (leaf, _) = tree.find_tab(&existing_2d).unwrap();
+ push_space_view_to_leaf(tree, leaf, space_view);
+ } else if let Some(existing_left) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::LEFT_PINHOLE_CAMERA)
+ {
+ let (node_index, _) = tree.find_tab(&existing_left).unwrap();
+ tree.split_left(node_index, 0.5, vec![space_view.into()]);
+ } else if let Some(existing_right) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RIGHT_PINHOLE_CAMERA)
+ {
+ let (node_index, _) = tree.find_tab(&existing_right).unwrap();
+ tree.split_left(node_index, 0.5, vec![space_view.into()]);
+ } else if let Some(existing_3d) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::COLOR_CAM_3D)
+ {
+ let (node_index, _) = tree.find_tab(&existing_3d).unwrap();
+ tree.split_below(node_index, 0.5, vec![space_view.into()]);
+ } else {
+ let top_left = find_top_left_leaf(tree);
+ push_space_view_to_leaf(tree, top_left, space_view);
+ }
+ }
+ ref space_path
+ if space_path.hash() == depthai::entity_paths::MONO_CAM_3D.hash() =>
+ {
+ if let Some(existing_3d) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::MONO_CAM_3D)
+ {
+ let (leaf, _) = tree.find_tab(&existing_3d).unwrap();
+ push_space_view_to_leaf(tree, leaf, space_view);
+ } else if let Some(existing_3d_color) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::COLOR_CAM_3D)
+ {
+ let (leaf, _) = tree.find_tab(&existing_3d_color).unwrap();
+ tree.split_right(leaf, 0.5, vec![space_view.into()]);
+ } else if let Some(existing_left) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::LEFT_PINHOLE_CAMERA)
+ {
+ let (leaf, _) = tree.find_tab(&existing_left).unwrap();
+ tree.split_above(leaf, 0.5, vec![space_view.into()]);
+ } else if let Some(existing_right) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RIGHT_PINHOLE_CAMERA)
+ {
+ let (leaf, _) = tree.find_tab(&existing_right).unwrap();
+ tree.split_above(leaf, 0.5, vec![space_view.into()]);
+ } else if let Some(existing_color) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RGB_PINHOLE_CAMERA)
+ {
+ let (leaf, _) = tree.find_tab(&existing_color).unwrap();
+ tree.split_right(leaf, 0.5, vec![space_view.into()]);
+ } else {
+ let top_left = find_top_left_leaf(tree);
+ push_space_view_to_leaf(tree, top_left, space_view);
+ }
+ }
+ ref space_path
+ if space_path.hash() == depthai::entity_paths::LEFT_PINHOLE_CAMERA.hash() =>
+ {
+ if let Some(existing_left) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::LEFT_PINHOLE_CAMERA)
+ {
+ let (leaf, _) = tree.find_tab(&existing_left).unwrap();
+ push_space_view_to_leaf(tree, leaf, space_view);
+ } else if let Some(existing_right) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RIGHT_PINHOLE_CAMERA)
+ {
+ let (leaf, _) = tree.find_tab(&existing_right).unwrap();
+ push_space_view_to_leaf(tree, leaf, space_view);
+ } else if let Some(existing_3d) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::MONO_CAM_3D)
+ {
+ let (node_index, _) = tree.find_tab(&existing_3d).unwrap();
+ tree.split_below(node_index, 0.5, vec![space_view.into()]);
+ } else if let Some(existing_2d_color) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RGB_PINHOLE_CAMERA)
+ {
+ let (node_index, _) = tree.find_tab(&existing_2d_color).unwrap();
+ tree.split_right(node_index, 0.5, vec![space_view.into()]);
+ } else {
+ let top_left = find_top_left_leaf(tree);
+ push_space_view_to_leaf(tree, top_left, space_view);
+ }
+ }
+ ref space_path
+ if space_path.hash() == depthai::entity_paths::RIGHT_PINHOLE_CAMERA.hash() =>
+ {
+ if let Some(existing_right) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RIGHT_PINHOLE_CAMERA)
+ {
+ let (leaf, _) = tree.find_tab(&existing_right).unwrap();
+ push_space_view_to_leaf(tree, leaf, space_view);
+ } else if let Some(existing_left) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::LEFT_PINHOLE_CAMERA)
+ {
+ let (leaf, _) = tree.find_tab(&existing_left).unwrap();
+ push_space_view_to_leaf(tree, leaf, space_view);
+ } else if let Some(existing_3d) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::MONO_CAM_3D)
+ {
+ let (node_index, _) = tree.find_tab(&existing_3d).unwrap();
+ tree.split_below(node_index, 0.5, vec![space_view.into()]);
+ } else if let Some(existing_2d_color) =
+ find_space_path_in_tree(tree, &depthai::entity_paths::RGB_PINHOLE_CAMERA)
+ {
+ let (node_index, _) = tree.find_tab(&existing_2d_color).unwrap();
+ tree.split_right(node_index, 0.5, vec![space_view.into()]);
+ } else {
+ let top_left = find_top_left_leaf(tree);
+ push_space_view_to_leaf(tree, top_left, space_view);
+ }
+ }
+ _ => {}
+ };
+ }
+ }
+}
+
+/// Default layout of space views tuned for depthai-viewer
+pub(crate) fn default_tree_from_space_views(
viewport_size: egui::Vec2,
visible: &std::collections::BTreeSet,
space_views: &HashMap,
-) -> egui_dock::Tree {
- let mut tree = egui_dock::Tree::new(vec![]);
-
- let mut space_make_infos = space_views
+) -> egui_dock::Tree {
+ // TODO(filip): Implement sensible auto layout when space views changes.
+ // Something like:
+ // - Get the tabs that need to be added or removed
+ // - Removal is easy, just remove the tab
+ // - Addition should try to layout like currently 3d, 2d views. New views just appear in the top left corner i guess.
+ let mut tree = egui_dock::Tree::new(Vec::new());
+
+ let spaces = space_views
.iter()
.filter(|(space_view_id, _space_view)| visible.contains(space_view_id))
// Sort for determinism:
@@ -76,30 +420,116 @@ pub(crate) fn tree_from_space_views(
}
}
ViewCategory::Tensor | ViewCategory::TimeSeries => Some(1.0), // Not sure if we should do `None` here.
- ViewCategory::Text => Some(2.0), // Make text logs wide
+ ViewCategory::Text | ViewCategory::NodeGraph => Some(2.0), // Make text logs wide
ViewCategory::BarChart => None,
};
SpaceMakeInfo {
id: *space_view_id,
- path: space_view.space_path.clone(),
- category: space_view.category,
+ path: Some(space_view.space_path.clone()),
+ category: Some(space_view.category),
aspect_ratio,
+ kind: SpaceViewKind::Data,
}
})
.collect_vec();
- if !space_make_infos.is_empty() {
- // Users often organize by path prefix, so we start by splitting along that
- let layout = layout_by_path_prefix(viewport_size, &mut space_make_infos);
- tree_from_split(&mut tree, egui_dock::NodeIndex(0), &layout);
+ if !spaces.is_empty() {
+ let layout = LayoutSplit::LeftRight(
+ {
+ if spaces.len() == 1 {
+ LayoutSplit::Leaf(spaces)
+ } else {
+ // Split space views:
+ // - Color stream available: Split top 3d, bottom 2d
+ // - if mono available split it right from color streams into 3d top and both 2d in a tab group on the bottom
+ let mut top_left_spaces = Vec::new();
+ let mut top_right_spaces = Vec::new();
+ let mut bottom_left_spaces = Vec::new();
+ let mut bottom_right_spaces = Vec::new();
+ spaces.iter().cloned().for_each(|space| {
+ let Some(space_path) = &space.path else {
+ return;
+ };
+ if space_path.hash() == depthai::entity_paths::COLOR_CAM_3D.hash() {
+ top_left_spaces.push(space);
+ } else if space_path.hash()
+ == depthai::entity_paths::RGB_PINHOLE_CAMERA.hash()
+ {
+ top_right_spaces.push(space);
+ } else if space_path.hash() == depthai::entity_paths::MONO_CAM_3D.hash() {
+ bottom_left_spaces.push(space);
+ } else {
+ bottom_right_spaces.push(space);
+ }
+ });
+
+ let color_empty = top_left_spaces.is_empty() && top_right_spaces.is_empty();
+ let mono_empty =
+ bottom_left_spaces.is_empty() && bottom_right_spaces.is_empty();
+ let mut color_split = LayoutSplit::TopBottom(
+ LayoutSplit::Leaf(top_left_spaces.clone()).into(),
+ 0.5,
+ LayoutSplit::Leaf(top_right_spaces.clone()).into(),
+ );
+ let mut mono_split = LayoutSplit::TopBottom(
+ LayoutSplit::Leaf(bottom_left_spaces.clone()).into(),
+ 0.5,
+ LayoutSplit::Leaf(bottom_right_spaces.clone()).into(),
+ );
+
+ if !color_empty && mono_empty {
+ color_split
+ } else if !color_empty && !mono_empty {
+ if top_left_spaces.is_empty() {
+ color_split = LayoutSplit::Leaf(top_right_spaces);
+ } else if top_right_spaces.is_empty() {
+ color_split = LayoutSplit::Leaf(top_left_spaces);
+ }
+ if bottom_left_spaces.is_empty() {
+ mono_split = LayoutSplit::Leaf(bottom_right_spaces);
+ } else if bottom_right_spaces.is_empty() {
+ mono_split = LayoutSplit::Leaf(bottom_left_spaces);
+ }
+ LayoutSplit::LeftRight(color_split.into(), 0.5, mono_split.into())
+ } else if color_empty && !mono_empty {
+ mono_split
+ } else {
+ LayoutSplit::Leaf(spaces)
+ }
+ }
+ }
+ .into(),
+ 0.7,
+ right_panel_split().into(),
+ );
+ tree_from_split(&mut tree, NodeIndex::root(), &layout);
+ } else {
+ tree_from_split(
+ &mut tree,
+ NodeIndex::root(),
+ &LayoutSplit::LeftRight(
+ LayoutSplit::Leaf(vec![]).into(),
+ 0.7,
+ right_panel_split().into(),
+ ),
+ );
}
+ // Always set the config tab as the active tab
+ let (config_node, config_tab) = tree
+ .find_tab(
+ tree.tabs()
+ .find(|tab| tab.space_view_id == CONFIG_SPACE_VIEW.id)
+ .unwrap(), // CONFIG_SPACE_VIEW is always present
+ )
+ .unwrap();
+ tree.set_active_tab(config_node, config_tab);
tree
}
fn tree_from_split(
- tree: &mut egui_dock::Tree,
+ tree: &mut egui_dock::Tree,
parent: egui_dock::NodeIndex,
split: &LayoutSplit,
) {
@@ -114,9 +544,15 @@ fn tree_from_split(
tree_from_split(tree, top_ni, top);
tree_from_split(tree, bottom_ni, bottom);
}
- LayoutSplit::Leaf(space_info) => {
+ LayoutSplit::Leaf(space_infos) => {
tree.set_focused_node(parent);
- tree.push_to_focused_leaf(space_info.id);
+ for space_info in space_infos {
+ tree.push_to_focused_leaf(Tab {
+ space_view_id: space_info.id,
+ space_view_kind: space_info.kind,
+ space_path: space_info.path.clone(),
+ });
+ }
}
}
}
@@ -126,7 +562,7 @@ fn layout_by_category(viewport_size: egui::Vec2, spaces: &mut [SpaceMakeInfo]) -
assert!(!spaces.is_empty());
if spaces.len() == 1 {
- LayoutSplit::Leaf(spaces[0].clone())
+ LayoutSplit::Leaf(spaces.to_vec())
} else {
let groups = group_by_category(spaces);
@@ -145,7 +581,7 @@ fn layout_by_path_prefix(viewport_size: egui::Vec2, spaces: &mut [SpaceMakeInfo]
assert!(!spaces.is_empty());
if spaces.len() == 1 {
- LayoutSplit::Leaf(spaces[0].clone())
+ LayoutSplit::Leaf(spaces.to_vec())
} else {
let groups = group_by_path_prefix(spaces);
@@ -264,7 +700,10 @@ fn desired_aspect_ratio(spaces: &[SpaceMakeInfo]) -> Option {
fn group_by_category(space_infos: &[SpaceMakeInfo]) -> Vec> {
let mut groups: BTreeMap> = Default::default();
for info in space_infos {
- groups.entry(info.category).or_default().push(info.clone());
+ let Some(category) = info.category else {
+ continue;
+ };
+ groups.entry(category).or_default().push(info.clone());
}
groups.into_values().collect()
}
@@ -277,7 +716,12 @@ fn group_by_path_prefix(space_infos: &[SpaceMakeInfo]) -> Vec
let paths = space_infos
.iter()
- .map(|space_info| space_info.path.as_slice().to_vec())
+ .map(|space_info| {
+ let Some(path) = &space_info.path else {
+ panic!("Space {:?} has no path", space_info);
+ };
+ path.as_slice().to_vec()
+ })
.collect_vec();
for i in 0.. {
diff --git a/crates/re_viewer/src/ui/blueprint.rs b/crates/re_viewer/src/ui/blueprint.rs
index 8a173ac58883..969fb15465f5 100644
--- a/crates/re_viewer/src/ui/blueprint.rs
+++ b/crates/re_viewer/src/ui/blueprint.rs
@@ -18,8 +18,8 @@ impl Blueprint {
pub fn new(egui_ctx: &egui::Context) -> Self {
let screen_size = egui_ctx.screen_rect().size();
Self {
- blueprint_panel_expanded: screen_size.x > 750.0,
- selection_panel_expanded: screen_size.x > 1000.0,
+ blueprint_panel_expanded: true,
+ selection_panel_expanded: true,
time_panel_expanded: screen_size.y > 600.0,
viewport: Default::default(),
}
@@ -71,7 +71,8 @@ impl Blueprint {
..Default::default()
}
.show(ui, |ui| {
- self.viewport.tree_ui(ctx, ui);
+ self.viewport
+ .add_or_remove_space_views_ui(ctx, ui, spaces_info);
});
});
}
@@ -91,15 +92,13 @@ impl Blueprint {
.show_inside(ui, |ui| {
ui.horizontal_centered(|ui| {
ui.strong("Blueprint").on_hover_text(
- "The Blueprint is where you can configure the Rerun Viewer.",
+ "The Blueprint is where you can configure the Depthai Viewer.",
);
ui.allocate_ui_with_layout(
ui.available_size_before_wrap(),
egui::Layout::right_to_left(egui::Align::Center),
|ui| {
- self.viewport
- .add_new_spaceview_button_ui(ctx, ui, spaces_info);
self.reset_button_ui(ctx, ui, spaces_info);
},
);
diff --git a/crates/re_viewer/src/ui/bottom_panel/mod.rs b/crates/re_viewer/src/ui/bottom_panel/mod.rs
new file mode 100644
index 000000000000..6b3e30e06094
--- /dev/null
+++ b/crates/re_viewer/src/ui/bottom_panel/mod.rs
@@ -0,0 +1,115 @@
+use super::Blueprint;
+use crate::ViewerContext;
+use egui::Vec2;
+use egui_dock::{DockArea, TabViewer, Tree};
+
+struct Tabs<'a, 'b> {
+ ctx: &'a mut ViewerContext<'b>,
+}
+
+impl<'a, 'b> Tabs<'a, 'b> {
+ fn xlink_statistics_ui(&mut self, ui: &mut egui::Ui) {
+ ui.label("Xlink");
+ }
+
+ fn imu_ui(&mut self, ui: &mut egui::Ui) {
+ ui.label("IMU");
+ }
+}
+
+impl<'a, 'b> TabViewer for Tabs<'a, 'b> {
+ type Tab = Tab;
+
+ fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
+ match tab {
+ Tab::XlinkStatistics => self.xlink_statistics_ui(ui),
+ Tab::Imu => self.imu_ui(ui),
+ }
+ }
+
+ fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
+ match tab {
+ Tab::XlinkStatistics => "Xlink Statistics".into(),
+ Tab::Imu => "IMU".into(),
+ }
+ }
+}
+
+enum Tab {
+ XlinkStatistics,
+ Imu,
+}
+
+/// The bottom panel of the viewer.
+/// This is where XlinkOut statistics and IMU are logged.
+/// In the future this panel will also be used to replay recordings. (that will likely look mostly like time_panel)
+#[derive(serde::Serialize, serde::Deserialize)]
+pub struct BottomPanel {
+ #[serde(skip)]
+ dock_tree: Tree,
+}
+
+impl Default for BottomPanel {
+ fn default() -> Self {
+ Self {
+ dock_tree: Tree::new(vec![Tab::XlinkStatistics, Tab::Imu]),
+ }
+ }
+}
+
+impl BottomPanel {
+ pub fn show_panel(
+ &mut self,
+ ctx: &mut ViewerContext<'_>,
+ blueprint: &mut Blueprint,
+ ui: &mut egui::Ui,
+ ) {
+ let top_bar_height = 28.0;
+ let margin = ctx.re_ui.bottom_panel_margin();
+ let mut panel_frame = ctx.re_ui.bottom_panel_frame();
+
+ let screen_height = ui.ctx().screen_rect().width();
+
+ let collapsed = egui::TopBottomPanel::bottom("bottom_panel_collapsed")
+ .resizable(false)
+ .show_separator_line(false)
+ .frame(panel_frame)
+ .default_height(44.0);
+
+ let min_height = 150.0;
+ let expanded = egui::TopBottomPanel::bottom("bottom_panel_expanded")
+ .resizable(true)
+ .show_separator_line(false)
+ .frame(panel_frame)
+ .min_height(min_height)
+ .default_height((0.25 * screen_height).clamp(min_height, 250.0).round());
+
+ egui::TopBottomPanel::show_animated_between_inside(
+ ui,
+ true,
+ collapsed,
+ expanded,
+ |ui: &mut egui::Ui, expansion: f32| {
+ if expansion < 1.0 {
+ // Collapsed or animating
+ ui.horizontal(|ui| {
+ ui.spacing_mut().interact_size = Vec2::splat(top_bar_height);
+ ui.visuals_mut().button_frame = true;
+ // self.collapsed_ui(ctx, ui);
+ });
+ } else {
+ // Expanded:
+ // Add extra margin on the left which was intentionally missing on the controls.
+ let mut top_rop_frame = egui::Frame::default();
+ top_rop_frame.inner_margin.left = 8.0;
+ top_rop_frame.show(ui, |ui| {
+ DockArea::new(&mut self.dock_tree)
+ .id(egui::Id::new("bottom_panel_tabs"))
+ .style(re_ui::egui_dock_style(ui.style()))
+ .show_inside(ui, &mut Tabs { ctx });
+ });
+ }
+ },
+ );
+ }
+}
diff --git a/crates/re_viewer/src/ui/data_blueprint.rs b/crates/re_viewer/src/ui/data_blueprint.rs
index 97f0fb3cdc7f..e965ebeb2c19 100644
--- a/crates/re_viewer/src/ui/data_blueprint.rs
+++ b/crates/re_viewer/src/ui/data_blueprint.rs
@@ -210,8 +210,8 @@ impl DataBlueprintTree {
) {
crate::profile_function!();
+ self.entity_paths.clear();
let mut new_leaf_groups = Vec::new();
-
for path in paths {
self.entity_paths.insert(path.clone());
diff --git a/crates/re_viewer/src/ui/data_ui/image.rs b/crates/re_viewer/src/ui/data_ui/image.rs
index 630c011513b7..b187d16892d9 100644
--- a/crates/re_viewer/src/ui/data_ui/image.rs
+++ b/crates/re_viewer/src/ui/data_ui/image.rs
@@ -5,13 +5,13 @@ use re_log_types::{
component_types::{ClassId, Tensor, TensorDataMeaning},
DecodedTensor, TensorElement,
};
-use re_renderer::renderer::ColormappedTexture;
-use re_ui::ReUi;
use crate::{
misc::{caches::TensorStats, ViewerContext},
ui::annotations::AnnotationMap,
};
+use re_renderer::renderer::ColormappedTexture;
+use re_ui::ReUi;
use super::{EntityDataUi, UiVerbosity};
diff --git a/crates/re_viewer/src/ui/device_settings_panel.rs b/crates/re_viewer/src/ui/device_settings_panel.rs
new file mode 100644
index 000000000000..19cd0b1e953b
--- /dev/null
+++ b/crates/re_viewer/src/ui/device_settings_panel.rs
@@ -0,0 +1,397 @@
+use crate::{depthai::depthai, misc::ViewerContext};
+
+use strum::IntoEnumIterator; // Needed for enum::iter()
+
+/// The "Selection View" side-bar.
+#[derive(serde::Deserialize, serde::Serialize, Default)]
+#[serde(default)]
+pub(crate) struct DeviceSettingsPanel {}
+
+const CONFIG_UI_WIDTH: f32 = 224.0;
+
+impl DeviceSettingsPanel {
+ #[allow(clippy::unused_self)]
+ pub fn show_panel(&mut self, ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) {
+ let mut available_devices = ctx.depthai_state.get_devices();
+ let currently_selected_device = ctx.depthai_state.selected_device.clone();
+ let mut combo_device: depthai::DeviceId = currently_selected_device.id;
+ if !combo_device.is_empty() && available_devices.is_empty() {
+ available_devices.push(combo_device.clone());
+ }
+
+ egui::CentralPanel::default()
+ .frame(egui::Frame {
+ inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()),
+ ..Default::default()
+ })
+ .show_inside(ui, |ui| {
+ ui.add_sized(
+ [ui.available_width(), re_ui::ReUi::box_height()],
+ |ui: &mut egui::Ui| {
+ ui.horizontal(|ui| {
+ ctx.re_ui.labeled_combo_box(
+ ui,
+ "Device",
+ if !combo_device.is_empty() {
+ combo_device.clone()
+ } else {
+ "No device selected".to_owned()
+ },
+ true,
+ |ui: &mut egui::Ui| {
+ if ui
+ .selectable_value(
+ &mut combo_device,
+ String::new(),
+ "No device",
+ )
+ .changed()
+ {
+ ctx.depthai_state.set_device(combo_device.clone());
+ }
+ for device in available_devices {
+ if ui
+ .selectable_value(
+ &mut combo_device,
+ device.clone(),
+ device,
+ )
+ .changed()
+ {
+ ctx.depthai_state.set_device(combo_device.clone());
+ }
+ }
+ },
+ );
+ })
+ .response
+ },
+ );
+
+ if ctx.depthai_state.applied_device_config.update_in_progress {
+ ui.add_sized([CONFIG_UI_WIDTH, 10.0], |ui: &mut egui::Ui| {
+ ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
+ ui.add(egui::Spinner::new())
+ })
+ .response
+ });
+ return;
+ }
+ Self::device_configuration_ui(ctx, ui);
+ });
+ }
+
+ fn device_configuration_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) {
+ let mut device_config = ctx.depthai_state.modified_device_config.config.clone();
+ let primary_700 = ctx.re_ui.design_tokens.primary_700;
+ egui::ScrollArea::both()
+ .auto_shrink([false; 2])
+ .show(ui, |ui| {
+ let mut style = ui.style_mut().clone();
+ style.spacing.scroll_bar_inner_margin = 0.0;
+ ui.set_style(style);
+ egui::Frame {
+ fill: ctx.re_ui.design_tokens.gray_50,
+ inner_margin: egui::Margin::symmetric(30.0, 21.0),
+ ..Default::default()
+ }
+ .show(ui, |ui| {
+ ui.horizontal(|ui| {
+ ui.vertical(|ui| {
+ ui.collapsing(
+ egui::RichText::new("Color Camera").color(primary_700),
+ |ui| {
+ ui.vertical(|ui| {
+ ui.set_width(CONFIG_UI_WIDTH);
+ ctx.re_ui.labeled_combo_box(
+ ui,
+ "Resolution",
+ format!("{}", device_config.color_camera.resolution),
+ false,
+ |ui| {
+ for res in &ctx
+ .depthai_state
+ .selected_device
+ .supported_color_resolutions
+ {
+ ui.selectable_value(
+ &mut device_config.color_camera.resolution,
+ *res,
+ format!("{res}"),
+ );
+ }
+ },
+ );
+ ctx.re_ui.labeled_dragvalue(
+ ui,
+ "FPS",
+ &mut device_config.color_camera.fps,
+ 0..=120,
+ );
+ ctx.re_ui.labeled_checkbox(
+ ui,
+ "Stream",
+ &mut device_config.color_camera.stream_enabled,
+ );
+ });
+ },
+ );
+ ui.collapsing(
+ egui::RichText::new("Left Mono Camera").color(primary_700),
+ |ui| {
+ ui.vertical(|ui| {
+ ui.set_width(CONFIG_UI_WIDTH);
+ ctx.re_ui.labeled_combo_box(
+ ui,
+ "Resolution",
+ format!("{}", device_config.left_camera.resolution),
+ false,
+ |ui| {
+ for res in &ctx
+ .depthai_state
+ .selected_device
+ .supported_left_mono_resolutions
+ {
+ ui.selectable_value(
+ &mut device_config.left_camera.resolution,
+ *res,
+ format!("{res}"),
+ );
+ }
+ },
+ );
+ ctx.re_ui.labeled_dragvalue(
+ ui,
+ "FPS",
+ &mut device_config.left_camera.fps,
+ 0..=120,
+ );
+ ctx.re_ui.labeled_checkbox(
+ ui,
+ "Stream",
+ &mut device_config.left_camera.stream_enabled,
+ );
+ })
+ },
+ );
+
+ ui.collapsing(
+ egui::RichText::new("Right Mono Camera").color(primary_700),
+ |ui| {
+ ui.vertical(|ui| {
+ ui.set_width(CONFIG_UI_WIDTH);
+ ctx.re_ui.labeled_combo_box(
+ ui,
+ "Resolution",
+ format!("{}", device_config.right_camera.resolution),
+ false,
+ |ui| {
+ for res in &ctx
+ .depthai_state
+ .selected_device
+ .supported_right_mono_resolutions
+ {
+ ui.selectable_value(
+ &mut device_config.right_camera.resolution,
+ *res,
+ format!("{res}"),
+ );
+ }
+ },
+ );
+ ctx.re_ui.labeled_dragvalue(
+ ui,
+ "FPS",
+ &mut device_config.right_camera.fps,
+ 0..=120,
+ );
+ ctx.re_ui.labeled_checkbox(
+ ui,
+ "Stream",
+ &mut device_config.right_camera.stream_enabled,
+ );
+ })
+ },
+ );
+
+ // This is a hack, I wanted AI settings at the bottom, but some depth settings names
+ // are too long and it messes up the width of the ui layout somehow.
+ ui.collapsing(
+ egui::RichText::new("AI settings").color(primary_700),
+ |ui| {
+ ui.vertical(|ui| {
+ ui.set_width(CONFIG_UI_WIDTH);
+ ctx.re_ui.labeled_combo_box(
+ ui,
+ "AI Model",
+ device_config.ai_model.display_name.clone(),
+ false,
+ |ui| {
+ for nn in &ctx.depthai_state.neural_networks {
+ ui.selectable_value(
+ &mut device_config.ai_model,
+ nn.clone(),
+ &nn.display_name,
+ );
+ }
+ },
+ );
+ });
+ },
+ );
+
+ let mut depth = device_config.depth.unwrap_or_default();
+ if depth.align == depthai::CameraBoardSocket::CENTER && !depth.lr_check
+ {
+ depth.align = depthai::CameraBoardSocket::AUTO;
+ }
+
+ ui.collapsing(
+ egui::RichText::new("Depth settings").color(primary_700),
+ |ui| {
+ ui.vertical(|ui| {
+ ui.set_width(CONFIG_UI_WIDTH);
+ ctx.re_ui.labeled_checkbox(
+ ui,
+ "LR Check",
+ &mut depth.lr_check,
+ );
+ ctx.re_ui.labeled_combo_box(
+ ui,
+ "Align to",
+ format!("{:?}", depth.align),
+ false,
+ |ui| {
+ for align in
+ depthai::CameraBoardSocket::depth_align_options(
+ )
+ {
+ if align == depthai::CameraBoardSocket::CENTER
+ && !depth.lr_check
+ {
+ continue;
+ }
+ ui.selectable_value(
+ &mut depth.align,
+ align,
+ format!("{align:?}"),
+ );
+ }
+ },
+ );
+ ctx.re_ui.labeled_combo_box(
+ ui,
+ "Median Filter",
+ format!("{:?}", depth.median),
+ false,
+ |ui| {
+ for filter in depthai::DepthMedianFilter::iter() {
+ ui.selectable_value(
+ &mut depth.median,
+ filter,
+ format!("{filter:?}"),
+ );
+ }
+ },
+ );
+ ctx.re_ui.labeled_dragvalue(
+ ui,
+ "LR Threshold",
+ &mut depth.lrc_threshold,
+ 0..=10,
+ );
+ ctx.re_ui.labeled_checkbox(
+ ui,
+ "Extended Disparity",
+ &mut depth.extended_disparity,
+ );
+ ctx.re_ui.labeled_checkbox(
+ ui,
+ "Subpixel Disparity",
+ &mut depth.subpixel_disparity,
+ );
+ ctx.re_ui.labeled_dragvalue(
+ ui,
+ "Sigma",
+ &mut depth.sigma,
+ 0..=65535,
+ );
+ ctx.re_ui.labeled_dragvalue(
+ ui,
+ "Confidence",
+ &mut depth.confidence,
+ 0..=255,
+ );
+ ctx.re_ui.labeled_toggle_switch(
+ ui,
+ "Depth enabled",
+ &mut device_config.depth_enabled,
+ );
+ });
+ },
+ );
+
+ device_config.depth = Some(depth);
+ ctx.depthai_state.modified_device_config.config = device_config.clone();
+ ui.vertical(|ui| {
+ ui.horizontal(|ui| {
+ let only_runtime_configs_changed =
+ depthai::State::only_runtime_configs_changed(
+ &ctx.depthai_state.applied_device_config.config,
+ &device_config,
+ );
+ let apply_enabled = !only_runtime_configs_changed
+ && device_config
+ != ctx.depthai_state.applied_device_config.config
+ && !ctx.depthai_state.selected_device.id.is_empty();
+ if !apply_enabled && only_runtime_configs_changed {
+ ctx.depthai_state
+ .set_device_config(&mut device_config, true);
+ }
+ if ctx.depthai_state.selected_device.id.is_empty() {
+ ctx.depthai_state
+ .set_device_config(&mut device_config, false);
+ }
+
+ ui.add_enabled_ui(apply_enabled, |ui| {
+ ui.scope(|ui| {
+ let mut style = ui.style_mut().clone();
+ if apply_enabled {
+ let color =
+ ctx.re_ui.design_tokens.primary_bg_color;
+ let hover_color =
+ ctx.re_ui.design_tokens.primary_hover_bg_color;
+ style.visuals.widgets.hovered.bg_fill = hover_color;
+ style.visuals.widgets.hovered.weak_bg_fill =
+ hover_color;
+ style.visuals.widgets.inactive.bg_fill = color;
+ style.visuals.widgets.inactive.weak_bg_fill = color;
+ style.visuals.widgets.inactive.fg_stroke.color =
+ egui::Color32::WHITE;
+ style.visuals.widgets.hovered.fg_stroke.color =
+ egui::Color32::WHITE;
+ }
+ style.spacing.button_padding =
+ egui::Vec2::new(24.0, 4.0);
+ ui.set_style(style);
+ if ui
+ .add_sized(
+ [CONFIG_UI_WIDTH, re_ui::ReUi::box_height()],
+ egui::Button::new("Apply"),
+ )
+ .clicked()
+ {
+ ctx.depthai_state
+ .set_device_config(&mut device_config, false);
+ }
+ });
+ });
+ });
+ });
+ });
+ ui.add_space(ui.available_width());
+ });
+ });
+ });
+ }
+}
diff --git a/crates/re_viewer/src/ui/memory_panel.rs b/crates/re_viewer/src/ui/memory_panel.rs
index 796c98977a9d..5c61be30e84a 100644
--- a/crates/re_viewer/src/ui/memory_panel.rs
+++ b/crates/re_viewer/src/ui/memory_panel.rs
@@ -57,7 +57,7 @@ impl MemoryPanel {
});
egui::CentralPanel::default().show_inside(ui, |ui| {
- ui.label("π Rerun Viewer memory use over time");
+ ui.label("π Depthai Viewer memory use over time");
self.plot(ui, limit);
});
}
@@ -69,7 +69,7 @@ impl MemoryPanel {
store_config: &DataStoreConfig,
store_stats: &DataStoreStats,
) {
- ui.strong("Rerun Viewer resource usage");
+ ui.strong("Depthai Viewer resource usage");
ui.separator();
ui.collapsing("CPU Resources", |ui| {
diff --git a/crates/re_viewer/src/ui/mod.rs b/crates/re_viewer/src/ui/mod.rs
index da730d21b5e7..9bb817412638 100644
--- a/crates/re_viewer/src/ui/mod.rs
+++ b/crates/re_viewer/src/ui/mod.rs
@@ -10,14 +10,18 @@ mod space_view_entity_picker;
mod space_view_heuristics;
mod view_bar_chart;
mod view_category;
+mod view_node_graph;
mod view_tensor;
mod view_text;
mod view_time_series;
mod viewport;
+pub(crate) mod bottom_panel;
pub(crate) mod data_ui;
+pub(crate) mod device_settings_panel;
pub(crate) mod memory_panel;
pub(crate) mod selection_panel;
+pub(crate) mod stats_panel;
pub(crate) mod time_panel;
pub mod view_spatial;
@@ -27,7 +31,7 @@ pub mod view_spatial;
use self::scene::SceneQuery;
pub(crate) use self::blueprint::Blueprint;
-pub(crate) use self::space_view::{SpaceView, SpaceViewId};
+pub(crate) use self::space_view::{SpaceView, SpaceViewId, SpaceViewKind};
pub use self::annotations::{Annotations, DefaultColor, MISSING_ANNOTATIONS};
pub use self::data_blueprint::DataBlueprintGroupHandle;
diff --git a/crates/re_viewer/src/ui/selection_history_ui.rs b/crates/re_viewer/src/ui/selection_history_ui.rs
index e91c53966da7..3b20a9f046ac 100644
--- a/crates/re_viewer/src/ui/selection_history_ui.rs
+++ b/crates/re_viewer/src/ui/selection_history_ui.rs
@@ -1,8 +1,8 @@
use egui::RichText;
use re_ui::Command;
-use super::SelectionHistory;
-use crate::{misc::ItemCollection, ui::Blueprint, Item};
+use super::{SelectionHistory, Viewport};
+use crate::{misc::ItemCollection, Item};
// ---
@@ -11,30 +11,31 @@ impl SelectionHistory {
&mut self,
re_ui: &re_ui::ReUi,
ui: &mut egui::Ui,
- blueprint: &Blueprint,
+ viewport: &Viewport,
) -> Option {
- self.control_bar_ui(re_ui, ui, blueprint)
+ self.control_bar_ui(re_ui, ui, viewport)
}
fn control_bar_ui(
&mut self,
re_ui: &re_ui::ReUi,
ui: &mut egui::Ui,
- blueprint: &Blueprint,
+ viewport: &Viewport,
) -> Option {
ui.horizontal_centered(|ui| {
- ui.strong("Selection").on_hover_text("The Selection View contains information and options about the currently selected object(s).");
-
// TODO(emilk): an egui helper for right-to-left
ui.allocate_ui_with_layout(
ui.available_size_before_wrap(),
egui::Layout::right_to_left(egui::Align::Center),
|ui| {
- let next = self.next_button_ui(re_ui, ui, blueprint);
- let prev = self.prev_button_ui(re_ui, ui, blueprint);
+ let next = self.next_button_ui(re_ui, ui, viewport);
+ let prev = self.prev_button_ui(re_ui, ui, viewport);
prev.or(next)
- }).inner
- }).inner
+ },
+ )
+ .inner
+ })
+ .inner
}
#[must_use]
@@ -63,7 +64,7 @@ impl SelectionHistory {
&mut self,
re_ui: &re_ui::ReUi,
ui: &mut egui::Ui,
- blueprint: &Blueprint,
+ viewport: &Viewport,
) -> Option {
// undo selection
if let Some(previous) = self.previous() {
@@ -75,13 +76,13 @@ impl SelectionHistory {
\n\
Right-click for more.",
Command::SelectionPrevious.format_shortcut_tooltip_suffix(ui.ctx()),
- item_collection_to_string(blueprint, &previous.selection),
+ item_collection_to_string(viewport, &previous.selection),
));
let response = response.context_menu(|ui| {
// undo: newest on top, oldest on bottom
for i in (0..self.current).rev() {
- self.history_item_ui(blueprint, ui, i);
+ self.history_item_ui(viewport, ui, i);
}
});
@@ -106,7 +107,7 @@ impl SelectionHistory {
&mut self,
re_ui: &re_ui::ReUi,
ui: &mut egui::Ui,
- blueprint: &Blueprint,
+ viewport: &Viewport,
) -> Option {
// redo selection
if let Some(next) = self.next() {
@@ -118,13 +119,13 @@ impl SelectionHistory {
\n\
Right-click for more.",
Command::SelectionNext.format_shortcut_tooltip_suffix(ui.ctx()),
- item_collection_to_string(blueprint, &next.selection),
+ item_collection_to_string(viewport, &next.selection),
));
let response = response.context_menu(|ui| {
// redo: oldest on top, most recent on bottom
for i in (self.current + 1)..self.stack.len() {
- self.history_item_ui(blueprint, ui, i);
+ self.history_item_ui(viewport, ui, i);
}
});
@@ -145,12 +146,12 @@ impl SelectionHistory {
None
}
- fn history_item_ui(&mut self, blueprint: &Blueprint, ui: &mut egui::Ui, index: usize) {
+ fn history_item_ui(&mut self, viewport: &Viewport, ui: &mut egui::Ui, index: usize) {
if let Some(sel) = self.stack.get(index) {
ui.horizontal(|ui| {
{
// borrow checker workaround
- let sel = item_collection_to_string(blueprint, sel);
+ let sel = item_collection_to_string(viewport, sel);
if ui.selectable_value(&mut self.current, index, sel).clicked() {
ui.close_menu();
}
@@ -169,10 +170,10 @@ fn item_kind_ui(ui: &mut egui::Ui, sel: &Item) {
ui.weak(RichText::new(format!("({})", sel.kind())));
}
-fn item_collection_to_string(blueprint: &Blueprint, items: &ItemCollection) -> String {
+fn item_collection_to_string(viewport: &Viewport, items: &ItemCollection) -> String {
assert!(!items.is_empty()); // history never contains empty selections.
if items.len() == 1 {
- item_to_string(blueprint, items.iter().next().unwrap())
+ item_to_string(viewport, items.iter().next().unwrap())
} else if let Some(kind) = items.are_all_same_kind() {
format!("{}x {}s", items.len(), kind)
} else {
@@ -180,10 +181,10 @@ fn item_collection_to_string(blueprint: &Blueprint, items: &ItemCollection) -> S
}
}
-fn item_to_string(blueprint: &Blueprint, item: &Item) -> String {
+fn item_to_string(viewport: &Viewport, item: &Item) -> String {
match item {
Item::SpaceView(sid) => {
- if let Some(space_view) = blueprint.viewport.space_view(sid) {
+ if let Some(space_view) = viewport.space_view(sid) {
space_view.display_name.clone()
} else {
"".to_owned()
@@ -191,7 +192,7 @@ fn item_to_string(blueprint: &Blueprint, item: &Item) -> String {
}
Item::InstancePath(_, entity_path) => entity_path.to_string(),
Item::DataBlueprintGroup(sid, handle) => {
- if let Some(space_view) = blueprint.viewport.space_view(sid) {
+ if let Some(space_view) = viewport.space_view(sid) {
if let Some(group) = space_view.data_blueprint.group(*handle) {
group.display_name.clone()
} else {
diff --git a/crates/re_viewer/src/ui/selection_panel.rs b/crates/re_viewer/src/ui/selection_panel.rs
index f84b282b4b6a..1b28d6785db5 100644
--- a/crates/re_viewer/src/ui/selection_panel.rs
+++ b/crates/re_viewer/src/ui/selection_panel.rs
@@ -1,4 +1,6 @@
-use egui::NumExt as _;
+use egui::{
+ NumExt as _,
+};
use re_data_store::{
query_latest_single, ColorMapper, Colormap, EditableAutoValue, EntityPath, EntityProperties,
};
@@ -8,81 +10,34 @@ use re_log_types::{
};
use crate::{
- ui::{view_spatial::SpatialNavigationMode, Blueprint},
- Item, UiVerbosity, ViewerContext,
+ ui::view_spatial::SpatialNavigationMode, Item, UiVerbosity, ViewerContext,
};
-use super::{data_ui::DataUi, space_view::ViewState};
+use super::{data_ui::DataUi, space_view::ViewState, Viewport};
// ---
/// The "Selection View" side-bar.
-#[derive(Default, serde::Deserialize, serde::Serialize)]
+#[derive(serde::Deserialize, serde::Serialize, Default)]
#[serde(default)]
pub(crate) struct SelectionPanel {}
impl SelectionPanel {
- #[allow(clippy::unused_self)]
- pub fn show_panel(
- &mut self,
- ctx: &mut ViewerContext<'_>,
- ui: &mut egui::Ui,
- blueprint: &mut Blueprint,
- ) {
- let screen_width = ui.ctx().screen_rect().width();
-
- let panel = egui::SidePanel::right("selection_view")
- .min_width(120.0)
- .default_width((0.45 * screen_width).min(250.0).round())
- .max_width((0.65 * screen_width).round())
- .resizable(true)
- .frame(egui::Frame {
- fill: ui.style().visuals.panel_fill,
- ..Default::default()
+ pub fn show_panel(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui, viewport: &mut Viewport) {
+ egui::ScrollArea::both()
+ .auto_shrink([true; 2])
+ .show(ui, |ui| {
+ egui::Frame {
+ inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()),
+ ..Default::default()
+ }
+ .show(ui, |ui| {
+ Self::contents(ui, ctx, viewport);
+ });
});
-
- panel.show_animated_inside(
- ui,
- blueprint.selection_panel_expanded,
- |ui: &mut egui::Ui| {
- egui::TopBottomPanel::top("selection_panel_title_bar")
- .exact_height(re_ui::ReUi::title_bar_height())
- .frame(egui::Frame {
- inner_margin: egui::Margin::symmetric(re_ui::ReUi::view_padding(), 0.0),
- ..Default::default()
- })
- .show_inside(ui, |ui| {
- if let Some(selection) = ctx
- .rec_cfg
- .selection_state
- .selection_ui(ctx.re_ui, ui, blueprint)
- {
- ctx.set_multi_selection(selection.iter().cloned());
- }
- });
-
- egui::ScrollArea::both()
- .auto_shrink([false; 2])
- .show(ui, |ui| {
- egui::Frame {
- inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()),
- ..Default::default()
- }
- .show(ui, |ui| {
- self.contents(ui, ctx, blueprint);
- });
- });
- },
- );
}
- #[allow(clippy::unused_self)]
- fn contents(
- &mut self,
- ui: &mut egui::Ui,
- ctx: &mut ViewerContext<'_>,
- blueprint: &mut Blueprint,
- ) {
+ fn contents(ui: &mut egui::Ui, ctx: &mut ViewerContext<'_>, viewport: &mut Viewport) {
crate::profile_function!();
let query = ctx.current_query();
@@ -95,7 +50,7 @@ impl SelectionPanel {
let selection = ctx.selection().to_vec();
for (i, item) in selection.iter().enumerate() {
ui.push_id(i, |ui| {
- what_is_selected_ui(ui, ctx, blueprint, item);
+ what_is_selected_ui(ui, ctx, viewport, item);
if has_data_section(item) {
ctx.re_ui.large_collapsing_header(ui, "Data", true, |ui| {
@@ -105,7 +60,7 @@ impl SelectionPanel {
ctx.re_ui
.large_collapsing_header(ui, "Blueprint", true, |ui| {
- blueprint_ui(ui, ctx, blueprint, item);
+ blueprint_ui(ui, ctx, viewport, item);
});
if i + 1 < num_selections {
@@ -115,6 +70,27 @@ impl SelectionPanel {
});
}
}
+
+ pub fn selection_panel_options_ui(
+ ctx: &mut ViewerContext<'_>,
+ ui: &mut egui::Ui,
+ viewport: &mut Viewport,
+ tab_bar_rect: egui::Rect,
+ ) {
+ let tab_bar_rect = tab_bar_rect.shrink2(egui::vec2(4.0, 0.0)); // Add some side margin outside the frame
+
+ ui.allocate_ui_at_rect(tab_bar_rect, |ui| {
+ ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
+ if let Some(selection) = ctx
+ .rec_cfg
+ .selection_state
+ .selection_ui(ctx.re_ui, ui, viewport)
+ {
+ ctx.set_multi_selection(selection.iter().cloned());
+ }
+ });
+ });
+ }
}
fn has_data_section(item: &Item) -> bool {
@@ -129,7 +105,7 @@ fn has_data_section(item: &Item) -> bool {
pub fn what_is_selected_ui(
ui: &mut egui::Ui,
ctx: &mut ViewerContext<'_>,
- blueprint: &mut Blueprint,
+ viewport: &mut Viewport,
item: &Item,
) {
match item {
@@ -151,7 +127,7 @@ pub fn what_is_selected_ui(
});
}
Item::SpaceView(space_view_id) => {
- if let Some(space_view) = blueprint.viewport.space_view_mut(space_view_id) {
+ if let Some(space_view) = viewport.space_view_mut(space_view_id) {
ui.horizontal(|ui| {
ui.label("Space view:");
ui.text_edit_singleline(&mut space_view.display_name);
@@ -169,7 +145,7 @@ pub fn what_is_selected_ui(
ui.end_row();
if let Some(space_view_id) = space_view_id {
- if let Some(space_view) = blueprint.viewport.space_view_mut(space_view_id) {
+ if let Some(space_view) = viewport.space_view_mut(space_view_id) {
ui.label("in Space View:");
ctx.space_view_button(ui, space_view);
ui.end_row();
@@ -178,7 +154,7 @@ pub fn what_is_selected_ui(
});
}
Item::DataBlueprintGroup(space_view_id, data_blueprint_group_handle) => {
- if let Some(space_view) = blueprint.viewport.space_view_mut(space_view_id) {
+ if let Some(space_view) = viewport.space_view_mut(space_view_id) {
if let Some(group) = space_view
.data_blueprint
.group_mut(*data_blueprint_group_handle)
@@ -238,12 +214,12 @@ impl DataUi for Item {
fn blueprint_ui(
ui: &mut egui::Ui,
ctx: &mut ViewerContext<'_>,
- blueprint: &mut Blueprint,
+ viewport: &mut Viewport,
item: &Item,
) {
match item {
Item::ComponentPath(component_path) => {
- list_existing_data_blueprints(ui, ctx, component_path.entity_path(), blueprint);
+ list_existing_data_blueprints(ui, ctx, component_path.entity_path(), viewport);
}
Item::SpaceView(space_view_id) => {
@@ -253,8 +229,7 @@ fn blueprint_ui(
.on_hover_text("Manually add or remove entities from the Space View.")
.clicked()
{
- blueprint
- .viewport
+ viewport
.show_add_remove_entities_window(*space_view_id);
}
@@ -263,25 +238,25 @@ fn blueprint_ui(
.on_hover_text("Create an exact duplicate of this Space View including all blueprint settings")
.clicked()
{
- if let Some(space_view) = blueprint.viewport.space_view(space_view_id) {
+ if let Some(space_view) = viewport.space_view(space_view_id) {
let mut new_space_view = space_view.clone();
new_space_view.id = super::SpaceViewId::random();
- blueprint.viewport.add_space_view(new_space_view);
- blueprint.viewport.mark_user_interaction();
+ viewport.add_space_view(new_space_view);
+ viewport.mark_user_interaction();
}
}
});
ui.add_space(ui.spacing().item_spacing.y);
- if let Some(space_view) = blueprint.viewport.space_view_mut(space_view_id) {
+ if let Some(space_view) = viewport.space_view_mut(space_view_id) {
space_view.selection_ui(ctx, ui);
}
}
Item::InstancePath(space_view_id, instance_path) => {
- if let Some(space_view) = space_view_id
- .and_then(|space_view_id| blueprint.viewport.space_view_mut(&space_view_id))
+ if let Some(space_view) =
+ space_view_id.and_then(|space_view_id| viewport.space_view_mut(&space_view_id))
{
if instance_path.instance_key.is_specific() {
ui.horizontal(|ui| {
@@ -303,12 +278,12 @@ fn blueprint_ui(
data_blueprint.set(instance_path.entity_path.clone(), props);
}
} else {
- list_existing_data_blueprints(ui, ctx, &instance_path.entity_path, blueprint);
+ list_existing_data_blueprints(ui, ctx, &instance_path.entity_path, viewport);
}
}
Item::DataBlueprintGroup(space_view_id, data_blueprint_group_handle) => {
- if let Some(space_view) = blueprint.viewport.space_view_mut(space_view_id) {
+ if let Some(space_view) = viewport.space_view_mut(space_view_id) {
if let Some(group) = space_view
.data_blueprint
.group_mut(*data_blueprint_group_handle)
@@ -332,11 +307,9 @@ fn list_existing_data_blueprints(
ui: &mut egui::Ui,
ctx: &mut ViewerContext<'_>,
entity_path: &EntityPath,
- blueprint: &Blueprint,
+ viewport: &Viewport,
) {
- let space_views_with_path = blueprint
- .viewport
- .space_views_containing_entity_path(entity_path);
+ let space_views_with_path = viewport.space_views_containing_entity_path(entity_path);
if space_views_with_path.is_empty() {
ui.weak("(Not shown in any Space View)");
@@ -346,7 +319,7 @@ fn list_existing_data_blueprints(
ui.indent("list of data blueprints indent", |ui| {
for space_view_id in &space_views_with_path {
- if let Some(space_view) = blueprint.viewport.space_view(space_view_id) {
+ if let Some(space_view) = viewport.space_view(space_view_id) {
ctx.entity_path_button_to(
ui,
Some(*space_view_id),
@@ -409,35 +382,98 @@ fn entity_props_ui(
});
}
-fn colormap_props_ui(ui: &mut egui::Ui, entity_props: &mut EntityProperties) {
- let current = *entity_props.color_mapper.get();
+fn colormap_props_ui(
+ ctx: &mut ViewerContext<'_>,
+ ui: &mut egui::Ui,
+ entity_path: &EntityPath,
+ entity_props: &mut EntityProperties,
+) {
+ // Color mapping picker
+ {
+ let current = *entity_props.color_mapper.get();
+ ui.label("Color map");
+ egui::ComboBox::from_id_source("depth_color_mapper")
+ .selected_text(current.to_string())
+ .show_ui(ui, |ui| {
+ ui.style_mut().wrap = Some(false);
+ ui.set_min_width(64.0);
+
+ let mut add_label = |proposed| {
+ if ui
+ .selectable_label(current == proposed, proposed.to_string())
+ .clicked()
+ {
+ entity_props.color_mapper = EditableAutoValue::Auto(proposed);
+ }
+ };
+
+ add_label(ColorMapper::Colormap(Colormap::Grayscale));
+ add_label(ColorMapper::Colormap(Colormap::Turbo));
+ add_label(ColorMapper::Colormap(Colormap::Viridis));
+ add_label(ColorMapper::Colormap(Colormap::Plasma));
+ add_label(ColorMapper::Colormap(Colormap::Magma));
+ add_label(ColorMapper::Colormap(Colormap::Inferno));
+ add_label(ColorMapper::AlbedoTexture);
+ });
+ ui.end_row();
+ }
+
+ if *entity_props.color_mapper.get() != ColorMapper::AlbedoTexture {
+ return;
+ }
- ui.label("Color map");
- egui::ComboBox::from_id_source("color_mapper")
- .selected_text(current.to_string())
- .show_ui(ui, |ui| {
+ // Albedo texture picker
+ if let Some(tree) = entity_path
+ .parent()
+ .and_then(|path| ctx.log_db.entity_db.tree.subtree(&path))
+ {
+ let query = ctx.current_query();
+ let current = entity_props.albedo_texture.clone();
+
+ ui.label("Albedo texture");
+
+ let mut combo = egui::ComboBox::from_id_source("depth_color_texture");
+ if let Some(current) = current.as_ref() {
+ combo = combo.selected_text(current.to_string());
+ } else {
+ // Select the first image-shaped tensor we find
+ // tree.visit_children_recursively(&mut |ent_path| {
+ // if entity_props.albedo_texture.is_some() {
+ // return;
+ // }
+ // let Some(tensor) =
+ // query_latest_single::(&ctx.log_db.entity_db, ent_path, &query) else {
+ // return;
+ // };
+ // if tensor.is_shaped_like_an_image() {
+ // entity_props.albedo_texture = Some(ent_path.clone());
+ // }
+ // });
+ }
+
+ combo.show_ui(ui, |ui| {
ui.style_mut().wrap = Some(false);
ui.set_min_width(64.0);
- // TODO(cmc): that is not ideal but I don't want to import yet another proc-macro...
- let mut add_label = |proposed| {
- if ui
- .selectable_label(current == proposed, proposed.to_string())
- .clicked()
+ tree.visit_children_recursively(&mut |ent_path| {
+ let Some(tensor) = query_latest_single::(
+ &ctx.log_db.entity_db,
+ ent_path,
+ &query,
+ ) else {
+ return;
+ };
+
+ if tensor.is_shaped_like_an_image()
+ && ui
+ .selectable_label(current.as_ref() == Some(ent_path), ent_path.to_string())
+ .clicked()
{
- entity_props.color_mapper = EditableAutoValue::Auto(proposed);
+ entity_props.albedo_texture = Some(ent_path.clone());
}
- };
-
- add_label(ColorMapper::Colormap(Colormap::Grayscale));
- add_label(ColorMapper::Colormap(Colormap::Turbo));
- add_label(ColorMapper::Colormap(Colormap::Viridis));
- add_label(ColorMapper::Colormap(Colormap::Plasma));
- add_label(ColorMapper::Colormap(Colormap::Magma));
- add_label(ColorMapper::Colormap(Colormap::Inferno));
+ });
});
-
- ui.end_row();
+ }
}
fn pinhole_props_ui(
@@ -510,9 +546,25 @@ fn depth_props_ui(
backproject_radius_scale_ui(ui, &mut entity_props.backproject_radius_scale);
+ ui.label("Backproject radius scale");
+ let mut radius_scale = *entity_props.backproject_radius_scale.get();
+ let speed = (radius_scale * 0.001).at_least(0.001);
+ if ui
+ .add(
+ egui::DragValue::new(&mut radius_scale)
+ .clamp_range(0.0..=1.0e8)
+ .speed(speed),
+ )
+ .on_hover_text("Scales the radii of the points in the backprojected point cloud")
+ .changed()
+ {
+ entity_props.backproject_radius_scale = EditableAutoValue::UserEdited(radius_scale);
+ }
+ ui.end_row();
+
// TODO(cmc): This should apply to the depth map entity as a whole, but for that we
// need to get the current hardcoded colormapping out of the image cache first.
- colormap_props_ui(ui, entity_props);
+ colormap_props_ui(ctx, ui, entity_path, entity_props);
}
Some(())
@@ -522,6 +574,7 @@ fn depth_from_world_scale_ui(ui: &mut egui::Ui, property: &mut EditableAutoValue
ui.label("Backproject meter");
let mut value = *property.get();
let speed = (value * 0.05).at_least(0.01);
+
let response = ui
.add(
egui::DragValue::new(&mut value)
@@ -563,4 +616,4 @@ fn backproject_radius_scale_ui(ui: &mut egui::Ui, property: &mut EditableAutoVal
*property = EditableAutoValue::UserEdited(value);
}
ui.end_row();
-}
+}
\ No newline at end of file
diff --git a/crates/re_viewer/src/ui/space_view.rs b/crates/re_viewer/src/ui/space_view.rs
index 016491107a31..365c151cfd6b 100644
--- a/crates/re_viewer/src/ui/space_view.rs
+++ b/crates/re_viewer/src/ui/space_view.rs
@@ -3,14 +3,15 @@ use re_data_store::{EntityPath, EntityPropertyMap, EntityTree, InstancePath, Tim
use re_renderer::{GpuReadbackIdentifier, ScreenshotProcessor};
use crate::{
+ depthai::depthai,
misc::{space_info::SpaceInfoCollection, SpaceViewHighlights, TransformCache, ViewerContext},
ui::view_category::categorize_entity_path,
};
use super::{
data_blueprint::DataBlueprintTree, space_view_heuristics::default_queried_entities,
- view_bar_chart, view_category::ViewCategory, view_spatial, view_tensor, view_text,
- view_time_series,
+ view_bar_chart, view_category::ViewCategory, view_node_graph, view_spatial, view_tensor,
+ view_text, view_time_series,
};
// ----------------------------------------------------------------------------
@@ -21,6 +22,14 @@ use super::{
)]
pub struct SpaceViewId(uuid::Uuid);
+#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
+pub enum SpaceViewKind {
+ Data,
+ Stats,
+ Config,
+ Selection,
+}
+
impl SpaceViewId {
pub fn random() -> Self {
Self(uuid::Uuid::new_v4())
@@ -66,6 +75,8 @@ pub struct SpaceView {
/// True if the user is expected to add entities themselves. False otherwise.
pub entities_determined_by_user: bool,
+
+ pub is_depthai_spaceview: bool,
}
impl SpaceView {
@@ -78,11 +89,31 @@ impl SpaceView {
// this led to somewhat confusing and inconsistent behavior. See https://github.com/rerun-io/rerun/issues/1220
// Spaces are now always named after the final element of the space-path (or the root), independent of the
// query entities.
- let display_name = if let Some(name) = space_path.iter().last() {
- name.to_string()
- } else {
- // Include category name in the display for root paths because they look a tad bit too short otherwise.
- format!("/ ({category})")
+ let mut is_depthai_spaceview = true;
+ let display_name = match space_path {
+ ep if ep.hash() == depthai::entity_paths::RGB_PINHOLE_CAMERA.hash() => {
+ "Color camera (2D)".into()
+ }
+ ep if ep.hash() == depthai::entity_paths::COLOR_CAM_3D.hash() => {
+ "Color camera (3D)".into()
+ }
+ ep if ep.hash() == depthai::entity_paths::RIGHT_PINHOLE_CAMERA.hash() => {
+ "Right mono camera (2D)".into()
+ }
+ ep if ep.hash() == depthai::entity_paths::LEFT_PINHOLE_CAMERA.hash() => {
+ "Left mono camera (2D)".into()
+ }
+ ep if ep.hash() == depthai::entity_paths::MONO_CAM_3D.hash() => {
+ "Mono cameras (3D)".into()
+ }
+ _ => {
+ is_depthai_spaceview = false;
+ if let Some(entity_path_part) = space_path.iter().last() {
+ entity_path_part.to_string()
+ } else {
+ format!("/ ({category})")
+ }
+ }
};
let mut data_blueprint_tree = DataBlueprintTree::default();
@@ -97,6 +128,7 @@ impl SpaceView {
view_state: ViewState::default(),
category,
entities_determined_by_user: false,
+ is_depthai_spaceview,
}
}
@@ -191,6 +223,7 @@ impl SpaceView {
}
}
}
+ ViewCategory::NodeGraph => self.view_state.state_node_graph.selection_ui(ctx.re_ui, ui),
}
}
@@ -262,6 +295,11 @@ impl SpaceView {
scene.load(ctx, &query);
self.view_state.ui_tensor(ctx, ui, &scene);
}
+ ViewCategory::NodeGraph => {
+ let mut scene = view_node_graph::SceneNodeGraph::default();
+ scene.load(ctx, &query);
+ self.view_state.ui_node_graph(ctx, ui, &scene);
+ }
};
}
@@ -324,6 +362,7 @@ pub struct ViewState {
state_bar_chart: view_bar_chart::BarChartState,
pub state_spatial: view_spatial::ViewSpatialState,
state_tensors: ahash::HashMap,
+ state_node_graph: view_node_graph::ViewNodeGraphState,
}
impl ViewState {
@@ -410,6 +449,21 @@ impl ViewState {
});
}
+ fn ui_node_graph(
+ &mut self,
+ ctx: &mut ViewerContext<'_>,
+ ui: &mut egui::Ui,
+ scene: &view_node_graph::SceneNodeGraph,
+ ) {
+ egui::Frame {
+ inner_margin: re_ui::ReUi::view_padding().into(),
+ ..egui::Frame::default()
+ }
+ .show(ui, |ui| {
+ view_node_graph::view_node_graph(ctx, ui, &mut self.state_node_graph, scene)
+ });
+ }
+
fn ui_bar_chart(
&mut self,
ctx: &mut ViewerContext<'_>,
diff --git a/crates/re_viewer/src/ui/space_view_heuristics.rs b/crates/re_viewer/src/ui/space_view_heuristics.rs
index df1da9956907..88181e47cd75 100644
--- a/crates/re_viewer/src/ui/space_view_heuristics.rs
+++ b/crates/re_viewer/src/ui/space_view_heuristics.rs
@@ -4,8 +4,8 @@ use ahash::HashMap;
use itertools::Itertools;
use nohash_hasher::IntSet;
use re_arrow_store::{DataStore, LatestAtQuery, Timeline};
-use re_data_store::{log_db::EntityDb, query_latest_single, ComponentName, EntityPath};
-use re_log_types::{component_types::Tensor, Component};
+use re_data_store::{log_db::EntityDb, query_latest_single, ComponentName, EntityPath, EntityTree};
+use re_log_types::{component_types::Tensor, Component, EntityPathPart};
use crate::{
misc::{space_info::SpaceInfoCollection, ViewerContext},
@@ -22,8 +22,24 @@ pub fn all_possible_space_views(
crate::profile_function!();
// Everything with a SpaceInfo is a candidate (that is root + whenever there is a transform),
- // as well as all direct descendants of the root.
- let root_children = &ctx.log_db.entity_db.tree.children;
+ // as well as all direct descendants of the root that have some messages.
+ let root_children = &ctx
+ .log_db
+ .entity_db
+ .tree
+ .children
+ .iter()
+ .filter(|(_k, v)| {
+ let timelines = v.prefix_times.timelines();
+ let mut total_msgs = 0;
+ for timeline in timelines {
+ if let Some(hist) = v.prefix_times.get(timeline) {
+ total_msgs += hist.total_count();
+ }
+ }
+ total_msgs != 0
+ })
+ .collect::>();
let candidate_space_paths = spaces_info
.iter()
.map(|info| &info.path)
@@ -111,6 +127,11 @@ fn is_interesting_space_view_not_at_root(
false
}
+/// Function to create the non data space views. Configuration panel and blueprint panel.
+fn create_blueprint_and_selection_space_view() {
+ // let mut config_viewport =
+}
+
/// List out all space views we generate by default for the available data.
pub fn default_created_space_views(
ctx: &ViewerContext<'_>,
@@ -256,6 +277,8 @@ fn is_default_added_to_space_view(
re_log_types::component_types::InstanceKey::name(),
re_log_types::component_types::KeypointId::name(),
DataStore::insert_id_key(),
+ re_log_types::ImuData::name(), // Separate plotting view for IMU data.
+ re_log_types::XlinkStats::name(), // Separate plotting view for XLink stats.
];
entity_path.is_descendant_of(space_path)
@@ -293,7 +316,6 @@ pub fn default_queried_entities(
.cloned(),
);
});
-
entities
}
diff --git a/crates/re_viewer/src/ui/stats_panel.rs b/crates/re_viewer/src/ui/stats_panel.rs
new file mode 100644
index 000000000000..e05199f5d8bf
--- /dev/null
+++ b/crates/re_viewer/src/ui/stats_panel.rs
@@ -0,0 +1,335 @@
+use egui::{
+ emath::History,
+ plot::{Line, Plot, PlotPoints},
+};
+use egui_dock::{TabViewer, Tree};
+use itertools::Itertools;
+use re_arrow_store::{LatestAtQuery, TimeInt, Timeline};
+use re_log_types::{
+ component_types::{ImuData, XlinkStats},
+ Component,
+};
+use strum::{EnumIter, IntoEnumIterator};
+
+use crate::{depthai::depthai, misc::ViewerContext};
+
+use super::Viewport;
+
+pub struct StatsPanel {}
+
+impl StatsPanel {
+ pub fn show_panel(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui, state: &mut StatsPanelState) {
+ let mut tree = state.tree.clone(); // Have to clone to avoid borrowing issue
+ state.imu_tab_visible = false; // Check every frame if the IMU tab is visible
+ egui_dock::DockArea::new(&mut tree)
+ .id(egui::Id::new("stats_panel"))
+ .style(re_ui::egui_dock_style(ui.style()))
+ .show_inside(ui, &mut StatsTabs { ctx, state });
+ state.tree = tree;
+ }
+
+ pub fn stats_panel_options_ui(
+ ctx: &mut ViewerContext<'_>,
+ ui: &mut egui::Ui,
+ viewport: &mut Viewport,
+ tab_bar_rect: egui::Rect,
+ ) {
+ }
+}
+
+#[derive(Debug, Copy, Clone, EnumIter, PartialEq, Eq)]
+enum ImuTabKind {
+ Accel,
+ Gyro,
+ Mag,
+}
+
+#[derive(Debug, Copy, Clone, EnumIter, PartialEq, Eq)]
+enum Xyz {
+ X,
+ Y,
+ Z,
+}
+
+#[derive(Debug, Copy, Clone, EnumIter, PartialEq, Eq)]
+pub enum StatTabKind {
+ Imu,
+ Xlink,
+}
+
+pub struct StatsPanelState {
+ tree: Tree,
+ accel_history: History<[f32; 3]>,
+ gyro_history: History<[f32; 3]>,
+ magnetometer_history: History<[f32; 3]>,
+ start_time: instant::Instant, // Time elapsed from spawning the app
+ imu_tab_visible: bool, // Used to subscribe and unsubscribe from the IMU data
+ xlink_stats_history: History<[f64; 4]>, // [MB written in last time frame, MB read in last time frame, MB written total, MB read total]
+ avg_xlink_stats_plot_history: History<[f64; 2]>, // [Avg MB written, Avg MB read]
+}
+
+impl Default for StatsPanelState {
+ fn default() -> Self {
+ Self {
+ tree: Tree::new(StatTabKind::iter().collect_vec()),
+ accel_history: History::new(0..1000, 5.0),
+ gyro_history: History::new(0..1000, 5.0),
+ magnetometer_history: History::new(0..1000, 5.0),
+ start_time: instant::Instant::now(),
+ imu_tab_visible: false,
+ xlink_stats_history: History::new(0..1000, 1.0),
+ avg_xlink_stats_plot_history: History::new(0..1000, 5.0),
+ }
+ }
+}
+
+impl StatsPanelState {
+ pub fn update(&mut self, ctx: &mut ViewerContext<'_>) {
+ self.update_imu(ctx);
+ self.update_xlink(ctx);
+ }
+
+ /// Push new data into the history buffers.
+ fn update_imu(&mut self, ctx: &mut ViewerContext<'_>) {
+ self.update_imu_subscription(ctx);
+ let now = self.start_time.elapsed().as_secs_f64();
+ let imu_entity_path = &ImuData::entity_path();
+ if let Ok(latest) = re_query::query_entity_with_primary::(
+ &ctx.log_db.entity_db.data_store,
+ &LatestAtQuery::new(Timeline::log_time(), TimeInt::MAX),
+ imu_entity_path,
+ &[ImuData::name()],
+ ) {
+ let _ = latest.visit1(|_inst, imu_data| {
+ self.accel_history
+ .add(now, [imu_data.accel.x, imu_data.accel.y, imu_data.accel.z]);
+ self.gyro_history
+ .add(now, [imu_data.gyro.x, imu_data.gyro.y, imu_data.gyro.z]);
+ if let Some(mag) = imu_data.mag {
+ self.magnetometer_history.add(now, [mag.x, mag.y, mag.z]);
+ }
+ });
+ }
+ }
+
+ fn update_xlink(&mut self, ctx: &mut ViewerContext<'_>) {
+ let now = self.start_time.elapsed().as_secs_f64();
+ let entity_path = &XlinkStats::entity_path();
+ if let Ok(latest) = re_query::query_entity_with_primary::(
+ &ctx.log_db.entity_db.data_store,
+ &LatestAtQuery::new(Timeline::log_time(), TimeInt::MAX),
+ entity_path,
+ &[XlinkStats::name()],
+ ) {
+ let _ = latest.visit1(|_inst, xlink_stats| {
+ let (mut written, mut read) = (
+ (xlink_stats.bytes_written / 1e6 as i64) as f64,
+ (xlink_stats.bytes_read / 1e6 as i64) as f64,
+ );
+ if let Some((time, [_, _, total_written, total_read])) =
+ self.xlink_stats_history.iter().last()
+ {
+ written = (written - total_written) / (now - time);
+ read = (read - total_read) / (now - time);
+ }
+
+ self.xlink_stats_history.add(
+ now,
+ [
+ written,
+ read,
+ (xlink_stats.bytes_written / 1e6 as i64) as f64,
+ (xlink_stats.bytes_read / 1e6 as i64) as f64,
+ ],
+ );
+ self.avg_xlink_stats_plot_history.add(
+ now,
+ [
+ self.xlink_stats_history
+ .iter()
+ .map(|(_, [written, _, _, _])| written)
+ .sum::()
+ / self.xlink_stats_history.len() as f64,
+ self.xlink_stats_history
+ .iter()
+ .map(|(_, [_, read, _, _])| read)
+ .sum::()
+ / self.xlink_stats_history.len() as f64,
+ ],
+ );
+ });
+ }
+ }
+
+ pub fn update_imu_subscription(&mut self, ctx: &mut ViewerContext<'_>) {
+ let unsub = !self.imu_tab_visible
+ && ctx
+ .depthai_state
+ .subscriptions
+ .contains(&depthai::ChannelId::ImuData);
+ if unsub {
+ let subs = ctx
+ .depthai_state
+ .subscriptions
+ .iter()
+ .filter_map(|x| {
+ if x != &depthai::ChannelId::ImuData {
+ return Some(x.clone());
+ } else {
+ return None;
+ }
+ })
+ .collect_vec();
+ ctx.depthai_state.set_subscriptions(&subs);
+ self.accel_history.clear();
+ self.gyro_history.clear();
+ self.magnetometer_history.clear();
+ } else if self.imu_tab_visible
+ && !ctx
+ .depthai_state
+ .subscriptions
+ .contains(&depthai::ChannelId::ImuData)
+ {
+ let mut subs = ctx.depthai_state.subscriptions.clone();
+ subs.push(depthai::ChannelId::ImuData);
+ ctx.depthai_state.set_subscriptions(&subs);
+ }
+ }
+}
+
+struct StatsTabs<'a, 'b> {
+ ctx: &'a mut ViewerContext<'b>,
+ state: &'a mut StatsPanelState,
+}
+
+impl<'a, 'b> StatsTabs<'a, 'b> {
+ fn imu_ui(&mut self, ui: &mut egui::Ui) {
+ let imu_entity_path = &ImuData::entity_path();
+ egui::ScrollArea::both().show(ui, |ui| {
+ egui::Frame {
+ inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()),
+ ..Default::default()
+ }
+ .show(ui, |ui| {
+ let max_width = ui.available_width();
+ for kind in ImuTabKind::iter() {
+ self.xyz_plot_ui(ui, kind, max_width);
+ }
+ });
+ });
+ }
+
+ fn xlink_ui(&mut self, ui: &mut egui::Ui) {
+ ui.vertical(|ui| {
+ egui::Frame {
+ inner_margin: egui::Margin::same(re_ui::ReUi::view_padding()),
+ ..Default::default()
+ }
+ .show(ui, |ui| {
+ let (history, display_name, unit) = (
+ &mut self.state.avg_xlink_stats_plot_history,
+ "XLink throughput",
+ "",
+ );
+ let Some(latest) = history.latest() else {
+ ui.label(format!("No {display_name} data yet"));
+ return;
+ };
+ ui.label(format!(
+ "{display_name}: avg. Sent from device {:.2} MB/s, avg. Sent to Device: {:.2} MB/s",
+ latest[0], latest[1]
+ ));
+ Plot::new(display_name).show(ui, |plot_ui| {
+ plot_ui.line(
+ Line::new(PlotPoints::new(
+ history
+ .iter()
+ .map(|(t, [written, _])| [t, written])
+ .collect_vec(),
+ ))
+ .color(egui::Color32::BLUE),
+ );
+ plot_ui.line(
+ Line::new(PlotPoints::new(
+ history.iter().map(|(t, [_, read])| [t, read]).collect_vec(),
+ ))
+ .color(egui::Color32::RED),
+ );
+ });
+ });
+ });
+ }
+
+ fn xyz_plot_ui(&mut self, ui: &mut egui::Ui, kind: ImuTabKind, max_width: f32) {
+ ui.vertical(|ui| {
+ let (history, display_name, unit) = match kind {
+ ImuTabKind::Accel => (&mut self.state.accel_history, "Accelerometer", "(m/s^2)"),
+ ImuTabKind::Gyro => (&mut self.state.gyro_history, "Gyroscope", "(rad/s)"),
+ ImuTabKind::Mag => (&mut self.state.magnetometer_history, "Magnetometer", "(uT)"),
+ };
+ let Some(latest) = history.latest() else {
+ ui.label(format!("No {display_name} data yet"));
+ return;
+ };
+ ui.label(display_name);
+ ui.add_sized([max_width, 150.0], |ui: &mut egui::Ui| {
+ ui.horizontal(|ui| {
+ ui.add_sized([max_width, 150.0], |ui: &mut egui::Ui| {
+ Plot::new(format!("{kind:?}"))
+ .allow_drag(false)
+ .allow_zoom(false)
+ .allow_scroll(false)
+ .show(ui, |plot_ui| {
+ for axis in Xyz::iter() {
+ plot_ui.line(
+ Line::new(PlotPoints::new(
+ (*history)
+ .iter()
+ .map(|(t, v)| [t, v[axis as usize].into()])
+ .collect_vec(),
+ ))
+ .color(match axis
+ {
+ Xyz::X => egui::Color32::RED,
+ Xyz::Y => egui::Color32::GREEN,
+ Xyz::Z => egui::Color32::BLUE,
+ }),
+ );
+ }
+ })
+ .response
+ });
+ })
+ .response
+ });
+
+ ui.label(format!(
+ "{display_name}: ({:.2}, {:.2}, {:.2}) {unit}",
+ latest[0], latest[1], latest[2]
+ ));
+ });
+ }
+}
+
+impl<'a, 'b> TabViewer for StatsTabs<'a, 'b> {
+ type Tab = StatTabKind;
+
+ fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
+ match tab {
+ StatTabKind::Imu => {
+ self.state.imu_tab_visible = true;
+ self.imu_ui(ui);
+ }
+ StatTabKind::Xlink => {
+ self.xlink_ui(ui);
+ }
+ };
+ }
+
+ fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
+ match tab {
+ StatTabKind::Imu => "IMU".into(),
+ StatTabKind::Xlink => "Xlink".into(),
+ }
+ }
+}
diff --git a/crates/re_viewer/src/ui/time_panel/data_density_graph.rs b/crates/re_viewer/src/ui/time_panel/data_density_graph.rs
index bdf311b4ccf2..83b48a7ab007 100644
--- a/crates/re_viewer/src/ui/time_panel/data_density_graph.rs
+++ b/crates/re_viewer/src/ui/time_panel/data_density_graph.rs
@@ -540,7 +540,7 @@ fn show_row_ids_tooltip(
}
ui.add_space(8.0);
- crate::ui::selection_panel::what_is_selected_ui(ui, ctx, blueprint, item);
+ crate::ui::selection_panel::what_is_selected_ui(ui, ctx, &mut blueprint.viewport, item);
ui.add_space(8.0);
let timeline = *ctx.rec_cfg.time_ctrl.timeline();
diff --git a/crates/re_viewer/src/ui/time_panel/mod.rs b/crates/re_viewer/src/ui/time_panel/mod.rs
index e106cb4ed74a..73e8476ce531 100644
--- a/crates/re_viewer/src/ui/time_panel/mod.rs
+++ b/crates/re_viewer/src/ui/time_panel/mod.rs
@@ -525,7 +525,7 @@ impl TimePanel {
if is_visible {
response.on_hover_ui(|ui| {
let item = Item::ComponentPath(component_path.clone());
- what_is_selected_ui(ui, ctx, blueprint, &item);
+ what_is_selected_ui(ui, ctx, &mut blueprint.viewport, &item);
ui.add_space(8.0);
let query = ctx.current_query();
component_path.data_ui(ctx, ui, super::UiVerbosity::Small, &query);
diff --git a/crates/re_viewer/src/ui/view_category.rs b/crates/re_viewer/src/ui/view_category.rs
index 9a23512e42af..d0166aa2815e 100644
--- a/crates/re_viewer/src/ui/view_category.rs
+++ b/crates/re_viewer/src/ui/view_category.rs
@@ -2,7 +2,8 @@ use re_arrow_store::{LatestAtQuery, TimeInt};
use re_data_store::{EntityPath, LogDb, Timeline};
use re_log_types::{
component_types::{
- Box3D, LineStrip2D, LineStrip3D, Point2D, Point3D, Rect2D, Scalar, Tensor, TextEntry,
+ Box3D, LineStrip2D, LineStrip3D, NodeGraph, Point2D, Point3D, Rect2D, Scalar, Tensor,
+ TextEntry,
},
Arrow3D, Component, Mesh3D, Transform,
};
@@ -29,6 +30,7 @@ pub enum ViewCategory {
/// High-dimensional tensor view
Tensor,
+ NodeGraph,
}
impl ViewCategory {
@@ -39,6 +41,7 @@ impl ViewCategory {
ViewCategory::BarChart => &re_ui::icons::SPACE_VIEW_HISTOGRAM,
ViewCategory::Spatial => &re_ui::icons::SPACE_VIEW_3D,
ViewCategory::Tensor => &re_ui::icons::SPACE_VIEW_TENSOR,
+ ViewCategory::NodeGraph => &re_ui::icons::SPACE_VIEW_TENSOR, // TODO(filip): add icon
}
}
}
@@ -51,6 +54,7 @@ impl std::fmt::Display for ViewCategory {
ViewCategory::BarChart => "Bar Chart",
ViewCategory::Spatial => "Spatial",
ViewCategory::Tensor => "Tensor",
+ ViewCategory::NodeGraph => "Node Graph",
})
}
}
@@ -111,6 +115,8 @@ pub fn categorize_entity_path(
}
}
}
+ } else if component == NodeGraph::name() {
+ set.insert(ViewCategory::NodeGraph);
}
}
diff --git a/crates/re_viewer/src/ui/view_node_graph/mod.rs b/crates/re_viewer/src/ui/view_node_graph/mod.rs
new file mode 100644
index 000000000000..2839c6b28214
--- /dev/null
+++ b/crates/re_viewer/src/ui/view_node_graph/mod.rs
@@ -0,0 +1,5 @@
+mod scene;
+pub(crate) use self::scene::{NodeGraphEntry, SceneNodeGraph};
+
+mod ui;
+pub(crate) use self::ui::{view_node_graph, ViewNodeGraphState};
diff --git a/crates/re_viewer/src/ui/view_node_graph/scene.rs b/crates/re_viewer/src/ui/view_node_graph/scene.rs
new file mode 100644
index 000000000000..0f9b7c0361ea
--- /dev/null
+++ b/crates/re_viewer/src/ui/view_node_graph/scene.rs
@@ -0,0 +1,34 @@
+use crate::{ui::SceneQuery, ViewerContext};
+use re_data_store::EntityPath;
+// ---
+
+#[derive(Debug, Clone)]
+pub struct NodeGraphEntry {
+ pub entity_path: EntityPath,
+
+ /// `None` for timeless data.
+ pub time: Option,
+
+ pub color: Option<[u8; 4]>,
+
+ pub level: Option,
+
+ pub body: String,
+}
+
+/// A NodeGraph scene, with everything needed to render it.
+#[derive(Default)]
+pub struct SceneNodeGraph {
+ pub NodeGraph_entries: Vec,
+}
+
+impl SceneNodeGraph {
+ /// Loads all NodeGraph components into the scene according to the given query.
+ pub(crate) fn load(&mut self, ctx: &ViewerContext<'_>, query: &SceneQuery<'_>) {
+ crate::profile_function!();
+
+ let store = &ctx.log_db.entity_db.data_store;
+
+ for entity_path in query.entity_paths {}
+ }
+}
diff --git a/crates/re_viewer/src/ui/view_node_graph/ui.rs b/crates/re_viewer/src/ui/view_node_graph/ui.rs
new file mode 100644
index 000000000000..5b68f0dc0ecc
--- /dev/null
+++ b/crates/re_viewer/src/ui/view_node_graph/ui.rs
@@ -0,0 +1,91 @@
+use std::collections::BTreeMap;
+use crate::ViewerContext;
+use re_data_store::{EntityPath, Timeline};
+
+use super::{NodeGraphEntry, SceneNodeGraph};
+// --- Main view ---
+
+#[derive(Clone, Default, serde::Deserialize, serde::Serialize)]
+#[serde(default)]
+pub struct ViewNodeGraphState {
+ /// Keeps track of the latest time selection made by the user.
+ ///
+ /// We need this because we want the user to be able to manually scroll the
+ /// NodeGraph entry window however they please when the time cursor isn't moving.
+ latest_time: i64,
+
+ pub filters: ViewNodeGraphFilters,
+
+ monospace: bool,
+}
+
+impl ViewNodeGraphState {
+ pub fn selection_ui(&mut self, re_ui: &re_ui::ReUi, ui: &mut egui::Ui) {
+ crate::profile_function!();
+ re_log::info!("Holda from node graph");
+ }
+}
+
+pub(crate) fn view_node_graph(
+ ctx: &mut ViewerContext<'_>,
+ ui: &mut egui::Ui,
+ state: &mut ViewNodeGraphState,
+ scene: &SceneNodeGraph,
+) -> egui::Response {
+ crate::profile_function!();
+
+ ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| {
+ if ui.button("Button text").clicked() {
+ re_log::info!("Holda from node graph");
+ }
+ })
+ .response
+}
+
+// --- Filters ---
+
+// TODO(cmc): implement "body contains " filter.
+// TODO(cmc): beyond filters, it'd be nice to be able to swap columns at some point.
+#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
+pub struct ViewNodeGraphFilters {
+ // Column filters: which columns should be visible?
+ // Timelines are special: each one has a dedicated column.
+ pub col_timelines: BTreeMap,
+ pub col_entity_path: bool,
+ pub col_log_level: bool,
+
+ // Row filters: which rows should be visible?
+ pub row_entity_paths: BTreeMap,
+ pub row_log_levels: BTreeMap,
+}
+
+impl Default for ViewNodeGraphFilters {
+ fn default() -> Self {
+ Self {
+ col_entity_path: true,
+ col_log_level: true,
+ col_timelines: Default::default(),
+ row_entity_paths: Default::default(),
+ row_log_levels: Default::default(),
+ }
+ }
+}
+
+impl ViewNodeGraphFilters {
+ pub fn is_entity_path_visible(&self, entity_path: &EntityPath) -> bool {
+ self.row_entity_paths
+ .get(entity_path)
+ .copied()
+ .unwrap_or(true)
+ }
+
+ pub fn is_log_level_visible(&self, level: &str) -> bool {
+ self.row_log_levels.get(level).copied().unwrap_or(true)
+ }
+
+ // Checks whether new values are available for any of the filters, and updates everything
+ // accordingly.
+ fn update(&mut self, ctx: &mut ViewerContext<'_>, nodegraph_entries: &[NodeGraphEntry]) {
+ crate::profile_function!();
+ }
+}
diff --git a/crates/re_viewer/src/ui/view_spatial/eye.rs b/crates/re_viewer/src/ui/view_spatial/eye.rs
index 287481311101..d1909b7cfee5 100644
--- a/crates/re_viewer/src/ui/view_spatial/eye.rs
+++ b/crates/re_viewer/src/ui/view_spatial/eye.rs
@@ -165,6 +165,7 @@ impl Eye {
pub struct OrbitEye {
pub orbit_center: Vec3,
pub orbit_radius: f32,
+
pub world_from_view_rot: Quat,
pub fov_y: f32,
@@ -173,11 +174,38 @@ pub struct OrbitEye {
/// For controlling the eye with WSAD in a smooth way.
pub velocity: Vec3,
+
+ /// Left over scroll delta that still needs to be applied (smoothed out over several frames)
+ #[serde(skip)]
+ unprocessed_scroll_delta: f32,
}
impl OrbitEye {
const MAX_PITCH: f32 = 0.999 * 0.25 * std::f32::consts::TAU;
+ /// Scroll wheels delta are capped out at this value per second. Anything above is smoothed out over several frames.
+ ///
+ /// We generally only want this to only kick in when the user scrolls fast while we maintain very high framerate,
+ /// so don't go too low!
+ ///
+ /// To give a sense of ballpark:
+ /// * measured 14.0 as the value of a single notch on a logitech mouse wheel connected to a Macbook returns in a single frame (!)
+ /// (so scrolling 10 notches in a tenth of a second gives a per second scroll delta of 1400)
+ /// * macbook trackpad is typically at max 1.0 in every given frame
+ const MAX_SCROLL_DELTA_PER_SECOND: f32 = 1000.0;
+
+ pub fn new(orbit_center: Vec3, orbit_radius: f32, world_from_view_rot: Quat, up: Vec3) -> Self {
+ OrbitEye {
+ orbit_center,
+ orbit_radius,
+ world_from_view_rot,
+ fov_y: Eye::DEFAULT_FOV_Y,
+ up,
+ velocity: Vec3::ZERO,
+ unprocessed_scroll_delta: 0.0,
+ }
+ }
+
pub fn position(&self) -> Vec3 {
self.orbit_center + self.world_from_view_rot * vec3(0.0, 0.0, self.orbit_radius)
}
@@ -213,6 +241,10 @@ impl OrbitEye {
fov_y: egui::lerp(self.fov_y..=other.fov_y, t),
up: self.up.lerp(other.up, t).normalize_or_zero(),
velocity: self.velocity.lerp(other.velocity, t),
+ unprocessed_scroll_delta: lerp(
+ self.unprocessed_scroll_delta..=other.unprocessed_scroll_delta,
+ t,
+ ),
}
}
@@ -261,8 +293,9 @@ impl OrbitEye {
}
}
- /// Returns `true` if any change
- pub fn interact(&mut self, response: &egui::Response, drag_threshold: f32) -> bool {
+ /// Returns `true` if interaction occurred.
+ /// I.e. the camera changed via user input.
+ pub fn update(&mut self, response: &egui::Response, drag_threshold: f32) -> bool {
let mut did_interact = false;
if response.drag_delta().length() > drag_threshold {
@@ -283,21 +316,42 @@ impl OrbitEye {
}
}
- if response.hovered() {
+ let (zoom_delta, raw_scroll_delta) = if response.hovered() {
self.keyboard_navigation(&response.ctx);
- let factor = response
- .ctx
- .input(|i| i.zoom_delta() * (i.scroll_delta.y / 200.0).exp());
- if factor != 1.0 {
- let new_radius = self.orbit_radius / factor;
-
- // Don't let radius go too small or too big because this might cause infinity/nan in some calculations.
- // Max value is chosen with some generous margin of an observed crash due to infinity.
- if f32::MIN_POSITIVE < new_radius && new_radius < 1.0e17 {
- self.orbit_radius = new_radius;
- }
+ response.ctx.input(|i| (i.zoom_delta(), i.scroll_delta.y))
+ } else {
+ (1.0, 0.0)
+ };
+ if zoom_delta != 1.0 || raw_scroll_delta != 0.0 {
+ did_interact = true;
+ }
- did_interact = true;
+ // Mouse wheels often go very large steps!
+ // This makes the zoom speed feel clunky, so we smooth it out over several frames.
+ let frame_delta = response.ctx.input(|i| i.stable_dt).at_most(0.1);
+ let accumulated_scroll_delta = raw_scroll_delta + self.unprocessed_scroll_delta;
+ let unsmoothed_scroll_per_second = accumulated_scroll_delta / frame_delta;
+ let scroll_dir = unsmoothed_scroll_per_second.signum();
+ let scroll_delta = scroll_dir
+ * unsmoothed_scroll_per_second
+ .abs()
+ .at_most(Self::MAX_SCROLL_DELTA_PER_SECOND)
+ * frame_delta;
+ self.unprocessed_scroll_delta = accumulated_scroll_delta - scroll_delta;
+
+ if self.unprocessed_scroll_delta.abs() > 0.1 {
+ // We have a lot of unprocessed scroll delta, so we need to keep calling this function.
+ response.ctx.request_repaint();
+ }
+
+ let zoom_factor = zoom_delta * (scroll_delta / 200.0).exp();
+ if zoom_factor != 1.0 {
+ let new_radius = self.orbit_radius / zoom_factor;
+
+ // Don't let radius go too small or too big because this might cause infinity/nan in some calculations.
+ // Max value is chosen with some generous margin of an observed crash due to infinity.
+ if f32::MIN_POSITIVE < new_radius && new_radius < 1.0e17 {
+ self.orbit_radius = new_radius;
}
}
diff --git a/crates/re_viewer/src/ui/view_spatial/scene/mod.rs b/crates/re_viewer/src/ui/view_spatial/scene/mod.rs
index 0afad5c77dd3..81c2688a943a 100644
--- a/crates/re_viewer/src/ui/view_spatial/scene/mod.rs
+++ b/crates/re_viewer/src/ui/view_spatial/scene/mod.rs
@@ -7,7 +7,7 @@ use re_log_types::{
component_types::{ClassId, InstanceKey, KeypointId},
DecodedTensor, MeshId,
};
-use re_renderer::{Color32, OutlineMaskPreference, Size};
+use re_renderer::{renderer::TexturedRect, Color32, OutlineMaskPreference, Size};
use crate::{
misc::{mesh_loader::LoadedMesh, SpaceViewHighlights, TransformCache, ViewerContext},
@@ -63,15 +63,8 @@ pub struct Image {
pub tensor: DecodedTensor,
- /// If this is a depth map, how long is a meter?
- ///
- /// For example, with a `u16` dtype one might have
- /// `meter == 1000.0` for millimeter precision
- /// up to a ~65m range.
- pub meter: Option,
-
- /// A thing that provides additional semantic context for your dtype.
- pub annotations: Arc,
+ /// Textured rectangle for the renderer.
+ pub textured_rect: TexturedRect,
}
pub enum UiLabelTarget {
@@ -104,9 +97,6 @@ pub struct SceneSpatialUiData {
/// Picking any any of these rects cause the referred instance to be hovered.
/// Only use this for 2d overlays!
pub pickable_ui_rects: Vec<(egui::Rect, InstancePathHash)>,
-
- /// Images are a special case of rects where we're storing some extra information to allow miniature previews etc.
- pub images: Vec,
}
pub struct SceneSpatial {
@@ -231,7 +221,7 @@ impl SceneSpatial {
return SpatialNavigationMode::ThreeD;
}
- if !self.ui.images.is_empty() {
+ if !self.primitives.images.is_empty() {
return SpatialNavigationMode::TwoD;
}
if self.num_logged_3d_objects == 0 {
diff --git a/crates/re_viewer/src/ui/view_spatial/scene/picking.rs b/crates/re_viewer/src/ui/view_spatial/scene/picking.rs
index ddf73c2fdfbf..8d8325fdff1a 100644
--- a/crates/re_viewer/src/ui/view_spatial/scene/picking.rs
+++ b/crates/re_viewer/src/ui/view_spatial/scene/picking.rs
@@ -2,10 +2,10 @@
use ahash::HashSet;
use re_data_store::InstancePathHash;
-use re_log_types::{component_types::InstanceKey, EntityPathHash};
+use re_log_types::component_types::InstanceKey;
use re_renderer::PickingLayerProcessor;
-use super::{SceneSpatialPrimitives, SceneSpatialUiData};
+use super::{Image, SceneSpatialPrimitives, SceneSpatialUiData};
use crate::{
misc::instance_hash_conversions::instance_path_hash_from_picking_layer_id,
ui::view_spatial::eye::Eye,
@@ -116,11 +116,7 @@ impl PickingContext {
self,
previous_picking_result,
);
- let mut rect_hits = picking_textured_rects(
- self,
- &primitives.textured_rectangles,
- &primitives.textured_rectangles_ids,
- );
+ let mut rect_hits = picking_textured_rects(self, &primitives.images);
rect_hits.sort_by(|a, b| b.depth_offset.cmp(&a.depth_offset));
let ui_rect_hits = picking_ui_rects(self, ui_data);
@@ -241,23 +237,13 @@ fn picking_gpu(
}
}
-fn picking_textured_rects(
- context: &PickingContext,
- textured_rectangles: &[re_renderer::renderer::TexturedRect],
- textured_rectangles_ids: &[EntityPathHash],
-) -> Vec {
+fn picking_textured_rects(context: &PickingContext, images: &[Image]) -> Vec {
crate::profile_function!();
let mut hits = Vec::new();
- for (rect, id) in textured_rectangles
- .iter()
- .zip(textured_rectangles_ids.iter())
- {
- if !id.is_some() {
- continue;
- }
-
+ for image in images {
+ let rect = &image.textured_rect;
let rect_plane = macaw::Plane3::from_normal_point(
rect.extent_u.cross(rect.extent_v).normalize(),
rect.top_left_corner_position,
@@ -277,7 +263,7 @@ fn picking_textured_rects(
if (0.0..=1.0).contains(&u) && (0.0..=1.0).contains(&v) {
hits.push(PickingRayHit {
instance_path_hash: InstancePathHash {
- entity_path_hash: *id,
+ entity_path_hash: image.ent_path.hash(),
instance_key: InstanceKey::from_2d_image_coordinate(
[
(u * rect.colormapped_texture.texture.width() as f32) as u32,
diff --git a/crates/re_viewer/src/ui/view_spatial/scene/primitives.rs b/crates/re_viewer/src/ui/view_spatial/scene/primitives.rs
index 392f0bde4fa1..407d1eec47fa 100644
--- a/crates/re_viewer/src/ui/view_spatial/scene/primitives.rs
+++ b/crates/re_viewer/src/ui/view_spatial/scene/primitives.rs
@@ -1,6 +1,6 @@
use egui::Color32;
use re_data_store::EntityPath;
-use re_log_types::{component_types::InstanceKey, EntityPathHash};
+use re_log_types::component_types::InstanceKey;
use re_renderer::{
renderer::{DepthClouds, MeshInstance},
LineStripSeriesBuilder, PointCloudBuilder,
@@ -20,11 +20,7 @@ pub struct SceneSpatialPrimitives {
/// Estimated bounding box of all data in scene coordinates. Accumulated.
pub(super) bounding_box: macaw::BoundingBox,
- // TODO(andreas): Storing extra data like so is unsafe and not future proof either
- // (see also above comment on the need to separate cpu-readable data)
- pub textured_rectangles_ids: Vec,
- pub textured_rectangles: Vec,
-
+ pub images: Vec,
pub line_strips: LineStripSeriesBuilder,
pub points: PointCloudBuilder,
pub meshes: Vec,
@@ -44,8 +40,7 @@ impl SceneSpatialPrimitives {
pub fn new(re_ctx: &mut re_renderer::RenderContext) -> Self {
Self {
bounding_box: macaw::BoundingBox::nothing(),
- textured_rectangles_ids: Default::default(),
- textured_rectangles: Default::default(),
+ images: Default::default(),
line_strips: LineStripSeriesBuilder::new(re_ctx)
.radius_boost_in_ui_points_for_outlines(SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES),
points: PointCloudBuilder::new(re_ctx)
@@ -68,8 +63,7 @@ impl SceneSpatialPrimitives {
pub fn num_primitives(&self) -> usize {
let Self {
bounding_box: _,
- textured_rectangles,
- textured_rectangles_ids: _,
+ images,
line_strips,
points,
meshes,
@@ -77,7 +71,7 @@ impl SceneSpatialPrimitives {
any_outlines: _,
} = &self;
- textured_rectangles.len()
+ images.len()
+ line_strips.vertices.len()
+ points.vertices.len()
+ meshes.len()
@@ -89,8 +83,7 @@ impl SceneSpatialPrimitives {
let Self {
bounding_box,
- textured_rectangles_ids: _,
- textured_rectangles,
+ images,
line_strips,
points,
meshes,
@@ -100,7 +93,8 @@ impl SceneSpatialPrimitives {
*bounding_box = macaw::BoundingBox::nothing();
- for rect in textured_rectangles {
+ for image in images {
+ let rect = &image.textured_rect;
bounding_box.extend(rect.top_left_corner_position);
bounding_box.extend(rect.top_left_corner_position + rect.extent_u);
bounding_box.extend(rect.top_left_corner_position + rect.extent_v);
diff --git a/crates/re_viewer/src/ui/view_spatial/scene/scene_part/images.rs b/crates/re_viewer/src/ui/view_spatial/scene/scene_part/images.rs
index 5e1227feb4ad..574bb254f2db 100644
--- a/crates/re_viewer/src/ui/view_spatial/scene/scene_part/images.rs
+++ b/crates/re_viewer/src/ui/view_spatial/scene/scene_part/images.rs
@@ -9,7 +9,8 @@ use re_log_types::{
};
use re_query::{query_primary_with_history, EntityView, QueryError};
use re_renderer::{
- renderer::{DepthCloud, DepthCloudDepthData, RectangleOptions},
+ renderer::{DepthCloud, DepthCloudAlbedoData, RectangleOptions},
+ resource_managers::Texture2DCreationDesc,
Colormap, OutlineMaskPreference,
};
@@ -24,9 +25,7 @@ use crate::{
use super::ScenePart;
-#[allow(clippy::too_many_arguments)]
-fn push_tensor_texture(
- scene: &mut SceneSpatial,
+fn to_textured_rect(
ctx: &mut ViewerContext<'_>,
annotations: &Annotations,
world_from_obj: glam::Mat4,
@@ -34,10 +33,10 @@ fn push_tensor_texture(
tensor: &DecodedTensor,
multiplicative_tint: egui::Rgba,
outline_mask: OutlineMaskPreference,
-) {
+) -> Option {
crate::profile_function!();
- let Some([height, width, _]) = tensor.image_height_width_channels() else { return; };
+ let Some([height, width, _]) = tensor.image_height_width_channels() else { return None; };
let debug_name = ent_path.to_string();
let tensor_stats = ctx.cache.tensor_stats(tensor);
@@ -67,7 +66,7 @@ fn push_tensor_texture(
re_renderer::renderer::TextureFilterMin::Linear
};
- let textured_rect = re_renderer::renderer::TexturedRect {
+ Some(re_renderer::renderer::TexturedRect {
top_left_corner_position: world_from_obj.transform_point3(glam::Vec3::ZERO),
extent_u: world_from_obj.transform_vector3(glam::Vec3::X * width as f32),
extent_v: world_from_obj.transform_vector3(glam::Vec3::Y * height as f32),
@@ -79,15 +78,11 @@ fn push_tensor_texture(
depth_offset: -1, // Push to background. Mostly important for mouse picking order!
outline_mask,
},
- };
- scene.primitives.textured_rectangles.push(textured_rect);
- scene
- .primitives
- .textured_rectangles_ids
- .push(ent_path.hash());
+ })
}
Err(err) => {
re_log::error_once!("Failed to create texture from tensor for {debug_name:?}: {err}");
+ None
}
}
}
@@ -98,15 +93,17 @@ fn handle_image_layering(scene: &mut SceneSpatial) {
// Handle layered rectangles that are on (roughly) the same plane and were logged in sequence.
// First, group by similar plane.
// TODO(andreas): Need planes later for picking as well!
- let rects_grouped_by_plane = {
+ let images_grouped_by_plane = {
let mut cur_plane = macaw::Plane3::from_normal_dist(Vec3::NAN, std::f32::NAN);
let mut rectangle_group = Vec::new();
scene
.primitives
- .textured_rectangles
- .iter_mut()
+ .images
+ .drain(..) // We rebuild the list as we might reorder as well!
.batching(move |it| {
- for rect in it.by_ref() {
+ for image in it {
+ let rect = &image.textured_rect;
+
let prev_plane = cur_plane;
cur_plane = macaw::Plane3::from_normal_point(
rect.extent_u.cross(rect.extent_v).normalize(),
@@ -118,10 +115,10 @@ fn handle_image_layering(scene: &mut SceneSpatial) {
&& prev_plane.normal.dot(cur_plane.normal) < 0.99
&& (prev_plane.d - cur_plane.d) < 0.01
{
- let previous_group = std::mem::replace(&mut rectangle_group, vec![rect]);
+ let previous_group = std::mem::replace(&mut rectangle_group, vec![image]);
return Some(previous_group);
}
- rectangle_group.push(rect);
+ rectangle_group.push(image);
}
if !rectangle_group.is_empty() {
Some(rectangle_group.drain(..).collect())
@@ -129,14 +126,19 @@ fn handle_image_layering(scene: &mut SceneSpatial) {
None
}
})
- };
- // Then, change opacity & transformation for planes within group except the base plane.
- for mut grouped_rects in rects_grouped_by_plane {
- let total_num_images = grouped_rects.len();
- for (idx, rect) in grouped_rects.iter_mut().enumerate() {
+ }
+ .collect_vec();
+
+ // Then, for each planar group do resorting and change transparency.
+ for mut grouped_images in images_grouped_by_plane {
+ // Class id images should generally come last as they typically have large areas being zeroed out (which maps to fully transparent).
+ grouped_images.sort_by_key(|image| image.tensor.meaning == TensorDataMeaning::ClassId);
+
+ let total_num_images = grouped_images.len();
+ for (idx, image) in grouped_images.iter_mut().enumerate() {
// Set depth offset for correct order and avoid z fighting when there is a 3d camera.
// Keep behind depth offset 0 for correct picking order.
- rect.options.depth_offset =
+ image.textured_rect.options.depth_offset =
(idx as isize - total_num_images as isize) as re_renderer::DepthOffset;
// make top images transparent
@@ -145,8 +147,14 @@ fn handle_image_layering(scene: &mut SceneSpatial) {
} else {
1.0 / total_num_images.at_most(20) as f32
}; // avoid precision problems in framebuffer
- rect.options.multiplicative_tint = rect.options.multiplicative_tint.multiply(opacity);
+ image.textured_rect.options.multiplicative_tint = image
+ .textured_rect
+ .options
+ .multiplicative_tint
+ .multiply(opacity);
}
+
+ scene.primitives.images.extend(grouped_images);
}
}
@@ -159,7 +167,7 @@ impl ImagesPart {
scene: &mut SceneSpatial,
ctx: &mut ViewerContext<'_>,
transforms: &TransformCache,
- properties: &EntityProperties,
+ properties: &mut EntityProperties,
ent_path: &EntityPath,
world_from_obj: glam::Mat4,
highlights: &SpaceViewHighlights,
@@ -189,16 +197,6 @@ impl ImagesPart {
};
let annotations = scene.annotation_map.find(ent_path);
-
- // TODO(jleibs): Meter should really be its own component
- let meter = tensor.meter;
- scene.ui.images.push(Image {
- ent_path: ent_path.clone(),
- tensor: tensor.clone(),
- meter,
- annotations: annotations.clone(),
- });
-
let entity_highlight = highlights.entity_outline_mask(ent_path.hash());
if *properties.backproject_depth.get() && tensor.meaning == TensorDataMeaning::Depth {
@@ -233,8 +231,7 @@ impl ImagesPart {
DefaultColor::OpaqueWhite,
);
- push_tensor_texture(
- scene,
+ if let Some(textured_rect) = to_textured_rect(
ctx,
&annotations,
world_from_obj,
@@ -242,7 +239,13 @@ impl ImagesPart {
&tensor,
color.into(),
entity_highlight.overall,
- );
+ ) {
+ scene.primitives.images.push(Image {
+ ent_path: ent_path.clone(),
+ tensor,
+ textured_rect,
+ });
+ }
}
Ok(())
@@ -253,7 +256,7 @@ impl ImagesPart {
scene: &mut SceneSpatial,
ctx: &mut ViewerContext<'_>,
transforms: &TransformCache,
- properties: &EntityProperties,
+ properties: &mut EntityProperties,
tensor: &DecodedTensor,
ent_path: &EntityPath,
pinhole_ent_path: &EntityPath,
@@ -277,26 +280,55 @@ impl ImagesPart {
return Err(format!("Couldn't fetch pinhole extrinsics at {pinhole_ent_path:?}"));
};
- // TODO(cmc): automagically convert as needed for non-natively supported datatypes?
- let data = match &tensor.data {
- // NOTE: Shallow clone if feature `arrow` is enabled, full alloc + memcpy otherwise.
- TensorData::U16(data) => DepthCloudDepthData::U16(data.clone()),
- TensorData::F32(data) => DepthCloudDepthData::F32(data.clone()),
- _ => {
- return Err(format!(
- "Tensor datatype {} is not supported for backprojection",
- tensor.dtype()
- ));
- }
+ let Some([height, width, _]) = tensor.image_height_width_channels() else {
+ return Err(format!("Tensor at {ent_path:?} is not an image"));
+ };
+ let dimensions = glam::UVec2::new(width as _, height as _);
+
+ let depth_texture = {
+ // Ideally, we'd use the same key as for displaying the texture, but we might make other compromises regarding formats etc.!
+ // So to not couple this, we use a different key here
+ let texture_key = egui::util::hash((tensor.id(), "depth_cloud"));
+ let mut data_f32 = Vec::new();
+ ctx.render_ctx
+ .texture_manager_2d
+ .get_or_try_create_with(
+ texture_key,
+ &mut ctx.render_ctx.gpu_resources.textures,
+ || {
+ // TODO(andreas/cmc): Ideally we'd upload the u16 data as-is.
+ // However, R16Unorm is behind a feature flag and Depth16Unorm doesn't work on WebGL (and is awkward as this is a depth buffer format!).
+ let data = match &tensor.data {
+ TensorData::U16(data) => {
+ data_f32.extend(data.as_slice().iter().map(|d| *d as f32));
+ bytemuck::cast_slice(&data_f32).into()
+ }
+ TensorData::F32(data) => bytemuck::cast_slice(data).into(),
+ _ => {
+ return Err(format!(
+ "Tensor datatype {} is not supported for back-projection",
+ tensor.dtype()
+ ));
+ }
+ };
+
+ Ok(Texture2DCreationDesc {
+ label: format!("Depth cloud for {ent_path:?}").into(),
+ data,
+ format: wgpu::TextureFormat::R32Float,
+ width: width as _,
+ height: height as _,
+ })
+ },
+ )
+ .map_err(|err| format!("Failed to create depth cloud texture: {err}"))?
};
let depth_from_world_scale = *properties.depth_from_world_scale.get();
- let world_depth_from_data_depth = 1.0 / depth_from_world_scale;
- let (h, w) = (tensor.shape()[0].size, tensor.shape()[1].size);
- let dimensions = glam::UVec2::new(w as _, h as _);
+ let world_depth_from_texture_depth = 1.0 / depth_from_world_scale;
- let colormap = match *properties.color_mapper.get() {
+ let mut colormap = match *properties.color_mapper.get() {
re_data_store::ColorMapper::Colormap(colormap) => match colormap {
re_data_store::Colormap::Grayscale => Colormap::Grayscale,
re_data_store::Colormap::Turbo => Colormap::Turbo,
@@ -305,13 +337,58 @@ impl ImagesPart {
re_data_store::Colormap::Magma => Colormap::Magma,
re_data_store::Colormap::Inferno => Colormap::Inferno,
},
+ re_data_store::ColorMapper::AlbedoTexture => Colormap::AlbedoTexture,
};
+ let mut albedo_data = None;
+ let mut albedo_dimensions = glam::UVec2::ZERO;
+
+ if colormap == Colormap::AlbedoTexture {
+ let tensor = properties.albedo_texture.as_ref().and_then(|path| {
+ query_latest_single::(&ctx.log_db.entity_db, path, &ctx.current_query())
+ });
+ if let Some(tensor) = tensor {
+ let (h, w) = (tensor.shape()[0].size, tensor.shape()[1].size);
+ albedo_dimensions = glam::UVec2::new(w as _, h as _);
+
+ // TODO(cmc): How does one know whether the texture is sRGB or not at this point?
+ // TODO(cmc): We should easily be able to pass almost any datatype here.
+
+ albedo_data = match &tensor.data {
+ TensorData::U8(data) => {
+ if let Some([_, _, c]) = tensor.image_height_width_channels() {
+ match c {
+ 1 => Some(DepthCloudAlbedoData::Mono8(data.0.to_vec())),
+ 3 => Some(DepthCloudAlbedoData::Rgb8(data.0.to_vec())),
+ 4 => Some(DepthCloudAlbedoData::Rgb8Srgb(data.0.to_vec())),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ }
+ _ => {
+ re_log::debug_once!(
+ "Tensor datatype not supported for albedo texture ({:?})",
+ std::mem::discriminant(&tensor.data),
+ );
+ None
+ }
+ };
+ } else {
+ re_log::debug_once!(
+ "Albedo texture couldn't be fetched ({:?})",
+ properties.albedo_texture
+ );
+ colormap = Colormap::Grayscale;
+ }
+ }
+
// We want point radius to be defined in a scale where the radius of a point
// is a factor (`backproject_radius_scale`) of the diameter of a pixel projected
// at that distance.
let fov_y = intrinsics.fov_y().unwrap_or(1.0);
- let pixel_width_from_depth = (0.5 * fov_y).tan() / (0.5 * h as f32);
+ let pixel_width_from_depth = (0.5 * fov_y).tan() / (0.5 * height as f32);
let radius_scale = *properties.backproject_radius_scale.get();
let point_radius_from_world_depth = radius_scale * pixel_width_from_depth;
@@ -321,23 +398,25 @@ impl ImagesPart {
// This could only happen for Jpegs, and we should never get here.
// TODO(emilk): refactor the code so that we can always calculate a range for the tensor
re_log::warn_once!("Couldn't calculate range for a depth tensor!?");
- match data {
- DepthCloudDepthData::U16(_) => u16::MAX as f32,
- DepthCloudDepthData::F32(_) => 10.0,
+ match tensor.data {
+ TensorData::U16(_) => u16::MAX as f32,
+ _ => 10.0,
}
};
scene.primitives.depth_clouds.clouds.push(DepthCloud {
world_from_obj,
depth_camera_intrinsics: intrinsics.image_from_cam.into(),
- world_depth_from_data_depth,
+ world_depth_from_texture_depth,
point_radius_from_world_depth,
- max_depth_in_world: world_depth_from_data_depth * max_data_value,
+ max_depth_in_world: max_data_value / depth_from_world_scale,
depth_dimensions: dimensions,
- depth_data: data,
+ depth_texture,
colormap,
outline_mask_id: entity_highlight.overall,
picking_object_id: re_renderer::PickingLayerObjectId(ent_path.hash64()),
+ albedo_data,
+ albedo_dimensions,
});
Ok(())
@@ -355,7 +434,7 @@ impl ScenePart for ImagesPart {
) {
crate::profile_scope!("ImagesPart");
- for (ent_path, props) in query.iter_entities() {
+ for (ent_path, mut props) in query.iter_entities() {
let Some(world_from_obj) = transforms.reference_from_entity(ent_path) else {
continue;
};
@@ -375,7 +454,7 @@ impl ScenePart for ImagesPart {
scene,
ctx,
transforms,
- &props,
+ &mut props,
ent_path,
world_from_obj,
highlights,
diff --git a/crates/re_viewer/src/ui/view_spatial/space_camera_3d.rs b/crates/re_viewer/src/ui/view_spatial/space_camera_3d.rs
index f18d17a504a2..c429cbcaa5cf 100644
--- a/crates/re_viewer/src/ui/view_spatial/space_camera_3d.rs
+++ b/crates/re_viewer/src/ui/view_spatial/space_camera_3d.rs
@@ -6,7 +6,7 @@ use re_log_types::{EntityPath, ViewCoordinates};
/// A logged camera that connects spaces.
#[derive(Clone)]
pub struct SpaceCamera3D {
- /// Path to the entity which has the projection (pinhole, ortho or otherwise) transforms.
+ /// Path to the instance which has the projection (pinhole, ortho or otherwise) transforms.
///
/// We expect the camera transform to apply to this instance and every path below it.
pub ent_path: EntityPath,
diff --git a/crates/re_viewer/src/ui/view_spatial/ui.rs b/crates/re_viewer/src/ui/view_spatial/ui.rs
index 49209bb1b32e..bd2daf058b1e 100644
--- a/crates/re_viewer/src/ui/view_spatial/ui.rs
+++ b/crates/re_viewer/src/ui/view_spatial/ui.rs
@@ -5,7 +5,7 @@ use re_format::format_f32;
use egui::{NumExt, WidgetText};
use macaw::BoundingBox;
use re_log_types::component_types::{Tensor, TensorDataMeaning};
-use re_renderer::OutlineConfig;
+use re_renderer::{Colormap, OutlineConfig};
use crate::{
misc::{
@@ -30,6 +30,7 @@ use super::{
};
/// Describes how the scene is navigated, determining if it is a 2D or 3D experience.
+
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub enum SpatialNavigationMode {
#[default]
@@ -233,6 +234,40 @@ impl ViewSpatialState {
properties.backproject_radius_scale = EditableAutoValue::Auto(1.0);
}
+ let colormap = match *properties.color_mapper.get() {
+ re_data_store::ColorMapper::Colormap(colormap) => match colormap {
+ re_data_store::Colormap::Grayscale => Colormap::Grayscale,
+ re_data_store::Colormap::Turbo => Colormap::Turbo,
+ re_data_store::Colormap::Viridis => Colormap::Viridis,
+ re_data_store::Colormap::Plasma => Colormap::Plasma,
+ re_data_store::Colormap::Magma => Colormap::Magma,
+ re_data_store::Colormap::Inferno => Colormap::Inferno,
+ },
+ re_data_store::ColorMapper::AlbedoTexture => Colormap::AlbedoTexture,
+ };
+ // Set albedo texture if it is not set yet
+ if colormap == Colormap::AlbedoTexture && properties.albedo_texture.is_none() {
+ let mut tex_ep = None;
+ if let Some(tree) = entity_path
+ .parent()
+ .and_then(|path| ctx.log_db.entity_db.tree.subtree(&path))
+ {
+ tree.visit_children_recursively(&mut |ent_path| {
+ if tex_ep.is_some() {
+ return;
+ }
+ let Some(tensor) =
+ query_latest_single::(&ctx.log_db.entity_db, ent_path, &ctx.current_query()) else {
+ return;
+ };
+ if tensor.is_shaped_like_an_image() {
+ tex_ep = Some(ent_path.clone());
+ }
+ });
+ properties.albedo_texture = tex_ep;
+ }
+ }
+
data_blueprint
.data_blueprints_individual()
.set(entity_path.clone(), properties);
@@ -421,6 +456,7 @@ impl ViewSpatialState {
);
}
SpatialNavigationMode::TwoD => {
+ self.scene_bbox_accum = self.scene_bbox;
let scene_rect_accum = egui::Rect::from_min_max(
self.scene_bbox_accum.min.truncate().to_array().into(),
self.scene_bbox_accum.max.truncate().to_array().into(),
@@ -754,28 +790,29 @@ pub fn picking(
let picked_image_with_coords = if hit.hit_type == PickingHitType::TexturedRect
|| *ent_properties.backproject_depth.get()
{
- scene
- .ui
- .images
- .iter()
- .find(|image| image.ent_path == instance_path.entity_path)
- .and_then(|image| {
- // If we're here because of back-projection, but this wasn't actually a depth image, drop out.
- // (the back-projection property may be true despite this not being a depth image!)
- if hit.hit_type != PickingHitType::TexturedRect
- && *ent_properties.backproject_depth.get()
- && image.tensor.meaning != TensorDataMeaning::Depth
- {
- return None;
- }
- image.tensor.image_height_width_channels().map(|[_, w, _]| {
+ query_latest_single::(
+ &ctx.log_db.entity_db,
+ &instance_path.entity_path,
+ &ctx.current_query(),
+ )
+ .and_then(|tensor| {
+ // If we're here because of back-projection, but this wasn't actually a depth image, drop out.
+ // (the back-projection property may be true despite this not being a depth image!)
+ if hit.hit_type != PickingHitType::TexturedRect
+ && *ent_properties.backproject_depth.get()
+ && tensor.meaning != TensorDataMeaning::Depth
+ {
+ None
+ } else {
+ tensor.image_height_width_channels().map(|[_, w, _]| {
let coordinates = hit
.instance_path_hash
.instance_key
.to_2d_image_coordinate(w);
- (image, coordinates)
+ (tensor, coordinates)
})
- })
+ }
+ })
} else {
None
};
@@ -789,9 +826,9 @@ pub fn picking(
instance_path.clone(),
));
- response = if let Some((image, coords)) = picked_image_with_coords {
- if let Some(meter) = image.meter {
- if let Some(raw_value) = image.tensor.get(&[
+ response = if let Some((tensor, coords)) = picked_image_with_coords {
+ if let Some(meter) = tensor.meter {
+ if let Some(raw_value) = tensor.get(&[
picking_context.pointer_in_space2d.y.round() as _,
picking_context.pointer_in_space2d.x.round() as _,
]) {
@@ -815,10 +852,10 @@ pub fn picking(
&ctx.current_query(),
);
- if let [h, w, ..] = image.tensor.shape() {
+ if let Some([h, w, ..]) = tensor.image_height_width_channels() {
ui.separator();
ui.horizontal(|ui| {
- let (w, h) = (w.size as f32, h.size as f32);
+ let (w, h) = (w as f32, h as f32);
if *state.nav_mode.get() == SpatialNavigationMode::TwoD {
let rect = egui::Rect::from_min_size(
egui::Pos2::ZERO,
@@ -826,24 +863,30 @@ pub fn picking(
);
data_ui::image::show_zoomed_image_region_area_outline(
ui,
- &image.tensor,
+ &tensor,
[coords[0] as _, coords[1] as _],
space_from_ui.inverse().transform_rect(rect),
);
}
- let tensor_stats = *ctx.cache.tensor_stats(&image.tensor);
- let debug_name = image.ent_path.to_string();
- data_ui::image::show_zoomed_image_region(
- ctx.render_ctx,
- ui,
- &image.tensor,
- &tensor_stats,
- &image.annotations,
- image.meter,
- &debug_name,
- [coords[0] as _, coords[1] as _],
- );
+ let tensor_name = instance_path.to_string();
+ match ctx.cache.decode.try_decode_tensor_if_necessary(tensor) {
+ Ok(decoded_tensor) =>
+ data_ui::image::show_zoomed_image_region(
+ ctx.render_ctx,
+ ui,
+ &decoded_tensor,
+ ctx.cache.tensor_stats(&decoded_tensor),
+ &scene.annotation_map.find(&instance_path.entity_path),
+ decoded_tensor.meter,
+ &tensor_name,
+ [coords[0] as _, coords[1] as _],
+ ),
+ Err(err) =>
+ re_log::warn_once!(
+ "Encountered problem decoding tensor at path {tensor_name}: {err}"
+ ),
+ }
});
}
});
diff --git a/crates/re_viewer/src/ui/view_spatial/ui_3d.rs b/crates/re_viewer/src/ui/view_spatial/ui_3d.rs
index 295a1e05dec3..dfcbeb6b6d40 100644
--- a/crates/re_viewer/src/ui/view_spatial/ui_3d.rs
+++ b/crates/re_viewer/src/ui/view_spatial/ui_3d.rs
@@ -289,7 +289,7 @@ pub fn view_3d(
state
.state_3d
.update_eye(&response, &state.scene_bbox_accum, &scene.space_cameras);
- let did_interact_with_eye = orbit_eye.interact(&response, orbit_eye_drag_threshold);
+ let did_interact_with_eye = orbit_eye.update(&response, orbit_eye_drag_threshold);
let orbit_eye = *orbit_eye;
let eye = orbit_eye.to_eye();
@@ -453,6 +453,26 @@ pub fn view_3d(
}
}
+ if state.state_3d.show_bbox {
+ let bbox = scene.primitives.bounding_box();
+ if bbox.is_something() && bbox.is_finite() {
+ let scale = bbox.size();
+ let translation = bbox.center();
+ let bbox_from_unit_cube = glam::Affine3A::from_scale_rotation_translation(
+ scale,
+ Default::default(),
+ translation,
+ );
+ scene
+ .primitives
+ .line_strips
+ .batch("scene_bbox")
+ .add_box_outline(bbox_from_unit_cube)
+ .radius(Size::AUTO)
+ .color(egui::Color32::WHITE);
+ }
+ }
+
{
let orbit_center_alpha = egui::remap_clamp(
ui.input(|i| i.time) - state.state_3d.last_eye_interact_time,
@@ -633,14 +653,10 @@ fn default_eye(scene_bbox: &macaw::BoundingBox, space_specs: &SpaceSpecs) -> Orb
let eye_pos = center - radius * look_dir;
- OrbitEye {
- orbit_center: center,
- orbit_radius: radius,
- world_from_view_rot: Quat::from_affine3(
- &Affine3A::look_at_rh(eye_pos, center, look_up).inverse(),
- ),
- fov_y: Eye::DEFAULT_FOV_Y,
- up: space_specs.up.unwrap_or(Vec3::ZERO),
- velocity: Vec3::ZERO,
- }
+ OrbitEye::new(
+ center,
+ radius,
+ Quat::from_affine3(&Affine3A::look_at_rh(eye_pos, center, look_up).inverse()),
+ space_specs.up.unwrap_or(Vec3::ZERO),
+ )
}
diff --git a/crates/re_viewer/src/ui/view_spatial/ui_renderer_bridge.rs b/crates/re_viewer/src/ui/view_spatial/ui_renderer_bridge.rs
index 513abdf82430..e02885290f73 100644
--- a/crates/re_viewer/src/ui/view_spatial/ui_renderer_bridge.rs
+++ b/crates/re_viewer/src/ui/view_spatial/ui_renderer_bridge.rs
@@ -32,7 +32,11 @@ pub fn fill_view_builder(
.queue_draw(&primitives.points.to_draw_data(render_ctx)?)
.queue_draw(&RectangleDrawData::new(
render_ctx,
- &primitives.textured_rectangles,
+ &primitives
+ .images
+ .iter()
+ .map(|image| image.textured_rect.clone())
+ .collect::>(),
)?);
if matches!(background, ScreenBackground::GenericSkybox) {
diff --git a/crates/re_viewer/src/ui/view_tensor/tensor_slice_to_gpu.rs b/crates/re_viewer/src/ui/view_tensor/tensor_slice_to_gpu.rs
index 761c46d5da77..887471a9e519 100644
--- a/crates/re_viewer/src/ui/view_tensor/tensor_slice_to_gpu.rs
+++ b/crates/re_viewer/src/ui/view_tensor/tensor_slice_to_gpu.rs
@@ -1,5 +1,8 @@
use re_log_types::{component_types::TensorCastError, DecodedTensor, TensorDataType};
-use re_renderer::{renderer::ColormappedTexture, resource_managers::Texture2DCreationDesc};
+use re_renderer::{
+ renderer::ColormappedTexture,
+ resource_managers::{GpuTexture2D, Texture2DCreationDesc, TextureManager2DError},
+};
use crate::{
gpu_bridge::{range, RangeError},
@@ -28,10 +31,11 @@ pub fn colormapped_texture(
tensor: &DecodedTensor,
tensor_stats: &TensorStats,
state: &ViewTensorState,
-) -> Result {
+) -> Result> {
crate::profile_function!();
- let range = range(tensor_stats)?;
+ let range =
+ range(tensor_stats).map_err(|err| TextureManager2DError::DataCreation(err.into()))?;
let texture = upload_texture_slice_to_gpu(render_ctx, tensor, state.slice())?;
let color_mapping = state.color_mapping();
@@ -50,7 +54,7 @@ fn upload_texture_slice_to_gpu(
render_ctx: &mut re_renderer::RenderContext,
tensor: &DecodedTensor,
slice_selection: &SliceSelection,
-) -> Result {
+) -> Result> {
let id = egui::util::hash((tensor.id(), slice_selection));
crate::gpu_bridge::try_get_or_create_texture(render_ctx, id, || {
diff --git a/crates/re_viewer/src/ui/view_tensor/ui.rs b/crates/re_viewer/src/ui/view_tensor/ui.rs
index b26e2ba2f03b..b1c6066df1fa 100644
--- a/crates/re_viewer/src/ui/view_tensor/ui.rs
+++ b/crates/re_viewer/src/ui/view_tensor/ui.rs
@@ -27,7 +27,7 @@ pub struct SliceSelection {
pub selector_values: BTreeMap,
}
-#[derive(Clone, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct ViewTensorState {
/// What slice are we vieiwing?
slice: SliceSelection,
@@ -321,7 +321,8 @@ fn paint_colormap_gradient(
width,
height,
}
- });
+ })
+ .map_err(|err| anyhow::anyhow!("Failed to create horizontal gradient texture: {err}"))?;
let colormapped_texture = re_renderer::renderer::ColormappedTexture {
texture: horizontal_gradient,
diff --git a/crates/re_viewer/src/ui/view_time_series/ui.rs b/crates/re_viewer/src/ui/view_time_series/ui.rs
index c2d0d52861d3..68553847af5b 100644
--- a/crates/re_viewer/src/ui/view_time_series/ui.rs
+++ b/crates/re_viewer/src/ui/view_time_series/ui.rs
@@ -82,9 +82,10 @@ pub(crate) fn view_time_series(
plot = plot.x_grid_spacer(move |spacer| ns_grid_spacer(canvas_size, &spacer));
}
- let egui::InnerResponse {
+ let egui::plot::PlotResponse {
inner: time_x,
response,
+ transform: _,
} = plot.show(ui, |plot_ui| {
if plot_ui.plot_secondary_clicked() {
let timeline = ctx.rec_cfg.time_ctrl.timeline();
diff --git a/crates/re_viewer/src/ui/viewport.rs b/crates/re_viewer/src/ui/viewport.rs
index 5f4a0073b992..7f2508169b2c 100644
--- a/crates/re_viewer/src/ui/viewport.rs
+++ b/crates/re_viewer/src/ui/viewport.rs
@@ -9,15 +9,14 @@ use re_data_store::EntityPath;
use crate::{
misc::{space_info::SpaceInfoCollection, Item, SpaceViewHighlights, ViewerContext},
- ui::space_view_heuristics::default_created_space_views,
+ ui::{space_view_heuristics::default_created_space_views, stats_panel::StatsPanel},
};
use super::{
- data_blueprint::{DataBlueprintGroup, DataBlueprintGroupHandle},
+ device_settings_panel::DeviceSettingsPanel, selection_panel::SelectionPanel,
space_view_entity_picker::SpaceViewEntityPicker,
- space_view_heuristics::all_possible_space_views,
- view_category::ViewCategory,
- SpaceView, SpaceViewId,
+ space_view_heuristics::all_possible_space_views, stats_panel::StatsPanelState,
+ view_category::ViewCategory, SpaceView, SpaceViewId, SpaceViewKind,
};
// ----------------------------------------------------------------------------
@@ -40,18 +39,23 @@ pub struct Viewport {
/// One for each combination of what views are visible.
/// So if a user toggles the visibility of one SpaceView, we
/// switch which layout we are using. This is somewhat hacky.
- trees: HashMap>,
+ trees: HashMap>,
/// Show one tab as maximized?
maximized: Option,
- /// Set to `true` the first time the user messes around with the viewport blueprint.
- /// Before this is set we automatically add new spaces to the viewport
- /// when they show up in the data.
- has_been_user_edited: bool,
+ /// Store for each space view if the user has edited it (eg. removed).
+ /// Is reset when a space view get's automatically removed.
+ has_been_user_edited: HashMap,
#[serde(skip)]
space_view_entity_window: Option,
+ device_settings_panel: DeviceSettingsPanel,
+
+ #[serde(skip)]
+ stats_panel_state: StatsPanelState,
+
+ previous_frame_tree: Option>,
}
impl Viewport {
@@ -80,8 +84,11 @@ impl Viewport {
visible,
trees,
maximized,
- has_been_user_edited,
+ has_been_user_edited: _,
space_view_entity_window,
+ device_settings_panel: _,
+ stats_panel_state: _,
+ previous_frame_tree: _,
} = self;
if let Some(window) = space_view_entity_window {
@@ -90,8 +97,6 @@ impl Viewport {
}
}
- *has_been_user_edited = true;
-
trees.retain(|vis_set, _| !vis_set.contains(space_view_id));
if *maximized == Some(*space_view_id) {
@@ -102,229 +107,97 @@ impl Viewport {
space_views.remove(space_view_id)
}
- /// Show the blueprint panel tree view.
- pub fn tree_ui(&mut self, ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) {
+ pub fn add_or_remove_space_views_ui(
+ &mut self,
+ ctx: &mut ViewerContext<'_>,
+ ui: &mut egui::Ui,
+ spaces_info: &SpaceInfoCollection,
+ ) {
crate::profile_function!();
+ let entities_to_remove = ctx.depthai_state.get_entities_to_remove();
+
egui::ScrollArea::vertical()
.auto_shrink([false; 2])
.show(ui, |ui| {
- let space_view_ids = self
- .space_views
- .keys()
- .sorted_by_key(|space_view_id| &self.space_views[space_view_id].space_path)
- .copied()
- .collect_vec();
-
- for space_view_id in &space_view_ids {
- self.space_view_entry_ui(ctx, ui, space_view_id);
+ ui.style_mut().wrap = Some(false);
+
+ // Only show "logical" space views like Color camera, Mono Camera etc, don't go into
+ // details like rerun does in tree_ui, depthai-viewer users don't care about that
+ // as they didn't create the blueprint by logging the data
+ for space_view in all_possible_space_views(ctx, spaces_info)
+ .into_iter()
+ .filter(|sv| {
+ sv.is_depthai_spaceview && !entities_to_remove.contains(&sv.space_path)
+ })
+ {
+ self.available_space_view_row_ui(ctx, ui, space_view);
}
});
}
- /// If a group or spaceview has a total of this number of elements, show its subtree by default?
- fn default_open_for_group(group: &DataBlueprintGroup) -> bool {
- let num_children = group.children.len() + group.entities.len();
- 2 <= num_children && num_children <= 3
- }
-
- fn space_view_entry_ui(
+ fn available_space_view_row_ui(
&mut self,
ctx: &mut ViewerContext<'_>,
ui: &mut egui::Ui,
- space_view_id: &SpaceViewId,
+ space_view: SpaceView,
) {
- let Some(space_view) = self.space_views.get_mut(space_view_id) else {
- re_log::warn_once!("Bug: asked to show a ui for a Space View that doesn't exist");
- return;
- };
- debug_assert_eq!(space_view.id, *space_view_id);
-
- let mut visibility_changed = false;
- let mut removed_space_view = false;
- let mut is_space_view_visible = self.visible.contains(space_view_id);
-
- let root_group = space_view.data_blueprint.root_group();
- let default_open = Self::default_open_for_group(root_group);
- let collapsing_header_id = ui.id().with(space_view.id);
+ let space_path = space_view.space_path.clone(); // to avoid borrowing issue in .body() of collapsing state
+ let collapsing_header_id = ui.id().with(space_view.display_name.clone());
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
collapsing_header_id,
- default_open,
+ true,
)
.show_header(ui, |ui| {
- blueprint_row_with_buttons(
- ctx.re_ui,
- ui,
- true,
- is_space_view_visible,
- |ui| {
- let response = ctx.space_view_button(ui, space_view);
- if response.clicked() {
- if let Some(tree) = self.trees.get_mut(&self.visible) {
- focus_tab(tree, space_view_id);
- }
- }
- response
- },
- |re_ui, ui| {
- visibility_changed =
- visibility_button_ui(re_ui, ui, true, &mut is_space_view_visible).changed();
- removed_space_view = re_ui
- .small_icon_button(ui, &re_ui::icons::REMOVE)
- .on_hover_text("Remove Space View from the viewport.")
- .clicked();
- },
+ ui.label(space_view.display_name.clone());
+ let mut ui = ui.child_ui(
+ ui.max_rect(),
+ egui::Layout::right_to_left(egui::Align::Center),
);
+ if ctx
+ .re_ui
+ .small_icon_button(&mut ui, &re_ui::icons::ADD)
+ .clicked()
+ {
+ self.add_space_view(space_view);
+ }
})
.body(|ui| {
- Self::data_blueprint_tree_ui(
- ctx,
- ui,
- space_view.data_blueprint.root_handle(),
- space_view,
- self.visible.contains(space_view_id),
- );
- });
-
- if removed_space_view {
- self.remove(space_view_id);
- }
-
- if visibility_changed {
- self.has_been_user_edited = true;
- if is_space_view_visible {
- self.visible.insert(*space_view_id);
- } else {
- self.visible.remove(space_view_id);
- }
- }
- }
-
- fn data_blueprint_tree_ui(
- ctx: &mut ViewerContext<'_>,
- ui: &mut egui::Ui,
- group_handle: DataBlueprintGroupHandle,
- space_view: &mut SpaceView,
- space_view_visible: bool,
- ) {
- let Some(group) = space_view.data_blueprint.group(group_handle) else {
- debug_assert!(false, "Invalid group handle in blueprint group tree");
- return;
- };
-
- // TODO(andreas): These clones are workarounds against borrowing multiple times from data_blueprint_tree.
- let children = group.children.clone();
- let entities = group.entities.clone();
- let group_name = group.display_name.clone();
- let group_is_visible = group.properties_projected.visible && space_view_visible;
-
- for entity_path in &entities {
- ui.horizontal(|ui| {
- let mut properties = space_view
- .data_blueprint
- .data_blueprints_individual()
- .get(entity_path);
- blueprint_row_with_buttons(
- ctx.re_ui,
- ui,
- group_is_visible,
- properties.visible,
- |ui| {
- let name = entity_path.iter().last().unwrap().to_string();
- let label = format!("πΉ {name}");
- ctx.data_blueprint_button_to(ui, label, space_view.id, entity_path)
- },
- |re_ui, ui| {
- if visibility_button_ui(
- re_ui,
- ui,
- group_is_visible,
- &mut properties.visible,
- )
- .changed()
- {
- space_view
- .data_blueprint
- .data_blueprints_individual()
- .set(entity_path.clone(), properties);
- }
- if re_ui
- .small_icon_button(ui, &re_ui::icons::REMOVE)
- .on_hover_text("Remove Entity from the space view.")
- .clicked()
- {
- space_view.data_blueprint.remove_entity(entity_path);
- space_view.entities_determined_by_user = true;
- }
- },
- );
- });
- }
-
- for child_group_handle in &children {
- let Some(child_group) = space_view.data_blueprint.group_mut(*child_group_handle) else {
- debug_assert!(false, "Data blueprint group {group_name} has an invalid child");
- continue;
- };
-
- let mut remove_group = false;
- let default_open = Self::default_open_for_group(child_group);
- egui::collapsing_header::CollapsingState::load_with_default_open(
- ui.ctx(),
- ui.id().with(child_group_handle),
- default_open,
- )
- .show_header(ui, |ui| {
- blueprint_row_with_buttons(
- ctx.re_ui,
- ui,
- group_is_visible,
- child_group.properties_individual.visible,
- |ui| {
- ctx.data_blueprint_group_button_to(
- ui,
- child_group.display_name.clone(),
- space_view.id,
- *child_group_handle,
- )
- },
- |re_ui, ui| {
- visibility_button_ui(
- re_ui,
- ui,
- group_is_visible,
- &mut child_group.properties_individual.visible,
- );
- if re_ui
- .small_icon_button(ui, &re_ui::icons::REMOVE)
- .on_hover_text("Remove group and all its children from the space view.")
- .clicked()
- {
- remove_group = true;
- }
- },
- );
- })
- .body(|ui| {
- Self::data_blueprint_tree_ui(
- ctx,
- ui,
- *child_group_handle,
- space_view,
- space_view_visible,
- );
+ let instances_of_space_view = self.visible.iter().filter(|id| {
+ if let Some(sv) = self.space_views.get(*id) {
+ sv.space_path == space_path
+ } else {
+ false
+ }
});
- if remove_group {
- space_view.data_blueprint.remove_group(*child_group_handle);
- space_view.entities_determined_by_user = true;
+ let mut space_views_to_remove = Vec::new();
+ for sv_id in instances_of_space_view {
+ ui.horizontal(|ui| {
+ let label = format!("πΉ {}", self.space_views[sv_id].display_name);
+ ui.label(label);
+ let mut ui = ui.child_ui(
+ ui.max_rect(),
+ egui::Layout::right_to_left(egui::Align::Center),
+ );
+ if ctx
+ .re_ui
+ .small_icon_button(&mut ui, &re_ui::icons::REMOVE)
+ .clicked()
+ {
+ space_views_to_remove.push(*sv_id);
+ self.has_been_user_edited
+ .insert(self.space_views[sv_id].space_path.clone(), true);
+ }
+ });
}
- }
+ for sv_id in &space_views_to_remove {
+ self.remove(sv_id);
+ }
+ });
}
- pub(crate) fn mark_user_interaction(&mut self) {
- self.has_been_user_edited = true;
- }
+ pub(crate) fn mark_user_interaction(&mut self) {}
pub(crate) fn add_space_view(&mut self, mut space_view: SpaceView) -> SpaceViewId {
let id = space_view.id;
@@ -362,17 +235,52 @@ impl Viewport {
spaces_info: &SpaceInfoCollection,
) {
crate::profile_function!();
+ let mut space_views_to_remove = Vec::new();
+
+ // Get all the entity paths that aren't logged anymore
+ let entities_to_remove = ctx.depthai_state.get_entities_to_remove();
+ // First clear the has_been_user_edited entry, so if the entity path is a space path and it reappeaars later,
+ // it will get added back into the viewport
+ for ep in &entities_to_remove {
+ self.has_been_user_edited.insert(ep.clone(), false);
+ }
+ self.stats_panel_state.update(ctx);
+ // Remove all entities that are marked for removal from the space view.
+ // Remove the space view if it has no entities left
for space_view in self.space_views.values_mut() {
- space_view.on_frame_start(ctx, spaces_info);
- }
+ if let Some(group) = space_view
+ .data_blueprint
+ .group(space_view.data_blueprint.root_handle())
+ {
+ for ep in entities_to_remove.iter() {
+ space_view.data_blueprint.remove_entity(ep);
+ }
- if !self.has_been_user_edited {
- for space_view_candidate in default_created_space_views(ctx, spaces_info) {
- if self.should_auto_add_space_view(&space_view_candidate) {
- self.add_space_view(space_view_candidate);
+ if space_view.data_blueprint.entity_paths().is_empty() {
+ space_views_to_remove.push(space_view.id);
+ self.has_been_user_edited
+ .insert(space_view.space_path.clone(), false);
+ continue;
}
}
+ space_view.on_frame_start(ctx, spaces_info);
+ }
+ for id in &space_views_to_remove {
+ if self.space_views.get(id).is_some() {
+ self.remove(id);
+ }
+ }
+ for space_view_candidate in default_created_space_views(ctx, spaces_info) {
+ if !self
+ .has_been_user_edited
+ .get(&space_view_candidate.space_path)
+ .unwrap_or(&false)
+ && !entities_to_remove.contains(&space_view_candidate.space_path)
+ && self.should_auto_add_space_view(&space_view_candidate)
+ {
+ self.add_space_view(space_view_candidate);
+ }
}
}
@@ -418,12 +326,6 @@ impl Viewport {
self.trees.retain(|_, tree| is_tree_valid(tree));
- if let Some(space_view_id) = self.maximized {
- if !self.space_views.contains_key(&space_view_id) {
- self.maximized = None; // protect against bad deserialized data
- }
- }
-
let visible_space_views = if let Some(space_view_id) = self.maximized {
std::iter::once(space_view_id).collect()
} else {
@@ -431,16 +333,30 @@ impl Viewport {
};
// Lazily create a layout tree based on which SpaceViews should be visible:
- let tree = self
+ let mut tree = self
.trees
.entry(visible_space_views.clone())
.or_insert_with(|| {
- super::auto_layout::tree_from_space_views(
- ui.available_size(),
- &visible_space_views,
- &self.space_views,
- )
- });
+ // TODO(filip): Continue working on this smart layout updater
+ if let Some(previous_frame_tree) = &self.previous_frame_tree {
+ let mut tree = previous_frame_tree.clone();
+ super::auto_layout::update_tree(
+ &mut tree,
+ &visible_space_views,
+ &self.space_views,
+ self.maximized.is_some(),
+ );
+ tree
+ } else {
+ super::auto_layout::default_tree_from_space_views(
+ ui.available_size(),
+ &visible_space_views,
+ &self.space_views,
+ )
+ }
+ })
+ .clone();
+ self.previous_frame_tree = Some(tree.clone());
let num_space_views = tree.num_tabs();
if num_space_views == 0 {
@@ -449,7 +365,7 @@ impl Viewport {
let mut tab_viewer = TabViewer {
ctx,
- space_views: &mut self.space_views,
+ viewport: self,
};
ui.scope(|ui| {
@@ -457,7 +373,8 @@ impl Viewport {
ui.spacing_mut().item_spacing.x = re_ui::ReUi::view_padding();
- egui_dock::DockArea::new(tree)
+ egui_dock::DockArea::new(&mut tree)
+ .id(egui::Id::new("space_view_dock"))
.style(re_ui::egui_dock_style(ui.style()))
.show_inside(ui, &mut tab_viewer);
});
@@ -466,16 +383,11 @@ impl Viewport {
let tab_bars = tree
.iter()
.filter_map(|node| {
- let egui_dock::Node::Leaf {
- rect,
- viewport,
- tabs,
- active,
- } = node else {
- return None;
- };
+ let egui_dock::Node::Leaf { rect, viewport, tabs, active } = node else {
+ return None;
+ };
- let space_view_id = tabs.get(active.0)?;
+ let space_view_tab = tabs.get(active.0)?;
// `rect` includes the tab area, while `viewport` is just the tab body.
// so the tab bar rect is:
@@ -485,55 +397,37 @@ impl Viewport {
// rect/viewport can be invalid for the first frame
tab_bar_rect
.is_finite()
- .then_some((*space_view_id, tab_bar_rect))
+ .then_some((space_view_tab.clone(), tab_bar_rect))
})
.collect_vec();
-
- for (space_view_id, tab_bar_rect) in tab_bars {
- // rect/viewport can be invalid for the first frame
- space_view_options_ui(ctx, ui, self, tab_bar_rect, space_view_id, num_space_views);
- }
- }
-
- pub fn add_new_spaceview_button_ui(
- &mut self,
- ctx: &mut ViewerContext<'_>,
- ui: &mut egui::Ui,
- spaces_info: &SpaceInfoCollection,
- ) {
- #![allow(clippy::collapsible_if)]
-
- let icon_image = ctx.re_ui.icon_image(&re_ui::icons::ADD);
- let texture_id = icon_image.texture_id(ui.ctx());
- ui.menu_image_button(texture_id, re_ui::ReUi::small_icon_size(), |ui| {
- ui.style_mut().wrap = Some(false);
-
- for space_view in all_possible_space_views(ctx, spaces_info)
- .into_iter()
- .sorted_by_key(|space_view| space_view.space_path.to_string())
- {
- if ctx
- .re_ui
- .selectable_label_with_icon(
+ self.trees.insert(visible_space_views, tree);
+
+ for (
+ Tab {
+ space_view_id,
+ space_view_kind,
+ ..
+ },
+ tab_bar_rect,
+ ) in tab_bars
+ {
+ match space_view_kind {
+ SpaceViewKind::Data | SpaceViewKind::Stats => {
+ space_view_options_ui(
+ ctx,
ui,
- space_view.category.icon(),
- if space_view.space_path.is_root() {
- space_view.display_name.clone()
- } else {
- space_view.space_path.to_string()
- },
- false,
- )
- .clicked()
- {
- ui.close_menu();
- let new_space_view_id = self.add_space_view(space_view);
- ctx.set_single_selection(Item::SpaceView(new_space_view_id));
+ self,
+ tab_bar_rect,
+ space_view_id,
+ num_space_views,
+ );
+ }
+ SpaceViewKind::Selection => {
+ SelectionPanel::selection_panel_options_ui(ctx, ui, self, tab_bar_rect);
}
+ _ => {}
}
- })
- .response
- .on_hover_text("Add new space view.");
+ }
}
pub fn space_views_containing_entity_path(&self, path: &EntityPath) -> Vec {
@@ -651,51 +545,111 @@ fn visibility_button_ui(
// ----------------------------------------------------------------------------
+struct ViewportView {}
+
struct TabViewer<'a, 'b> {
ctx: &'a mut ViewerContext<'b>,
- space_views: &'a mut HashMap,
+ viewport: &'a mut Viewport,
}
-impl<'a, 'b> egui_dock::TabViewer for TabViewer<'a, 'b> {
- type Tab = SpaceViewId;
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+pub struct Tab {
+ pub space_view_id: SpaceViewId,
+
+ /// Rerun only displayed logged data in the viewport.
+ /// Depthai Viewer also has fixed ui panels that are part of the tab system.
+ /// This is used to distinguish between the two.
+ pub space_view_kind: SpaceViewKind,
+
+ pub space_path: Option,
+}
+
+impl From<&SpaceView> for Tab {
+ fn from(space_view: &SpaceView) -> Self {
+ Self {
+ space_view_id: space_view.id,
+ space_view_kind: SpaceViewKind::Data,
+ space_path: Some(space_view.space_path.clone()),
+ }
+ }
+}
+
+impl From for Tab {
+ fn from(space_view: SpaceView) -> Self {
+ Self::from(&space_view)
+ }
+}
- fn ui(&mut self, ui: &mut egui::Ui, space_view_id: &mut Self::Tab) {
+impl<'a, 'b> egui_dock::TabViewer for TabViewer<'a, 'b> {
+ type Tab = Tab;
+
+ fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
+ let Tab {
+ space_view_id,
+ space_view_kind,
+ space_path,
+ } = tab;
crate::profile_function!();
- let highlights = self
- .ctx
- .selection_state()
- .highlights_for_space_view(*space_view_id, self.space_views);
- let space_view = self
- .space_views
- .get_mut(space_view_id)
- .expect("Should have been populated beforehand");
+ match space_view_kind {
+ SpaceViewKind::Data => {
+ let highlights = self
+ .ctx
+ .selection_state()
+ .highlights_for_space_view(*space_view_id, &self.viewport.space_views);
+ let space_view = self
+ .viewport
+ .space_views
+ .get_mut(space_view_id)
+ .expect("Should have been populated beforehand");
- space_view_ui(self.ctx, ui, space_view, &highlights);
+ space_view_ui(self.ctx, ui, space_view, &highlights);
+ }
+ SpaceViewKind::Selection => SelectionPanel::show_panel(self.ctx, ui, self.viewport),
+ SpaceViewKind::Config => self.viewport.device_settings_panel.show_panel(self.ctx, ui),
+ SpaceViewKind::Stats => {
+ StatsPanel::show_panel(self.ctx, ui, &mut self.viewport.stats_panel_state);
+ }
+ _ => {}
+ }
}
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
- let space_view = self
- .space_views
- .get_mut(tab)
- .expect("Should have been populated beforehand");
-
- let mut text =
- egui::WidgetText::RichText(egui::RichText::new(space_view.display_name.clone()));
-
- if self.ctx.selection().contains(&Item::SpaceView(*tab)) {
- // Show that it is selected:
- let egui_ctx = &self.ctx.re_ui.egui_ctx;
- let selection_bg_color = egui_ctx.style().visuals.selection.bg_fill;
- text = text.background_color(selection_bg_color);
- }
+ match tab.space_view_kind {
+ SpaceViewKind::Data => {
+ let space_view = self
+ .viewport
+ .space_views
+ .get_mut(&tab.space_view_id)
+ .expect("Should have been populated beforehand");
+
+ let mut text = egui::WidgetText::RichText(egui::RichText::new(
+ space_view.display_name.clone(),
+ ));
- text
+ if self
+ .ctx
+ .selection()
+ .contains(&Item::SpaceView(tab.space_view_id))
+ {
+ // Show that it is selected:
+ let egui_ctx = &self.ctx.re_ui.egui_ctx;
+ let selection_bg_color = egui_ctx.style().visuals.selection.bg_fill;
+ text = text.background_color(selection_bg_color);
+ }
+
+ text
+ }
+ SpaceViewKind::Stats => "Stats".into(),
+ SpaceViewKind::Config => "Device Settings".into(),
+ SpaceViewKind::Selection => "Selection".into(),
+ }
}
fn on_tab_button(&mut self, tab: &mut Self::Tab, response: &egui::Response) {
if response.clicked() {
- self.ctx.set_single_selection(Item::SpaceView(*tab));
+ self.ctx
+ .set_single_selection(Item::SpaceView(tab.space_view_id));
}
}
}
@@ -706,6 +660,7 @@ fn help_text_ui(ui: &mut egui::Ui, space_view: &SpaceView) {
ViewCategory::BarChart => Some(crate::ui::view_bar_chart::HELP_TEXT),
ViewCategory::Spatial => Some(space_view.view_state.state_spatial.help_text()),
ViewCategory::Text | ViewCategory::Tensor => None,
+ ViewCategory::NodeGraph => None,
};
if let Some(help_text) = help_text {
@@ -722,8 +677,6 @@ fn space_view_options_ui(
space_view_id: SpaceViewId,
num_space_views: usize,
) {
- let Some(space_view) = viewport.space_views.get(&space_view_id) else { return; };
-
let tab_bar_rect = tab_bar_rect.shrink2(egui::vec2(4.0, 0.0)); // Add some side margin outside the frame
ui.allocate_ui_at_rect(tab_bar_rect, |ui| {
@@ -754,6 +707,56 @@ fn space_view_options_ui(
ctx.set_single_selection(Item::SpaceView(space_view_id));
}
}
+ let Some(space_view) = viewport.space_views.get_mut(&space_view_id) else {
+ return;
+ };
+
+ let icon_image = ctx.re_ui.icon_image(&re_ui::icons::GEAR);
+ let texture_id = icon_image.texture_id(ui.ctx());
+ ui.menu_image_button(texture_id, re_ui::ReUi::small_icon_size(), |ui| {
+ ui.style_mut().wrap = Some(false);
+ let entities = space_view.data_blueprint.entity_paths().clone();
+ let entities = entities.iter().filter(|ep| {
+ let eps_to_skip = vec![
+ EntityPath::from("color/camera/rgb"),
+ EntityPath::from("color/camera"),
+ EntityPath::from("mono/camera"),
+ EntityPath::from("mono/camera/left_mono"),
+ EntityPath::from("mono/camera/right_mono"),
+ ];
+ !eps_to_skip.contains(ep)
+ });
+ for entity_path in entities {
+ // if matches!(entity_path, EntityPath::from("color"))
+ ui.horizontal(|ui| {
+ let mut properties = space_view
+ .data_blueprint
+ .data_blueprints_individual()
+ .get(entity_path);
+ blueprint_row_with_buttons(
+ ctx.re_ui,
+ ui,
+ true,
+ properties.visible,
+ |ui| {
+ let name = entity_path.iter().last().unwrap().to_string();
+ let label = format!("πΉ {name}");
+ ctx.data_blueprint_button_to(ui, label, space_view.id, entity_path)
+ },
+ |re_ui, ui| {
+ if visibility_button_ui(re_ui, ui, true, &mut properties.visible)
+ .changed()
+ {
+ space_view
+ .data_blueprint
+ .data_blueprints_individual()
+ .set(entity_path.clone(), properties);
+ }
+ },
+ );
+ });
+ }
+ });
// Show help last, since not all space views have help text
help_text_ui(ui, space_view);
@@ -782,7 +785,7 @@ fn space_view_ui(
ui.centered_and_justified(|ui| {
ui.label(ctx.re_ui.warning_text("No time selected"));
});
- return
+ return;
};
space_view.scene_ui(ctx, ui, latest_at, space_view_highlights);
@@ -790,14 +793,14 @@ fn space_view_ui(
// ----------------------------------------------------------------------------
-fn focus_tab(tree: &mut egui_dock::Tree, tab: &SpaceViewId) {
+fn focus_tab(tree: &mut egui_dock::Tree, tab: &Tab) {
if let Some((node_index, tab_index)) = tree.find_tab(tab) {
tree.set_focused_node(node_index);
tree.set_active_tab(node_index, tab_index);
}
}
-fn is_tree_valid(tree: &egui_dock::Tree) -> bool {
+fn is_tree_valid(tree: &egui_dock::Tree) -> bool {
tree.iter().all(|node| match node {
egui_dock::Node::Vertical { rect: _, fraction }
| egui_dock::Node::Horizontal { rect: _, fraction } => fraction.is_finite(),
diff --git a/crates/re_viewer/src/viewer_analytics.rs b/crates/re_viewer/src/viewer_analytics.rs
index 8269f3c19cc4..02727b590127 100644
--- a/crates/re_viewer/src/viewer_analytics.rs
+++ b/crates/re_viewer/src/viewer_analytics.rs
@@ -1,4 +1,4 @@
-//! All telemetry analytics collected by the Rerun Viewer are defined in this file for easy auditing.
+//! All telemetry analytics collected by the Depthai Viewer are defined in this file for easy auditing.
//!
//! There are two exceptions:
//! * `crates/rerun/src/crash_handler.rs` sends anonymized callstacks on crashes
@@ -67,7 +67,7 @@ impl ViewerAnalytics {
// ----------------------------------------------------------------------------
-/// Here follows all the analytics collected by the Rerun Viewer.
+/// Here follows all the analytics collected by the Depthai Viewer.
#[cfg(all(not(target_arch = "wasm32"), feature = "analytics"))]
impl ViewerAnalytics {
/// When the viewer is first started
diff --git a/crates/re_viewer/src/web.rs b/crates/re_viewer/src/web.rs
index 58dcec080ce2..65cf3312ce8c 100644
--- a/crates/re_viewer/src/web.rs
+++ b/crates/re_viewer/src/web.rs
@@ -24,8 +24,9 @@ pub async fn start(
let web_options = eframe::WebOptions {
follow_system_theme: false,
- default_theme: eframe::Theme::Dark,
+ default_theme: eframe::Theme::Light,
wgpu_options: crate::wgpu_options(),
+ depth_buffer: 0,
};
eframe::start_web(
@@ -38,7 +39,7 @@ pub async fn start(
let startup_options = crate::StartupOptions {
memory_limit: re_memory::MemoryLimit {
// On wasm32 we only have 4GB of memory to play around with.
- limit: Some(3_500_000_000),
+ limit: Some(2_500_000_000),
},
persist_state,
};
diff --git a/crates/re_web_viewer_server/build.rs b/crates/re_web_viewer_server/build.rs
index b072925e41e5..79b8fe048136 100644
--- a/crates/re_web_viewer_server/build.rs
+++ b/crates/re_web_viewer_server/build.rs
@@ -89,19 +89,29 @@ impl<'a> Packages<'a> {
}
}
+fn get_and_track_env_var(env_var_name: &str) -> Result {
+ println!("cargo:rerun-if-env-changed={env_var_name}");
+ std::env::var(env_var_name)
+}
+
+fn is_tracked_env_var_set(env_var_name: &str) -> bool {
+ let var = get_and_track_env_var(env_var_name).map(|v| v.to_lowercase());
+ var == Ok("1".to_owned()) || var == Ok("yes".to_owned()) || var == Ok("true".to_owned())
+}
+
fn main() {
- if std::env::var("IS_IN_RERUN_WORKSPACE") != Ok("yes".to_owned()) {
+ if !is_tracked_env_var_set("IS_IN_RERUN_WORKSPACE") {
// Only run if we are in the rerun workspace, not on users machines.
return;
}
- if std::env::var("RERUN_IS_PUBLISHING") == Ok("yes".to_owned()) {
+ if is_tracked_env_var_set("RERUN_IS_PUBLISHING") {
// We don't need to rebuild - we should have done so beforehand!
// See `RELEASES.md`
return;
}
// Rebuild the web-viewer Wasm,
- // because the web_server library bundles it with `include_bytes!`
+ // because the web_server library bundles it with `include_bytes!`.
let metadata = MetadataCommand::new()
.features(CargoOpt::AllFeatures)
@@ -118,12 +128,12 @@ fn main() {
// or patched!).
pkgs.track_implicit_dep("re_viewer");
- if std::env::var("CARGO_FEATURE___CI").is_ok() {
+ if get_and_track_env_var("CARGO_FEATURE___CI").is_ok() {
// If the `__ci` feature is set we skip building the web viewer wasm, saving a lot of time.
// This feature is set on CI (hence the name), but also with `--all-features`, which is set by rust analyzer, bacon, etc.
eprintln!("__ci feature detected: Skipping building of web viewer wasm.");
} else {
let release = std::env::var("PROFILE").unwrap() == "release";
- re_build_web_viewer::build(release);
+ re_build_web_viewer::build(release, is_tracked_env_var_set("RERUN_BUILD_WEBGPU"));
}
}
diff --git a/crates/re_web_viewer_server/src/lib.rs b/crates/re_web_viewer_server/src/lib.rs
index 4d8f29172588..e6964931c490 100644
--- a/crates/re_web_viewer_server/src/lib.rs
+++ b/crates/re_web_viewer_server/src/lib.rs
@@ -211,7 +211,7 @@ impl WebViewerServer {
pub fn new(port: WebViewerServerPort) -> Result