Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require nasm for AV1 decoding, unless on arm64 macOS #7742

Merged
merged 6 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/contrib_rerun_py.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ jobs:
# this stops `re_web_viewer_server/build.rs` from running
RERUN_IS_PUBLISHING: true
run: |
cargo build \
pixi run cargo build \
--locked \
-p rerun-cli \
--no-default-features \
--features native_viewer,web_viewer \
--features release \
--release \
--target x86_64-unknown-linux-gnu

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/reusable_build_and_upload_rerun_cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,11 @@ jobs:
# this stops `re_web_viewer_server/build.rs` from running
RERUN_IS_PUBLISHING: true
run: |
cargo build \
pixi run cargo build \
--locked \
-p rerun-cli \
--no-default-features \
--features native_viewer,web_viewer \
--features release \
--release \
--target ${{ needs.set-config.outputs.TARGET }}

Expand Down
3 changes: 1 addition & 2 deletions crates/store/re_video/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ av1 = ["dep:dav1d"]

## Enable faster native video decoding with assembly.
## You need to install [nasm](https://nasm.us/) to compile with this feature.
# TODO(#7671): this feature flag currently does nothing on Linux.
nasm = [
# The default feature set of our dav1d fork has asm enabled (except on Linux, see above)
# The default feature set of our dav1d fork has asm enabled
"dav1d?/default",
]

Expand Down
24 changes: 16 additions & 8 deletions crates/store/re_video/src/decode/av1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,22 @@ impl SyncDav1dDecoder {
pub fn new(debug_name: String) -> Result<Self> {
re_tracing::profile_function!();

// TODO(#7671): enable this warning again on Linux when the `nasm` feature actually does something
#[allow(clippy::overly_complex_bool_expr)]
if !cfg!(target_os = "linux") && !cfg!(feature = "nasm") {
re_log::warn_once!(
"NOTE: native AV1 video decoder is running extra slowly. \
Speed it up by compiling Rerun with the `nasm` feature enabled. \
You'll need to also install nasm: https://nasm.us/"
);
if !cfg!(feature = "nasm") {
// The `nasm` feature makes AV1 decoding much faster.
// On Linux the difference is huge (~25x).
// On Windows, the difference was also pretty big (unsure how big).
// On an M3 Mac the difference is smalelr (2-3x),
// and ever without `nasm` emilk can play an 8k video at 2x speed.

if cfg!(target_os = "macos") && cfg!(target_arch = "aarch64") {
re_log::warn_once!(
"The native AV1 video decoder is unnecessarily slow. \
Speed it up by compiling Rerun with the `nasm` feature enabled."
);
} else {
// Better to return an error than to be perceived as being slow
return Err(Error::Dav1dWithoutNasm);
}
}

// See https://videolan.videolan.me/dav1d/structDav1dSettings.html for settings docs
Expand Down
5 changes: 5 additions & 0 deletions crates/store/re_video/src/decode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ pub enum Error {
#[cfg(not(target_arch = "wasm32"))]
#[error("dav1d: {0}")]
Dav1d(#[from] dav1d::Error),

#[cfg(feature = "av1")]
#[cfg(not(target_arch = "wasm32"))]
#[error("To enabled native AV1 decoding, compile Rerun with the `nasm` feature enabled.")]
Dav1dWithoutNasm,
}

pub type Result<T = (), E = Error> = std::result::Result<T, E>;
Expand Down
107 changes: 16 additions & 91 deletions crates/store/re_video/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,102 +1,27 @@
//! Video decoding library.

mod time;

pub mod decode;
pub mod demux;

pub use decode::{Chunk, Frame, PixelFormat};
pub use demux::{Config, Sample, VideoData, VideoLoadError};
pub use re_mp4::{TrackId, TrackKind};

/// A value in time units.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Time(i64);

impl Time {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(i64::MAX);

/// Create a new value in _time units_.
///
/// ⚠️ Don't use this for regular timestamps in seconds/milliseconds/etc.,
/// use the proper constructors for those instead!
/// This only exists for cases where you already have a value expressed in time units,
/// such as those received from the `WebCodecs` APIs.
#[inline]
pub fn new(v: i64) -> Self {
Self(v)
}

#[inline]
pub fn from_secs(v: f64, timescale: Timescale) -> Self {
Self((v * timescale.0 as f64).round() as i64)
}

#[inline]
pub fn from_millis(v: f64, timescale: Timescale) -> Self {
Self::from_secs(v / 1e3, timescale)
}

#[inline]
pub fn from_micros(v: f64, timescale: Timescale) -> Self {
Self::from_secs(v / 1e6, timescale)
}

#[inline]
pub fn from_nanos(v: i64, timescale: Timescale) -> Self {
Self::from_secs(v as f64 / 1e9, timescale)
}

/// Convert to a duration
#[inline]
pub fn duration(self, timescale: Timescale) -> std::time::Duration {
std::time::Duration::from_nanos(self.into_nanos(timescale) as _)
}

#[inline]
pub fn into_secs(self, timescale: Timescale) -> f64 {
self.0 as f64 / timescale.0 as f64
}
pub use self::{
decode::{Chunk, Frame, PixelFormat},
demux::{Config, Sample, VideoData, VideoLoadError},
time::{Time, Timescale},
};

#[inline]
pub fn into_millis(self, timescale: Timescale) -> f64 {
self.into_secs(timescale) * 1e3
/// Which features was this crate compiled with?
pub fn features() -> Vec<&'static str> {
// TODO(emilk): is there a helper crate for this?
Comment on lines +16 to +18
Copy link
Member Author

@emilk emilk Oct 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to do something smarter in a follow-up PR

let mut features = vec![];
if cfg!(feature = "av1") {
features.push("av1");
}

#[inline]
pub fn into_micros(self, timescale: Timescale) -> f64 {
self.into_secs(timescale) * 1e6
}

#[inline]
pub fn into_nanos(self, timescale: Timescale) -> i64 {
(self.into_secs(timescale) * 1e9).round() as i64
}
}

impl std::ops::Add for Time {
type Output = Self;

#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self(self.0.saturating_add(rhs.0))
}
}

impl std::ops::Sub for Time {
type Output = Self;

#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0.saturating_sub(rhs.0))
}
}

/// The number of time units per second.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timescale(u64);

impl Timescale {
pub(crate) fn new(v: u64) -> Self {
Self(v)
if cfg!(feature = "nasm") {
features.push("nasm");
}
features
}
93 changes: 93 additions & 0 deletions crates/store/re_video/src/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/// The number of time units per second.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timescale(u64);

impl Timescale {
pub(crate) fn new(v: u64) -> Self {
Self(v)
}
}

/// A value in time units.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Time(pub i64);

impl Time {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(i64::MAX);

/// Create a new value in _time units_.
///
/// ⚠️ Don't use this for regular timestamps in seconds/milliseconds/etc.,
/// use the proper constructors for those instead!
/// This only exists for cases where you already have a value expressed in time units,
/// such as those received from the `WebCodecs` APIs.
#[inline]
pub fn new(v: i64) -> Self {
Self(v)
}

#[inline]
pub fn from_secs(v: f64, timescale: Timescale) -> Self {
Self((v * timescale.0 as f64).round() as i64)
}

#[inline]
pub fn from_millis(v: f64, timescale: Timescale) -> Self {
Self::from_secs(v / 1e3, timescale)
}

#[inline]
pub fn from_micros(v: f64, timescale: Timescale) -> Self {
Self::from_secs(v / 1e6, timescale)
}

#[inline]
pub fn from_nanos(v: i64, timescale: Timescale) -> Self {
Self::from_secs(v as f64 / 1e9, timescale)
}

/// Convert to a duration
#[inline]
pub fn duration(self, timescale: Timescale) -> std::time::Duration {
std::time::Duration::from_nanos(self.into_nanos(timescale) as _)
}

#[inline]
pub fn into_secs(self, timescale: Timescale) -> f64 {
self.0 as f64 / timescale.0 as f64
}

#[inline]
pub fn into_millis(self, timescale: Timescale) -> f64 {
self.into_secs(timescale) * 1e3
}

#[inline]
pub fn into_micros(self, timescale: Timescale) -> f64 {
self.into_secs(timescale) * 1e6
}

#[inline]
pub fn into_nanos(self, timescale: Timescale) -> i64 {
(self.into_secs(timescale) * 1e9).round() as i64
}
}

impl std::ops::Add for Time {
type Output = Self;

#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self(self.0.saturating_add(rhs.0))
}
}

impl std::ops::Sub for Time {
type Output = Self;

#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0.saturating_sub(rhs.0))
}
}
11 changes: 8 additions & 3 deletions crates/top/rerun-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,18 @@ path = "src/bin/rerun.rs"
doc = false

[features]
# The default is what the user gets when they call `cargo install rerun-cli --locked`,
# so wer have all the bells and wistles here
## The default is what the user gets when they call `cargo install rerun-cli --locked`,
## so we have all the bells and wistles here, except those that may require extra tools
## (like "nasm").
## That is: `cargo install rerun-cli --locked` should work for _everyone_.
default = ["native_viewer", "web_viewer"]

## The features we enable when we build the pre-built binaries during our releases.
## This may enable features that require extra build tools that not everyone heas.
release = ["default", "nasm"]

## Enable faster native video decoding with assembly.
## You need to install [nasm](https://nasm.us/) to compile with this feature.
# TODO(#7671): this feature flag currently does nothing on linux.
nasm = ["rerun/nasm"]

## Support spawning a native viewer.
Expand Down
1 change: 0 additions & 1 deletion crates/top/rerun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ log = ["dep:env_logger", "dep:log"]

## Enable faster native video decoding with assembly.
## You need to install [nasm](https://nasm.us/) to compile with this feature.
# TODO(#7671): this feature flag currently does nothing on linux.
nasm = ["re_video/nasm"]

## Support spawning a native viewer.
Expand Down
1 change: 1 addition & 0 deletions crates/top/rerun/src/commands/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ where

if args.version {
println!("{build_info}");
println!("Video features: {}", re_video::features().join(" "));
return Ok(0);
}

Expand Down
1 change: 0 additions & 1 deletion rerun_py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ extension-module = ["pyo3/extension-module"]

## Enable faster native video decoding with assembly.
## You need to install [nasm](https://nasm.us/) to compile with this feature.
# TODO(#7671): this feature flag currently does nothing on linux.
nasm = ["re_video/nasm"]

## Support serving a web viewer over HTTP with `serve()`.
Expand Down
Loading