Asynchronously load_something in Macroquad

TLDR

In Macroquad, how can you load_textures without blocking the UI?

Details

Macroquad seems to use async as a means of emulating generators. There seems to be no executor as such.

Macroquad's load_* functions are all async, and, without an executor, .await seems to be the only way of getting them to run.

I want to do my load_'ing off the UI thread, which, without something like block_on(), implies using .await inside a closure passed to thread::spawn, which I don't know how to do.

Do you need WASM support? It seems that this is the main issue for not supporting an executor according to this conversation: using tokio async runtime · Issue #182 · not-fl3/macroquad · GitHub.

WASM support might be interesting in the long-term, but is not an immediate concern for my toy model which runs on the desktop.

In the first reply in the the issue you link, a Macroquad contributor states that "macroquad is entirely single-threaded".

Unless I'm mistaken :

  1. load_texture() takes a significant time to run,
  2. load_texture() must be .awaited,
  3. load_texture() seems not to yield until it has completed,
  4. the main macroquad loop must call next_frame().await,
  5. there seems to be no way to execute these .awaits on separate threads.

Does this mean that there is no way not to block the UI while load_texture() is running?

Have you considered creating a tokio Runtime (or any async executor runtime, really) before the loop starts, and spawning the async tasks on it? If you use tokio in particular if you call .enter() on the Runtime and store that handle in a variable then you'll be able to use tokio::spawn without having to explicitly pass a reference to the runtime around.

Yes, I did a quick 'n' dirty trial with futures rather than tokio and ran into some problems whose details I forget. Perhaps I should give this a more serious go.

But I would expect not-blocking-the-UI to be a pretty common requirement, so I'm surprised that I cannot find a prominent solution.

I would expect that to work as well as long as:

  • it spawns its own threads
  • you don't call block_on on the UI thread

I've tried a bunch of variations on the following theme, both using tokio and futures

use macroquad::prelude::*;
use tokio::runtime::Runtime;

#[macroquad::main("XXX")]
async fn main() {
    let mut args = std::env::args();
    let _executable = args.next();
    let path = args.next().unwrap(); // path to JPEG or other image

    let (tx, rx) = std::sync::mpsc::channel();

    let rt = Runtime::new().unwrap();
    let _rt_guard = rt.enter();

    tokio::spawn(async move {
        for _ in 0..10 {
            let texture = rt.block_on(load_texture(&path)).unwrap();
            tx.send(texture).unwrap();
        }
    });

    let mut textures = vec![];
    loop {
        clear_background(GRAY);
        draw_text(&format!("loaded {} textures.", textures.len()),
                  100.0, 100.0, 100.0, BLACK);

        if let Ok(texture) = rx.try_recv() {
            textures.push(texture);
        }

        next_frame().await
    }
}

So far, all attempts that compile have run into some run-time crash.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.