Sequencer Firefox Performance fixes

Sequencer Firefox Performance fixes
221 Replies
LukeAbby
LukeAbby2y ago
Alright so let me go through the high level reproduction steps: 1. Open up a game of Foundry in Firefox. 2. Add this effect to about however many tokens lag you, for me it's about a dozen:
for (const token of game.canvas.tokens.controlled) {
new Sequence()
.effect()
.file("jb2a.wind_stream.white", true)
.attachTo(token)
.persist()
.play();
}
for (const token of game.canvas.tokens.controlled) {
new Sequence()
.effect()
.file("jb2a.wind_stream.white", true)
.attachTo(token)
.persist()
.play();
}
Wasp
Wasp2y ago
https://raw.githubusercontent.com/fantasycalendar/FoundryVTT-Sequencer/next/module.json Have ya tried this version? It's the upcoming 3.0.0 version, should have some improvements, but I'm still open to hearing what you've found!
LukeAbby
LukeAbby2y ago
I'll pull it up again but I'll note that there's no prototype token persistence
Wasp
Wasp2y ago
Ah, fair
LukeAbby
LukeAbby2y ago
The main change I made was this:
const videos = {};
async function get_video_texture(inSrc, inBlob) {
console.log("GOT BLOB", inBlob);
if (inSrc in videos) {
console.log("VIDEO IS CACHED");
return videos[inSrc];
}

const texturePromise = new Promise(async (resolve) => {
const video = document.createElement("video");
video.preload = "auto";
video.crossOrigin = "anonymous";
video.controls = true;
video.autoplay = false;
video.autoload = true;
video.muted = true;
video.src = URL.createObjectURL(inBlob);

let canplay = true;
video.oncanplay = async () => {
if (!canplay)
return;
canplay = false;
video.height = video.videoHeight;
video.width = video.videoWidth;
const baseTexture = PIXI.BaseTexture.from(video, { resourceOptions: { autoPlay: false } });
if (game.settings.get(CONSTANTS.MODULE_NAME, "enable-pixi-fix")) {
baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
}
const texture = new PIXI.Texture(baseTexture);
resolve(texture);
};
video.onerror = () => {
URL.revokeObjectURL(video.src);
delete videos[inSrc];
reject();
};
});
videos[inSrc] = texturePromise;

return texturePromise;
}
const videos = {};
async function get_video_texture(inSrc, inBlob) {
console.log("GOT BLOB", inBlob);
if (inSrc in videos) {
console.log("VIDEO IS CACHED");
return videos[inSrc];
}

const texturePromise = new Promise(async (resolve) => {
const video = document.createElement("video");
video.preload = "auto";
video.crossOrigin = "anonymous";
video.controls = true;
video.autoplay = false;
video.autoload = true;
video.muted = true;
video.src = URL.createObjectURL(inBlob);

let canplay = true;
video.oncanplay = async () => {
if (!canplay)
return;
canplay = false;
video.height = video.videoHeight;
video.width = video.videoWidth;
const baseTexture = PIXI.BaseTexture.from(video, { resourceOptions: { autoPlay: false } });
if (game.settings.get(CONSTANTS.MODULE_NAME, "enable-pixi-fix")) {
baseTexture.alphaMode = PIXI.ALPHA_MODES.PREMULTIPLIED_ALPHA;
}
const texture = new PIXI.Texture(baseTexture);
resolve(texture);
};
video.onerror = () => {
URL.revokeObjectURL(video.src);
delete videos[inSrc];
reject();
};
});
videos[inSrc] = texturePromise;

return texturePromise;
}
this is the compiled js I added everything related to videos it seems like Chrome does this automatically or something, it changes NOTHING for Chrome but on Firefox when I have 10 effects it seems to make 10 corresponding videos and upload them to the GPU constantly which eventually strains the GPU when it's a large enough effect (over a MB in this case)
Wasp
Wasp2y ago
The main problem with that is that if all of them share the same video, they all have the same playback So if one is removed/paused/loops or needs have a different offset to its playback, that's not possible
LukeAbby
LukeAbby2y ago
hmm, understood I understand this isn't a general fix then, can you see if you reproduce this by pulling up Firefox though?
Wasp
Wasp2y ago
Firefox is generally terrible in foundry from what I know, due to it being largely out of sync with the chromium-verse Pretty much everyone in the mothership recommends to play on anything but Firefox Like, if you place multiple animated tiles on the scene, do you get the same issues?
LukeAbby
LukeAbby2y ago
Fair, I just realised that it was a Sequencer "bug" though once I tried reproducing in pure PIXI here's what I tried initially
function start() {
const app = createApp();

const windStreamPath = Sequencer.Database.getEntry("jb2a.wind_stream.white").file;

const windStreamTexture = PIXI.Texture.from(windStreamPath);
windStreamTexture.baseTexture.resource.source.loop = true;

const positions = [100, 200, 300, 400, 500];
for (let i = 0; i < 100; i++) {
for (const x of positions) {
for (const y of positions) {
addSprite(app, windStreamTexture, x + Math.floor(Math.random() * 10), y + Math.floor(Math.random() * 10));
}
}
}
}

