use std::sync::{Arc, Mutex};
use bevy::prelude::*;
use bevy::render::extract_resource::{ExtractResource, ExtractResourcePlugin};
use bevy::render::render_asset::RenderAssets;
use bevy::render::renderer::{RenderDevice, RenderQueue};
use bevy::render::texture::GpuImage;
use bevy::render::{Render, RenderApp, RenderSystems};
#[derive(Clone)]
pub struct FrameCopy {
pub buffer: wgpu::Buffer,
pub image: AssetId<Image>,
pub width: u32,
pub height: u32,
}
#[derive(Resource, Clone, ExtractResource)]
pub struct FrameHandoff(pub Arc<Mutex<Option<FrameCopy>>>);
impl Default for FrameHandoff {
fn default() -> Self {
Self(Arc::new(Mutex::new(None)))
}
}
impl FrameHandoff {
pub fn publish(&self, copy: FrameCopy) {
if let Ok(mut slot) = self.0.lock() {
*slot = Some(copy);
}
}
}
pub struct FrameBlitPlugin;
impl Plugin for FrameBlitPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<FrameHandoff>()
.add_plugins(ExtractResourcePlugin::<FrameHandoff>::default());
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(Render, blit_frame.in_set(RenderSystems::Prepare));
}
}
}
fn blit_frame(
handoff: Res<FrameHandoff>,
images: Res<RenderAssets<GpuImage>>,
device: Res<RenderDevice>,
queue: Res<RenderQueue>,
) {
let job = match handoff.0.lock() {
Ok(slot) => slot.clone(),
Err(_) => return,
};
let Some(job) = job else { return };
let Some(target) = images.get(job.image) else { return };
if target.size.width != job.width || target.size.height != job.height {
return;
}
let mut enc = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("mir-frame-blit"),
});
enc.copy_buffer_to_texture(
wgpu::TexelCopyBufferInfo {
buffer: &job.buffer,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(job.width * 4),
rows_per_image: Some(job.height),
},
},
wgpu::TexelCopyTextureInfo {
texture: &target.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::Extent3d {
width: job.width,
height: job.height,
depth_or_array_layers: 1,
},
);
queue.submit([enc.finish()]);
}