Skip to content

Commit

Permalink
refactor: move logic parts to crates
Browse files Browse the repository at this point in the history
  • Loading branch information
cs50victor committed Jan 24, 2024
1 parent 8cfe9ff commit 9cea76d
Show file tree
Hide file tree
Showing 15 changed files with 693 additions and 416 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# Rust
target/
Cargo.lock

# ggml binaries & other binaries in general
*.bin
Expand Down
25 changes: 19 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["new_media"]
members = ["crates/*"]

package.rust-version = "1.75.0"

Expand All @@ -19,3 +19,20 @@ panic = "abort" # Abort on panic, instead of unwinding the stack.
[profile.prod]
inherits = "release"
strip = true # Automatically strip symbols from the binary.


[workspace.dependencies]
anyhow = "1.0.75"
async-net = "2.0.0"
base64 = "0.21.5"
bevy = { version = "0.12.1", default-features = false, features = [
"bevy_asset",
"bevy_core_pipeline",
"bevy_render",
] }
bevy_panorbit_camera = "0.10.0"
crossbeam-channel = "0.5.10"
log = "0.4.20"
pretty_env_logger = "0.5.0"
wgpu = { version = "0.17.1", features = ["vulkan-portability"] }
tungstenite = "0.21.0"
17 changes: 17 additions & 0 deletions crates/bevy_frame_capture/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "bevy_frame_capture"
version = "0.1.0"
edition = "2021"
rust-version.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = { workspace = true }
bevy = { workspace = true }
base64 = { workspace = true }
image = { version = "0.24.7", default-features = false, features = ["png"] }
pollster = "0.3.0"
wgpu = { workspace = true }
log = { workspace = true }
futures-intrusive = "0.5.0"
181 changes: 181 additions & 0 deletions crates/bevy_frame_capture/src/image_copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
use std::sync::Arc;

use bevy::{
prelude::*,
render::{
render_asset::RenderAssets,
render_graph::{self, NodeRunError, RenderGraph, RenderGraphContext},
renderer::{RenderContext, RenderDevice, RenderQueue},
Extract, RenderApp,
},
};

use bevy::render::render_resource::{
Buffer, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d,
ImageCopyBuffer, ImageDataLayout, MapMode,
};
use pollster::FutureExt;
use wgpu::Maintain;

use std::sync::atomic::{AtomicBool, Ordering};

pub fn receive_images(
image_copiers: Query<&ImageCopier>,
mut images: ResMut<Assets<Image>>,
render_device: Res<RenderDevice>,
) {
for image_copier in image_copiers.iter() {
if !image_copier.enabled() {
continue;
}
// Derived from: https://sotrh.github.io/learn-wgpu/showcase/windowless/#a-triangle-without-a-window
// We need to scope the mapping variables so that we can
// unmap the buffer
async {
let buffer_slice = image_copier.buffer.slice(..);

// NOTE: We have to create the mapping THEN device.poll() before await
// the future. Otherwise the application will freeze.
let (tx, rx) = futures_intrusive::channel::shared::oneshot_channel();
buffer_slice.map_async(MapMode::Read, move |result| {
tx.send(result).unwrap();
});

render_device.poll(Maintain::Wait);

rx.receive().await.unwrap().unwrap();
if let Some(image) = images.get_mut(&image_copier.dst_image) {
image.data = buffer_slice.get_mapped_range().to_vec();
}

image_copier.buffer.unmap();
}
.block_on();
}
}

pub const IMAGE_COPY: &str = "image_copy";

pub struct ImageCopyPlugin;
impl Plugin for ImageCopyPlugin {
fn build(&self, app: &mut App) {
let render_app = app.add_systems(Update, receive_images).sub_app_mut(RenderApp);

render_app.add_systems(ExtractSchedule, image_copy_extract);

let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();

graph.add_node(IMAGE_COPY, ImageCopyDriver);

graph.add_node_edge(IMAGE_COPY, bevy::render::main_graph::node::CAMERA_DRIVER);
}
}

#[derive(Clone, Default, Resource, Deref, DerefMut)]
pub struct ImageCopiers(pub Vec<ImageCopier>);

#[derive(Clone, Component)]
pub struct ImageCopier {
buffer: Buffer,
enabled: Arc<AtomicBool>,
src_image: Handle<Image>,
dst_image: Handle<Image>,
}

impl ImageCopier {
pub fn new(
src_image: Handle<Image>,
dst_image: Handle<Image>,
size: Extent3d,
render_device: &RenderDevice,
) -> ImageCopier {
let padded_bytes_per_row =
RenderDevice::align_copy_bytes_per_row((size.width) as usize) * 4;

let cpu_buffer = render_device.create_buffer(&BufferDescriptor {
label: None,
size: padded_bytes_per_row as u64 * size.height as u64,
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
mapped_at_creation: false,
});

ImageCopier {
buffer: cpu_buffer,
src_image,
dst_image,
enabled: Arc::new(AtomicBool::new(true)),
}
}

pub fn enabled(&self) -> bool {
self.enabled.load(Ordering::Relaxed)
}
}

pub fn image_copy_extract(mut commands: Commands, image_copiers: Extract<Query<&ImageCopier>>) {
commands.insert_resource(ImageCopiers(
image_copiers.iter().cloned().collect::<Vec<ImageCopier>>(),
));
}

#[derive(Default)]
pub struct ImageCopyDriver;

impl render_graph::Node for ImageCopyDriver {
fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let image_copiers = world.get_resource::<ImageCopiers>().unwrap();
let gpu_images = world.get_resource::<RenderAssets<Image>>().unwrap();

for image_copier in image_copiers.iter() {
if !image_copier.enabled() {
continue;
}

let src_image = gpu_images.get(&image_copier.src_image).unwrap();

let mut encoder = render_context
.render_device()
.create_command_encoder(&CommandEncoderDescriptor::default());

let block_dimensions = src_image.texture_format.block_dimensions();
let block_size = src_image.texture_format.block_size(None).unwrap();

let padded_bytes_per_row = RenderDevice::align_copy_bytes_per_row(
(src_image.size.x as usize / block_dimensions.0 as usize) * block_size as usize,
);

let texture_extent = Extent3d {
width: src_image.size.x as u32,
height: src_image.size.y as u32,
depth_or_array_layers: 1,
};

encoder.copy_texture_to_buffer(
src_image.texture.as_image_copy(),
ImageCopyBuffer {
buffer: &image_copier.buffer,
layout: ImageDataLayout {
offset: 0,
bytes_per_row: Some(
std::num::NonZeroU32::new(padded_bytes_per_row as u32)
.unwrap()
.into(),
),
rows_per_image: None,
},
},
texture_extent,
);

let render_queue = world.get_resource::<RenderQueue>().unwrap();
render_queue.submit(std::iter::once(encoder.finish()));
}

Ok(())
}
}
3 changes: 3 additions & 0 deletions crates/bevy_frame_capture/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// Derived from: https://github.com/bevyengine/bevy/pull/5550
pub mod scene;
pub mod image_copy;
Loading

0 comments on commit 9cea76d

Please sign in to comment.