I am confused; I been making a little app and using Bevy out of laziness and for fun (not to mention I just enjoy coding the ECS for layout). it simply loads a mesh from file, waits for it to load then when fully loaded it takes a screenshot (camera to image rendering as no window) and even though all assets are loaded and even if I purposely wait several extra frames it only renders the mesh at an arbitrary time after and I cannot figure out what I am doing wrong?
#Delayed mesh rendering
14 messages · Page 1 of 1 (latest)
#[derive(Component)]
struct CameraImage(Handle<Image>);
#[derive(Event, Clone, Debug)]
struct TakeScreenshot;
#[derive(Resource)]
struct WaitingForAsset(Vec<UntypedHandle>, u32);
#[derive(Component)]
struct TargetedMesh;
fn waits_for_loaded(
mut commands: Commands,
assets: Res<AssetServer>,
mut awaiting_asset: ResMut<WaitingForAsset>,
cameras: Query<&mut Transform, With<Camera>>,
focused: Query<&Aabb, With<TargetedMesh>>,
mut rot: Local<f32>,
) {
if awaiting_asset
.0
.iter()
.all(|asset| assets.is_loaded_with_dependencies(asset))
{
if let Ok(aabb) = focused.single() {
let center: Vec3 = aabb.center.into();
let mut camera_transform: Vec3 = aabb.center.into();
let size = aabb.half_extents * 2.0;
let max_dimension = size.x.max(size.y).max(size.z);
let distance = max_dimension * 1.5;
camera_transform.z = distance;
let mut camera_transform =
Transform::from_translation(camera_transform).looking_at(center, Vec3::Y);
camera_transform.rotate_around(
center,
Quat::from_euler(EulerRot::XYZ, 0.0, *rot * 45.0, 0.0),
);
*rot += 1.0;
for mut camera in cameras {
*camera = camera_transform
}
commands.send_event(TakeScreenshot);
commands.remove_resource::<WaitingForAsset>();
}
} else { println!("waiting..."); }
}
fn take_screenshot(
mut commands: Commands,
mut screenshot: EventReader<TakeScreenshot>,
cameraimage: Single<&CameraImage>,
mut image_num: Local<usize>,
) {
for _ in screenshot.read() {
*image_num += 1;
let path = format!("screenshot-{}.webp", *image_num);
commands
.spawn(Screenshot::image(cameraimage.0.clone()))
.observe(save_to_disk(path).pipe(close_app));
}
}
pub fn save_to_disk(path: impl AsRef<Path>) -> impl FnMut(Trigger<ScreenshotCaptured>) {
let path = path.as_ref().to_owned();
move |trigger| {
let img = trigger.event().0.clone();
match img.try_into_dynamic() {
Ok(dyn_img) => match image::ImageFormat::from_path(&path) {
Ok(format) => {
let img = dyn_img.to_rgba8();
#[cfg(not(target_arch = "wasm32"))]
match img.save_with_format(&path, format) {
Ok(_) => info!("Screenshot saved to {}", path.display()),
Err(e) => error!("Cannot save screenshot, IO error: {e}"),
}
#[cfg(target_arch = "wasm32")]
{
let save_screenshot = || {
use image::EncodableLayout;
use wasm_bindgen::{JsCast, JsValue};
let mut image_buffer = std::io::Cursor::new(Vec::new());
img.write_to(&mut image_buffer, format)
.map_err(|e| JsValue::from_str(&format!("{e}")))?;
// SAFETY: `image_buffer` only exist in this closure, and is not used after this line
let parts = js_sys::Array::of1(&unsafe {
js_sys::Uint8Array::view(image_buffer.into_inner().as_bytes())
.into()
});
let blob = web_sys::Blob::new_with_u8_array_sequence(&parts)?;
let url = web_sys::Url::create_object_url_with_blob(&blob)?;
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let link = document.create_element("a")?;
link.set_attribute("href", &url)?;
link.set_attribute(
"download",
path.file_name()
.and_then(|filename| filename.to_str())
.ok_or_else(|| JsValue::from_str("Invalid filename"))?,
)?;
let html_element = link.dyn_into::<web_sys::HtmlElement>()?;
html_element.click();
web_sys::Url::revoke_object_url(&url)?;
Ok::<(), JsValue>(())
};
match (save_screenshot)() {
Ok(_) => info!("Screenshot saved to {}", path.display()),
Err(e) => error!("Cannot save screenshot, error: {e:?}"),
};
}
}
Err(e) => error!("Cannot save screenshot, requested format not recognized: {e}"),
},
Err(e) => error!("Cannot save screenshot, screen format cannot be understood: {e}"),
}
}
}
fn close_app(mut commands: Commands) {
commands.send_event(AppExit::Success);
}
``` is the functions for reference; I been on this for hours and no idea why the delay exists? I tried multiple things such as changing schedule order, checking all my loading code, etc, etc but alas nothing works other than purposely waiting a whole second~ (the save_to_disk function is copied from bevy as I need the alpha channel)
Shaders take some time to compile
is there a way to wait for them? and be aware when done?
You can:
- Wait for them to finish: https://dev-docs.bevy.org/bevy_render/render_resource/struct.PipelineCache.html#method.waiting_pipelines
- Set this to true to block the frame until shaders are ready https://dev-docs.bevy.org/bevy_render/struct.RenderPlugin.html#structfield.synchronous_pipeline_compilation
Cache for render and compute pipelines.
Contains the default Bevy rendering backend based on wgpu.
you're a star <3
fixed instantly, thanks alot, I couldn't find anything about it anywhere but I am prob blind mb <3
Nw, it's obscure atm
heads up: did you mean to use the dev-docs here? seems they were fine this time, but those are built from the main branch, while most people would be using the releases
@languid ferry I've so far always used the approach taken in https://github.com/rparrett/bevy_pipelines_ready, i.e. set an experimental constant and wait until that many pipelines have loaded.
Is there a difference between these approaches?
Because this seems simpler:
fn update_pipelines_ready(mut main_world: ResMut<MainWorld>, pipelines: Res<PipelineCache>) {
if let Some(mut pipelines_ready) = main_world.get_resource_mut::<PipelinesReady>() {
pipelines_ready.0 = pipelines.waiting_pipelines().count() == 0;
}
}
Yeah I have the dev docs bookmarked, but not the regular ones. Dw though, I knew these APIs didn't change 😛
bevy_pipelines_ready just predates waiting_pipelines a bit and I never looked into using it. I think the main difference would be in progress tracking.