#![allow(dead_code)]
use bevy::app::{PluginGroupBuilder, PluginsState};
use bevy::camera::RenderTarget;
use bevy::prelude::*;
use bevy::render::RenderPlugin;
use bevy::render::render_resource::TextureFormat;
use bevy::tasks::tick_global_task_pools_on_main_thread;
use bevy::window::ExitCondition;
use bevy::winit::WinitPlugin;
pub fn headless_plugins() -> PluginGroupBuilder {
DefaultPlugins
.set(WindowPlugin {
primary_window: None,
exit_condition: ExitCondition::DontExit,
..default()
})
.set(RenderPlugin {
synchronous_pipeline_compilation: true,
..default()
})
.disable::<WinitPlugin>()
}
pub fn finish(app: &mut App) {
while app.plugins_state() == PluginsState::Adding {
tick_global_task_pools_on_main_thread();
}
app.finish();
app.cleanup();
}
pub fn image_target(images: &mut Assets<Image>) -> RenderTarget {
RenderTarget::Image(
images
.add(Image::new_target_texture(
256,
256,
TextureFormat::Rgba8UnormSrgb,
None,
))
.into(),
)
}
pub fn read_texture(app: &mut App, image: Handle<Image>, row_bytes: usize, rows: usize) -> Vec<u8> {
read_textures(app, vec![(image, row_bytes, rows)])
.pop()
.expect("one texture requested")
}
pub fn read_textures(app: &mut App, requests: Vec<(Handle<Image>, usize, usize)>) -> Vec<Vec<u8>> {
use std::sync::{Arc, Mutex};
use bevy::render::gpu_readback::{Readback, ReadbackComplete};
let slots: Vec<Arc<Mutex<Option<Vec<u8>>>>> = requests.iter().map(|_| Arc::default()).collect();
let entities: Vec<Entity> = requests
.iter()
.zip(&slots)
.map(|((image, _, _), slot)| {
let sink = slot.clone();
app.world_mut()
.spawn(Readback::texture(image.clone()))
.observe(move |done: On<ReadbackComplete>| {
let mut sink = sink.lock().expect("lock");
if sink.is_none() {
*sink = Some(done.data.clone());
}
})
.id()
})
.collect();
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(60);
while std::time::Instant::now() < deadline {
app.update();
if slots.iter().all(|s| s.lock().expect("lock").is_some()) {
break;
}
}
for entity in entities {
app.world_mut().despawn(entity);
}
requests
.iter()
.zip(slots)
.map(|((_, row_bytes, rows), slot)| {
let padded = slot
.lock()
.expect("lock")
.take()
.expect("the readback completed");
let stride = row_bytes.next_multiple_of(256);
assert_eq!(padded.len(), stride * rows);
padded
.chunks_exact(stride)
.flat_map(|row| row[..*row_bytes].iter().copied())
.collect()
})
.collect()
}
pub fn half(bits: u16) -> f32 {
let sign = if bits & 0x8000 != 0 { -1.0 } else { 1.0 };
let exponent = i32::from((bits >> 10) & 0x1F);
let mantissa = f32::from(bits & 0x3FF);
match exponent {
0 => sign * mantissa * 2f32.powi(-24),
31 => sign * f32::INFINITY,
e => sign * (1.0 + mantissa / 1024.0) * 2f32.powi(e - 15),
}
}