-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Open
Labels
area: graphicsGraphics related issueGraphics related issue
Description
The downloaded arrayBuffer in SogBundleParser.load stays in memory, hence duplicating the heap memory footprint of a SOG entity.
Since the texture cleanup is done by the GSplatSogsData._destroyGpuResources there is no need to keep the textures in asset registry.
I batched the SogBundleParser.load in order to free the array after parsing and everything seems to work, but not ready for PR
async load(url, callback, asset) {
try {
let arrayBuffer = await downloadArrayBuffer(url, asset);
const files = parseZipArchive(arrayBuffer);
// deflate
for (const file of files) {
if (file.compression === 'deflate') {
file.data = await inflate(file.data);
}
}
// access bundled meta.json
const metaFile = files.find(f => f.filename === 'meta.json');
if (!metaFile) {
callback('Error: meta.json not found');
return;
}
// parse json
let meta;
try {
meta = JSON.parse(new TextDecoder().decode(metaFile.data));
} catch (err) {
callback(`Error parsing meta.json: ${err}`);
return;
}
// extract filenames from meta.json
const filenames = ['means', 'scales', 'quats', 'sh0', 'shN'].map(key => meta[key]?.files ?? []).flat();
arrayBuffer = null;
// load referenced textures
const textures = {};
const promises = [];
for (const filename of filenames) {
const file = files.find(f => f.filename === filename);
let texture;
if (file) {
// file is embedded
texture = new Asset(filename, 'texture', {
url: `${url.load}/${filename}`,
filename,
contents: file.data
}, {
mipmaps: false
});
} else {
// file doesn't exist in bundle, treat it as a url
const url = (new URL(filename, new URL(filename, window.location.href).toString())).toString();
texture = new Asset(filename, 'texture', {
url,
filename
}, {
mipmaps: false
});
}
const promise = new Promise((resolve, reject) => {
const onLoad = () => {
texture.off('load', onLoad);
texture.off('error', onError);
resolve(null);
};
const onError = (err) => {
texture.off('load', onLoad);
texture.off('error', onError);
reject(err);
};
texture.on('load', onLoad);
texture.on('error', onError);
});
//this.app.assets.add(texture);
textures[filename] = texture;
promises.push(promise);
}
Object.values(textures).forEach(t => this.app.assets.load(t));
await Promise.allSettled(promises);
// Release CPU-side texture data after GPU upload
Object.values(textures).forEach((t) => {
if (t.file) {
t.file.contents = null;
}
});
// construct the gsplat resource
const data = new GSplatSogsData();
data.meta = meta;
data.numSplats = meta.count;
data.means_l = textures[meta.means.files[0]].resource;
data.means_u = textures[meta.means.files[1]].resource;
data.quats = textures[meta.quats.files[0]].resource;
data.scales = textures[meta.scales.files[0]].resource;
data.sh0 = textures[meta.sh0.files[0]].resource;
data.sh_centroids = textures[meta.shN?.files[0]]?.resource;
data.sh_labels = textures[meta.shN?.files[1]]?.resource;
const decompress = asset.data?.decompress;
if (!decompress) {
// no need to prepare gpu data if decompressing
await data.prepareGpuData();
}
const resource = decompress ?
new GSplatResource(this.app.graphicsDevice, await data.decompress()) :
new GSplatSogsResource(this.app.graphicsDevice, data);
callback(null, resource);
} catch (err) {
callback(err);
}
}
}
Metadata
Metadata
Assignees
Labels
area: graphicsGraphics related issueGraphics related issue