[SOLVED] PlayCanvas crashes after a big .glb file is loaded

I used a script to load an .glb asset from the examples.
It works on desktop computer. But crashes on iPhone and iPad.

The .glb file is 170MBytes.

It uses this script:

var LoadGlbAsset = pc.createScript('loadGlbAsset');
LoadGlbAsset.attributes.add('glbAsset', { type: 'asset', assetType: 'binary'});

// initialize code called once per entity
LoadGlbAsset.prototype.initialize = function() {
    utils.loadGlbContainerFromAsset(this.glbAsset, null, this.glbAsset.name, function (err, asset) {
        if (err) {
            console.error(err);
            return;
        } 
        var renderRootEntity = asset.resource.instantiateRenderEntity();
        this.entity.addChild(renderRootEntity);
    }.bind(this));
};

// swap method called for script hot-reloading
// inherit your script state here
// LoadGlb.prototype.swap = function(old) { };

// to learn more about script anatomy, please read:
// http://developer.playcanvas.com/en/user-manual/scripting/

Which uses this utility script:

(function(){
    var utils = {};
    var app = pc.Application.getApplication();

    /**
     * @name utils#loadGlbContainerFromAsset
     * @function
     * @description Load a GLB container from a binary asset that is a GLB. If the asset is not loaded yet, it will load the asset.
     * @param {pc.Asset} glbBinAsset The binary asset that is the GLB.
     * @param {Object} options Optional. Extra options to do extra processing on the GLB.
     * @param {String} assetName. Name of the asset.
     * @param {Function} callback The callback function for loading the asset. Signature is `function(string:error, asset:containerAsset)`.
     * If `error` is null, then the load is successful.
     * @returns {pc.Asset} The asset that is created for the container resource.
     */
    utils.loadGlbContainerFromAsset = function (glbBinAsset, options, assetName, callback) {
        var onAssetReady = function (asset) {
            var blob = new Blob([asset.resource]);
            var data = URL.createObjectURL(blob);
            return this.loadGlbContainerFromUrl(data, options, assetName, function (error, asset) {
                callback(error, asset);
                URL.revokeObjectURL(data);
            });
        }.bind(this);

        glbBinAsset.ready(onAssetReady);
        app.assets.load(glbBinAsset);
    };

    /**
     * @name utils#loadGlbContainerFromUrl
     * @function
     * @description Load a GLB container from a URL that returns a `model/gltf-binary` as a GLB.
     * @param {String} url The URL for the GLB
     * @param {Object} options Optional. Extra options to do extra processing on the GLB.
     * @param {String} assetName. Name of the asset.
     * @param {Function} callback The callback function for loading the asset. Signature is `function(string:error, asset:containerAsset)`.
     * If `error` is null, then the load is successful.
     * @returns {pc.Asset} The asset that is created for the container resource.
     */
    utils.loadGlbContainerFromUrl = function (url, options, assetName, callback) {
        var filename = assetName + '.glb';
        var file = {
            url: url,
            filename: filename
        };

        var asset = new pc.Asset(filename, 'container', file, null, options);
        asset.once('load', function (containerAsset) {
            if (callback) {
                // As we play animations by name, if we have only one animation, keep it the same name as
                // the original container otherwise, postfix it with a number
                var animations = containerAsset.resource.animations;
                if (animations.length == 1) {
                    animations[0].name = assetName;
                } else if (animations.length > 1) {
                    for (var i = 0; i < animations.length; ++i) {
                        animations[i].name = assetName + ' ' + i.toString();
                    }
                }

                callback(null, containerAsset);
            }
        });

        app.assets.add(asset);
        app.assets.load(asset);

        return asset;
    };

    window.utils = utils;
})();

Hi @simmania,

iOS devices are known to crash when too much video memory is allocated. That’s not a PlayCanvas issue but Apple/Safari memory policy.

What’s your video memory allocation right now? You can view it using the profiler: Profiler | Learn PlayCanvas

There isn’t an official limit but from my experience I start to get crashes on iOS devices after 300-350 of VRAM allocated.

Most likely your model is too big (vertices and texture size).

I reduced it to 350MBytes and it works now!
I will reduce it further to be sure.
Thx

1 Like

@Leonidas does that limit vary depending on the device? I mean is there any difference between, for example, iPhone X and iPhone14?

From my experience, not so much or not at all (maybe only for older devices).

I think what can vary the limit is the amount of apps / tabs that you have open.

It would help so much if Apple wasn’t so secretive about this :pray: