playcanvas / playcanvas/engine

Give more control and information to the developer when loading resources

Open
#2,021 16 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: assets enhancement
Dominant language
JavaScript
Stars
16.8k
Forks
2k
Avg merge
4h 32m
Merged PRs (30d)
222

Description

Related issues:
#737 Asset loader should retry failed assets
#494 assets.load and assets.loadFromUrl to return XHR object if one created internally
#1246 Asset progress event support

Currently the resource loader is not resilient to unstable networks (such as mobile) and doesn't give much control or information (such as progress) back to the client.

This makes it very difficult for the client to know if a load has stalled or going very slowly to make the decision to retry or notify the user if the network connection has dropped.

Taking pc.SceneRegistry.loadSceneHierarchy for example:
https://github.com/playcanvas/engine/blob/master/src/framework/scene-registry.js#L144

    SceneRegistry.prototype.loadSceneHierarchy = function (url, callback) {
        var self = this;

        // Because we need to load scripts before we instance the hierarchy (i.e. before we create script components)
        // Split loading into load and open
        var handler = this._app.loader.getHandler("hierarchy");

        // include asset prefix if present
        if (this._app.assets && this._app.assets.prefix && !pc.ABSOLUTE_URL.test(url)) {
            url = pc.path.join(this._app.assets.prefix, url);
        }

        handler.load(url, function (err, data) {
            if (err) {
                if (callback) callback(err);
                return;
            }

            // called after scripts are preloaded
            var _loaded = function () {
                self._app.systems.script.preloading = true;
                var entity = handler.open(url, data);
                self._app.systems.script.preloading = false;

                // clear from cache because this data is modified by entity operations (e.g. destroy)
                self._app.loader.clearCache(url, "hierarchy");

                // add to hierarchy
                self._app.root.addChild(entity);

                // initialize components
                pc.ComponentSystem.initialize(entity);
                pc.ComponentSystem.postInitialize(entity);

                if (callback) callback(err, entity);
            };

            // load priority and referenced scripts before opening scene
            self._app._preloadScripts(data, _loaded);
        });
    };

The request is made to the handler which makes the call to pc.HierarchyHandler.load
https://github.com/playcanvas/engine/blob/master/src/resources/hierarchy.js#L10

        load: function (url, callback) {
            if (typeof url === 'string') {
                url = {
                    load: url,
                    original: url
                };
            }

            var assets = this._app.assets;

            pc.http.get(url.load, {
                retry: this.retryRequests
            }, function (err, response) {
                if (!err) {
                    pc.TemplateUtils.waitForTemplatesInScene(
                        response,
                        assets,
                        callback);
                } else {
                    var errMsg = 'Error while loading scene ' + url.original;
                    if (err.message) {
                        errMsg += ': ' + err.message;
                        if (err.stack) {
                            errMsg += '\n' + err.stack;
                        }
                    } else {
                        errMsg += ': ' + err;
                    }

                    callback(errMsg);
                }
            });
        },

But it (and other handlers) do not handle or return the XHR object which could be used to check progress and abort the request if need be.

Proposals:

  • Any resource handler should return a handle that wraps the XHR object to allow the client to abort a load
  • Any public API that does a load request should return this handle
  • The err returned in the callback (example) should be an object with an error code and string rather than just a string when an error occurs.

Questions:

  • Should the engine track progress as well or should that be optional for the client?
  • Should the engine retry requests as #737 mentions or be optional for the client?
  • Should the handle object be able to do the abort or should there be a matching cancel* function to any load* function?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/framework/scene-registry.js and src/resources/hierarchy.js, then trace pc.http.get and the related issues on retries, returned XHR objects, and progress events. The work is complete only after the scope is resolved and resource handlers and public load APIs consistently expose the agreed handle and structured callback errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
api
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.