use std::io::Read;
use std::num::NonZeroU32;
use std::ops::Deref;
use bevy_ecs::system::{Res, ResMut};
use bevy_render::render_asset::RenderAssets;
use bevy_render::render_resource::TextureFormat;
use bevy_render::renderer::{RenderDevice, RenderQueue};
use bevy_render::texture::{Image, ImageFormat, TextureFormatPixelInfo};
use wgpu::{
BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, ImageCopyBuffer,
ImageDataLayout, Maintain, TextureDescriptor, COPY_BYTES_PER_ROW_ALIGNMENT,
};
use crate::data::SharedDataSmuggler;
pub fn align_byte_size(value: u32) -> u32 {
value + (COPY_BYTES_PER_ROW_ALIGNMENT - (value % COPY_BYTES_PER_ROW_ALIGNMENT))
}
pub fn get_aligned_size(width: u32, height: u32, pixel_size: u32) -> u32 {
height * align_byte_size(width * pixel_size)
}
pub fn layout_data(width: u32, height: u32, format: TextureFormat) -> ImageDataLayout {
ImageDataLayout {
bytes_per_row: if height > 1 {
NonZeroU32::new(get_aligned_size(width, 1, format.pixel_size() as u32))
} else {
None
},
rows_per_image: None,
..Default::default()
}
}
pub fn smuggle_frame(
mut smugglers: ResMut<SharedDataSmuggler>,
images: Res<RenderAssets<Image>>,
render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>,
) {
let mut smugglers = smugglers.lock().unwrap();
for (_id, mut recorder) in smugglers.iter_mut() {
if let Some(image) = images.get(&recorder.target_handle) {
let width = image.size.x as u32;
let height = image.size.y as u32;
let device = render_device.wgpu_device();
let destination = device.create_buffer(&BufferDescriptor {
label: None,
size: get_aligned_size(width, height, image.texture_format.pixel_size() as u32)
as u64,
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let texture = image.texture.clone();
let mut encoder =
render_device.create_command_encoder(&CommandEncoderDescriptor { label: None });
encoder.copy_texture_to_buffer(
texture.as_image_copy(),
ImageCopyBuffer {
buffer: &destination,
layout: layout_data(
image.size.x as u32,
image.size.y as u32,
image.texture_format,
),
},
Extent3d {
width,
height,
..Default::default()
},
);
render_queue.submit([encoder.finish()]);
let slice = destination.slice(..);
slice.map_async(wgpu::MapMode::Read, move |result| {
let err = result.err();
if err.is_some() {
panic!("{}", err.unwrap().to_string());
}
});
device.poll(Maintain::Wait);
let data = slice.get_mapped_range();
let result = Vec::from(data.deref());
drop(data);
if result.len() == ((width * height) as usize * image.texture_format.pixel_size()) {
recorder.last_frame = Some(result)
} else {
let pixel_size = image.texture_format.pixel_size() as u32;
let initial_row_bytes = width * pixel_size;
let buffered_row_bytes = align_byte_size(width * pixel_size);
let result = result
.chunks_exact(buffered_row_bytes as usize) .flat_map(|row| row.iter().take(initial_row_bytes as usize)) .copied() .collect();
recorder.last_frame = Some(result);
}
}
}
}