Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
bevy v0.14
Browse files Browse the repository at this point in the history
  • Loading branch information
porkbrain authored Jul 5, 2024
2 parents 77e70ae + 0df5dad commit 454f8f4
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 43 deletions.
26 changes: 18 additions & 8 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,31 @@ name: Rust

on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]
branches: ["main"]

env:
CARGO_TERM_COLOR: always

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- uses: actions/checkout@v4

- name: Install dependencies
run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev
- name: Version check
run: rustc --version && cargo --version && rustfmt --version && cargo clippy --version
- name: Format
run: cargo fmt -- --check
- name: Check
run: cargo check
- name: Clippy
run: cargo clippy -- -D warnings
- name: Test
run: cargo test
- name: Broken docs
run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --offline
16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bevy_webp_anim"
version = "0.3.1"
version = "0.4.0"
edition = "2021"
license = "MIT OR Apache-2.0"
keywords = ["bevy", "webp", "video", "animation"]
Expand All @@ -12,20 +12,22 @@ exclude = ["examples", "assets"]


[dependencies]
image = "0.24"
image = "0.25"
image-webp = "*" # we want the one that image uses
thiserror = "1.0"
tokio = { version = "1.35", features = ["rt", "sync", "rt-multi-thread"] }

uuid = "1.7"

[dependencies.bevy]
default-features = false
features = ["bevy_asset", "bevy_render", "bevy_sprite"]
version = "0.13"
version = "0.14"


[dev-dependencies]
bevy = { version = "0.14", default-features = true }


[[example]]
name = "basic"
path = "examples/basic.rs"

[dev-dependencies]
bevy = { version = "0.13", default-features = true }
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ This would be useful if you play the same video in several places on your screen
Right now, you'd need separate decoders and separate `Handle<Image>`.

I don't need this feature in my project at the moment, but happy to add it if there's a demand.

## Issue: Explore bevy's work load system instead of a custom tokio runtime

Bevy comes with its own thread pool that can run tasks.
It might be preferable to use this instead of spawning another tokio runtime.
Binary file added assets/image_crate_example.webp
Binary file not shown.
2 changes: 1 addition & 1 deletion examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() {
bevy_webp_anim::systems::load_next_frame,
),
)
.run()
.run();
}

fn spawn_camera(mut commands: Commands) {
Expand Down
8 changes: 7 additions & 1 deletion src/loader.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use bevy::{
asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext},
prelude::*,
utils::ConditionalSendFuture,
};
use thiserror::Error;

Expand All @@ -27,7 +28,12 @@ impl AssetLoader for WebpLoader {
reader: &'a mut Reader,
_settings: &'a Self::Settings,
load_context: &'a mut LoadContext,
) -> bevy::utils::BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
) -> impl ConditionalSendFuture<
Output = Result<
<Self as AssetLoader>::Asset,
<Self as AssetLoader>::Error,
>,
> {
Box::pin(async move {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
Expand Down
89 changes: 63 additions & 26 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::{collections::BTreeMap, marker::PhantomData};
use std::{collections::BTreeMap, io::Cursor, marker::PhantomData};

use bevy::{
prelude::*,
render::{
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
},
utils::Uuid,
};
use image::{codecs::webp::WebPDecoder, AnimationDecoder};
use image::{codecs::webp::WebPDecoder, AnimationDecoder, ImageError};
use tokio::sync::mpsc::{Receiver, Sender};
pub use tokio::{runtime, sync::mpsc::error::TryRecvError};
use uuid::Uuid;

/// See [`WebpAnimator::prepared_frames_count`].
pub const DEFAULT_PREPARED_FRAMES_COUNT: usize = 16;
Expand Down Expand Up @@ -185,38 +185,75 @@ impl WebpVideo {
pub async fn produce(self, animation_frames: Sender<Image>) {
let WebpVideo { bytes, label } = self;

let decoder = match WebPDecoder::new(Cursor::new(bytes)) {
Ok(decoder) => decoder,
Err(e) => {
error!("Cannot construct webp decoder {label}: {e}");
return;
}
};

// We decode each frame only once and then play them over and
// over.
// This is optimized for replaying and low CPU usage.
//
// TODO: Enable an alternative with lower memory usage and
// faster startup times where we decode each frame every time
// it is played.
match WebPDecoder::new(bytes.as_slice())
.and_then(|decoder| decoder.into_frames().collect_frames())
{
Ok(frames) => loop {
for frame in &frames {
let (width, height) = frame.buffer().dimensions();
let image = Image::new(
Extent3d {
width,
height,
..default()
},
TextureDimension::D2,
frame.clone().into_buffer().into_raw(),
TextureFormat::Rgba8Unorm,
RenderAssetUsages::RENDER_WORLD,
);

// animation no longer required
if animation_frames.send(image).await.is_err() {
break;
// we need to collect because we send the frames over an async channel
let frames: Vec<_> = decoder
.into_frames()
.take_while(|frame_res| match frame_res {
Ok(_) => true,
Err(ImageError::Decoding(err)) => {
use std::error::Error;
if let Some(image_webp::DecodingError::NoMoreFrames) =
err.source().and_then(|err| {
err.downcast_ref::<image_webp::DecodingError>()
})
{
// iterator ended
// TODO: https://github.com/image-rs/image/issues/2263
} else {
error!(
"Cannot decode webp frame from video {label}: {:?}",
err.source()
);
}

false
}
},
Err(e) => error!("Cannot load webp video {label}: {e}"),
};
Err(e) => {
error!(
"Cannot collect webp frames from video {label}: {e}"
);
false
}
})
.filter_map(Result::ok)
.collect();

loop {
for frame in &frames {
let (width, height) = frame.buffer().dimensions();
let image = Image::new(
Extent3d {
width,
height,
..default()
},
TextureDimension::D2,
frame.clone().into_buffer().into_raw(),
TextureFormat::Rgba8Unorm,
RenderAssetUsages::RENDER_WORLD,
);

// animation no longer required
if animation_frames.send(image).await.is_err() {
break;
}
}
}
}
}

0 comments on commit 454f8f4

Please sign in to comment.