AssetRegistry not available in custom loading script (v2)

I’ve got custom functionality for scene and asset loading.
For it to work i set the preload property for assets to false in a custom loading screen.
This worked for v1. However it seems like in the v2 the AssetRegistry isn’t available yet and returns undefined.

This only happens when downloading the zipped application. In the editor it works as intended.

Here is the relevant code:

pc.script.createLoadingScreen(function (app) {

    const isDebug = new URLSearchParams(window.location.search).get("debug") === 'true';

    // Only remove preload if not in debug mode. This allows to start any scene in the editor, instead of having to start an empty scene and switch to the desired one.
    if (!isDebug) {
        // app.assets is undefined here!!!
        app.assets.on("add", function (asset) {
            if (asset.type !== "script" && asset.type !== "wasm" && asset.type !== "folder") {
                asset.preload = false;
            }
        });
    }

    ...

});

Is this an oversight in v2 or some intended change?

Is app undefined or app.assets?

app is defined
app.assets is undefined

image

I think this is related to us adding WebGPU support. Creating WebGPU device is an async operation, and so the app init function is also async, waiting for the device to be created.

I see that app fires this just before preloading … perhaps you can have your ‘asset.preload = false;’ code in there?

        this.fire('preload:start');

see here engine/src/framework/app-base.js at 720c3b78938496beaa0252232345747bc2236448 · playcanvas/engine · GitHub

I can’t reproduce this issue in a simple project PlayCanvas | HTML5 Game Engine

Edit: Ah yes, if WebGPU is used in the launch tab like this, I can reproduce the issue in the editor

Using preload:start does get rid of the error.
However setting preload to false, doesn’t have any effect then.
Seems like it’s too late in the pipeline to change preload behaviour

You will need to the change the code so that it iterates through the assets instead of creating a listener for when assets are added. Same O(*) cost overall.

When that event is called, the assets are already added to the registry

Seems like this does not work.
I’ve changed to the following:

app.once('preload:start', function () {
    const assets = this.assets.list({
        preload: true
    });
    assets.forEach((asset) => {
        if (asset.type !== "script" && asset.type !== "wasm" && asset.type !== "folder") {
            asset.preload = false;
        }
    });
});

I dont see why this doesnt work. In theory it should, but in practice it doesnt

Hm, odd. Have you checked after the forEach that the assets in the registry have been set preload correctly?

Ok so I’ve checked and logged the length of the asset registry filtered list, which is 216 before and 25 after. So it should work:

app.on('preload:start', function () {
    const assets = this.assets.list({
        preload: true
    });
    console.log(assets.length) // 216
    assets.forEach((asset) => {
        if (asset.type !== "script" && asset.type !== "wasm" && asset.type !== "folder") {
            asset.preload = false;
        }
    });
    const assetsAfter = this.assets.list({
        preload: true
    });
    console.log(assetsAfter.length) // 25
});

AppBase uses the same call to generate the list, so I dont get whats going on here. Unless the preload:start callback is somehow delayed

Shouldn’t be, events in PlayCanvas are synchronous. At this point, I would look at the preload function in the engine and start debugging there

Just to check, what do you mean by ‘doesn’t work’?

It loads the asset, altough its preload is set to false.
I’ll create a little test project

Here is an example project:
https://playcanvas.com/project/1401452/overview/preload

If I use the app.assets.on("add" it works and the skybox and lut textures arent preloaded. However as we already found out this doesnt work for WebGPU

If I use app.once('preload:start' and set the preload to false the skybox and lut textures are loaded, altough I set preload to false

Edit:
Check network tab for requests.
Start scene is an empty scene. So nothing should be loaded on demand

Alright so after adding the following log to AppBase and compiling a local version of the engine:

this.fire('preload:start');

// get list of assets to preload
const assets = this.assets.list({
    preload: true
});

console.log(`AppBase: ${assets.length}`)

if (assets.length === 0) {
    this.fire('preload:end');
    callback();
    return;
}

It does correctly report the length as 1 instead of 9. So I dont think it’s the preload which incorrectly loads the asset textures.

I have no idea what is loading them

Are the textures being referred by anything that is loaded. Eg a material used in the first scene that is loaded? etc PlayCanvas has some dependency behaviour where if a model is used with a material, the material will be loaded along with all the texture references in the material

Side note: You can use log points (DevTools টিপস: ব্রেকপয়েন্ট এবং লগপয়েন্ট  |  Blog  |  Chrome for Developers) in the Chrome debugger to log out things like you’ve done above without needed to rebuild and host an engine

Edit: I wonder if it’s related to this issue? Should AssetRegistery#add also load assets if marked as preload? · Issue #3107 · playcanvas/engine · GitHub

Yeah, the more I look at this, the more I think that ticket is the issue.

I can’t think of a way around this that’s ‘nice’ besides a custom engine that fires an event when the init function is done, monkey patching the AppBase init function to do the same thing or modifying the boilerplate code from the downloaded build

1 Like

I just tested and that is indeed the issue. Good find!

Patching the engine is no great workaround for us. We only export as zip from the editor and use that.

As you said adding an event after init is done would be a good solution. How about adding this to the code base?

Or well dont automatically load assets when adding them to the asset registry. This is kind of undocumented behaviour anyway.
However this might introduce error’s to projects relying on this behaviour.

You will have to talk to the team / add a PR to engine repo on this. They may have a better solution for this on proposal.

You can patch the engine from the project (although not sure if that’s now possible if you are using MJS builds).

What would be easier IMO is to modify the boilerplate from the build you download from the editor and add you preload logic between the init and configure calls.

You could write a small tool that would download the build from the PlayCanvas API and swap out/modify the boilerplate.