bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.28k stars 3.58k forks source link

Can't get Rendering Assets to unload from RAM on wasm webgl2.0 build #12057

Closed egol closed 8 months ago

egol commented 9 months ago

Bevy version

v0.13.0

[Optional] Relevant system information

GPU: RTX 2080 Ti Using Firefox 122 on Windows 10

AdapterInfo { name: "ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0)", vendor: 4318, device: 0, device_type: Other, driver: "", driver_info: "", backend: Gl }

(Not sure why the adapter info shows GTX 980 for firefox but problem is persistent on chrome as well)

Using Chrome shows:

 AdapterInfo { name: "ANGLE (NVIDIA, NVIDIA GeForce RTX 2080 Ti (0x00001E07) Direct3D11 vs_5_0 ps_5_0, D3D11)", vendor: 4318, device: 0, device_type: Other, driver: "", driver_info: "", backend: Gl }

What you did

I am trying to get the new "Unload Rendering Assets from RAM" feature working. I have been successful on a local cargo run --release build but I have not been able to replicate the memory usage on a wasm webgl build. The attachment below shows the comparative ram usage of the local windows build and the wasm build. For some reason the wasm build never ends up offloading the memory and stays at the pictured amount. For reference, I am loading 63 jpg images that are on average 2MB in size and have a resolution of 2587x3375 (obviously this could be optimized and I am currently perusing a workaround of drastically decreasing their resolution)

This is the code that sets my image assets to be offloaded:

fn check_asset_loading (
    mut next_state: ResMut<NextState<AppState>>,
    book_query: Query<&Book>,
    mut images: ResMut<Assets<Image>>,
    server: Res<AssetServer>,
    mut loading_progress: ResMut<LoadingProgress>
)
{
    let mut loaded = true;
    use bevy::asset::LoadState;
    for book in book_query.iter() {

        let mut num_loaded = 0;

        for handle in book.image_handles.iter() {
            let load_state = server.get_load_state(handle);

            if load_state.is_some() {
                if load_state.unwrap() != LoadState::Loaded {
                    loaded = false;
                }
                else {
                    num_loaded += 1;

                    if let Some(image) = images.get_mut(handle) {
                        image.asset_usage = RenderAssetUsages::RENDER_WORLD;
                    }
                }
            }
        }

        loading_progress.current = (100.0 / book.image_handles.len() as f32) * num_loaded as f32;

        if loaded && book.image_handles.len() == num_loaded {
            info!("all images loaded!");
            // window.single_mut().visible = true;
            next_state.set(AppState::Running);
        }
    }

}

this is my build pipeline for wasm:

cargo build --profile wasm-release --target wasm32-unknown-unknown
wasm-bindgen --no-typescript --target web --out-dir ./out/ --out-name "mygame" ./target/wasm32-unknown-unknown/release/book1-0.wasm

relevant toml:

[profile.wasm-release]
inherits = "release"
opt-level = "z"
lto = "fat"
codegen-units = 1

html used to run wasm:

<!doctype html>
<html lang="en">

<body style="margin: 0px;">
  <script type="module">
    import init from './mygame.js'

    init().catch((error) => {
      if (!error.message.startsWith("Using exceptions for control flow, don't mind me. This isn't actually an error!")) {
        throw error;
      }
    });
  </script>

  <canvas id="mygame-canvas" width="1280" height="720"></canvas>
</body>

</html>

What went wrong

Offloading to GPU memory works on a local windows build of my application but does not happen on a wasm build of the same application.

Additional information

Memory usage: image Firefox performance graph: image

egol commented 9 months ago

Chrome memory usage: image

Apologies for randomly closing and reopening this issue as I was unsure of if this is a browser or bevy related bug.

mockersf commented 9 months ago

Fun fact I just learned: you can't free memory in WASM, once you allocated it you can't return it to the OS/browser 😄

See https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions, no instruction to deallocate. Read https://github.com/WebAssembly/design/issues/1397 for more details.

A workaround for your usage would be to load the images one by one, and unload them before loading the next one. that way you'll need only the memory for one image

JMS55 commented 8 months ago

Not much we can really do to fix this, but I'd like to add a note to the documentation for 0.13.1.