// Basically taken frome Sequencer's effects-layer.js and/or ui-layer.js
function createApp() {
const canvas = document.createElement("canvas");
canvas.id = "test-layer";

canvas.style.cssText = `
position:absolute;
touch-action: none;
pointer-events: none;
width:100%;
height:100%;
z-index:0.1;
padding: 0;
margin: 0;
`;

document.body.appendChild(canvas);

const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
view: canvas,
antialias: true,
backgroundAlpha: 0.0,
sharedTicker: true
});

app.resizeTo = window;

globalThis.__PIXI_APP__ = app; // Allow PIXI extension to see the app.

document.body.appendChild(app.view);

return app;
}

function addSprite(app, texture, x, y) {
const windStreamSprite = new PIXI.Sprite(texture);
windStreamSprite.x = x;
windStreamSprite.y = y;
windStreamSprite.loop = true;

app.stage.addChild(windStreamSprite);
}

function startFPS() {
const ticker = new PIXI.Ticker();
let _lastTime = new Date().getTime();
let _timeValues = [];
ticker.add(() => {
const currentTime = new Date().getTime();
_timeValues.push(1000 / (currentTime - _lastTime));

if (_timeValues.length === 30) {
let total = 0;
for (let i = 0; i < 30; i++) {
total += _timeValues[i];
}

console.log(total / 30);

_timeValues.length = 0;
}

_lastTime = currentTime;
});
ticker.start();
}

start();
startFPS();
function start() {
const app = createApp();

const windStreamPath = Sequencer.Database.getEntry("jb2a.wind_stream.white").file;

const windStreamTexture = PIXI.Texture.from(windStreamPath);
windStreamTexture.baseTexture.resource.source.loop = true;

const positions = [100, 200, 300, 400, 500];
for (let i = 0; i < 100; i++) {
for (const x of positions) {
for (const y of positions) {
addSprite(app, windStreamTexture, x + Math.floor(Math.random() * 10), y + Math.floor(Math.random() * 10));
}
}
}
}

// Basically taken frome Sequencer's effects-layer.js and/or ui-layer.js
function createApp() {
const canvas = document.createElement("canvas");
canvas.id = "test-layer";

canvas.style.cssText = `
position:absolute;
touch-action: none;
pointer-events: none;
width:100%;
height:100%;
z-index:0.1;
padding: 0;
margin: 0;
`;

document.body.appendChild(canvas);

const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
view: canvas,
antialias: true,
backgroundAlpha: 0.0,
sharedTicker: true
});

app.resizeTo = window;

globalThis.__PIXI_APP__ = app; // Allow PIXI extension to see the app.

document.body.appendChild(app.view);

return app;
}

function addSprite(app, texture, x, y) {
const windStreamSprite = new PIXI.Sprite(texture);
windStreamSprite.x = x;
windStreamSprite.y = y;
windStreamSprite.loop = true;

app.stage.addChild(windStreamSprite);
}

function startFPS() {
const ticker = new PIXI.Ticker();
let _lastTime = new Date().getTime();
let _timeValues = [];
ticker.add(() => {
const currentTime = new Date().getTime();
_timeValues.push(1000 / (currentTime - _lastTime));

if (_timeValues.length === 30) {
let total = 0;
for (let i = 0; i < 30; i++) {
total += _timeValues[i];
}

console.log(total / 30);

_timeValues.length = 0;
}

_lastTime = currentTime;
});
ticker.start();
}

start();
startFPS();
sorry for the length but the key stuff is that this DOESN'T reproduce the problem and I realised it's not reproducing it because it's reusing the texture for every sprite
Wasp
Wasp2y ago
Indeed
LukeAbby
LukeAbby2y ago
even though it's making 100*5*5 sprites it doesn't really lag at all yeah
Wasp
Wasp2y ago
Essentially, each video texture is a whole html video tag and browser tabs can only generally handle a maximum of 70, that's the hard coded limit but most can at most handle 5-10, depending on the underlying system and browser
LukeAbby
LukeAbby2y ago
well my player starts getting lag around that area (5-10) yeah and I think it's because it's a large texture it's scaled down to the player token so it's unnecessarily high resolution
Wasp
Wasp2y ago
might be a good idea to request a scaled down version from the JB2A guys
LukeAbby
LukeAbby2y ago
yeah, I'll look into something like that would you accept a fix if I were to PR one that still kept all the features of current Sequencer to cache textures in a way that plays nice with firefox? I imagine it'd help with the chrome video limit as well. I can suggest my players not use Firefox but I imagine that'd annoy them
Want results from more Discord servers?
Add your server