Skip to content

Commit

Permalink
feat(framerate): framerate fixes when application errors occur
Browse files Browse the repository at this point in the history
  • Loading branch information
seankmartin committed Oct 21, 2024
1 parent ede15b9 commit 9e4e07d
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 23 deletions.
4 changes: 2 additions & 2 deletions src/display_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ export class DisplayContext extends RefCounted implements FrameNumberCounter {
this.updateStarted.dispatch();
const gl = this.gl;
const ext = this.framerateMonitor.getTimingExtension(gl);
const query = this.framerateMonitor.startFrameTimeQuery(gl, ext);
this.framerateMonitor.startFrameTimeQuery(gl, ext, this.frameNumber);
this.ensureBoundsUpdated();
this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
Expand All @@ -611,7 +611,7 @@ export class DisplayContext extends RefCounted implements FrameNumberCounter {
gl.clear(gl.COLOR_BUFFER_BIT);
this.gl.colorMask(true, true, true, true);
this.updateFinished.dispatch();
this.framerateMonitor.endFrameTimeQuery(gl, ext, query);
this.framerateMonitor.endLastTimeQuery(gl, ext);
this.framerateMonitor.grabAnyFinishedQueryResults(gl);
}

Expand Down
90 changes: 69 additions & 21 deletions src/util/framerate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,15 @@ export enum FrameTimingMethod {
MAX = 2,
}

interface QueryInfo {
glQuery: WebGLQuery;
frameNumber: number;
wasStarted: boolean;
wasEnded: boolean;
}

export class FramerateMonitor {
private timeElapsedQueries: (WebGLQuery | null)[] = [];
private timeElapsedQueries: QueryInfo[] = [];
private warnedAboutMissingExtension = false;
private storedTimeDeltas: number[] = [];

Expand All @@ -48,47 +55,88 @@ export class FramerateMonitor {
return ext;
}

startFrameTimeQuery(gl: WebGL2RenderingContext, ext: any) {
getOldestQueryIndexByFrameNumber() {
if (this.timeElapsedQueries.length === 0) {
return undefined;
}
let oldestQueryIndex = 0;
for (let i = 1; i < this.timeElapsedQueries.length; i++) {
const oldestQuery = this.timeElapsedQueries[oldestQueryIndex];
if (this.timeElapsedQueries[i].frameNumber < oldestQuery.frameNumber) {
oldestQueryIndex = i;
}
}
return oldestQueryIndex;
}

startFrameTimeQuery(
gl: WebGL2RenderingContext,
ext: any,
frameNumber: number,
) {
if (ext === null) {
return null;
}
const query = gl.createQuery();
if (query !== null) {
const currentQuery =
this.timeElapsedQueries[this.timeElapsedQueries.length - 1];
if (query !== null && currentQuery !== query) {
gl.beginQuery(ext.TIME_ELAPSED_EXT, query);
if (this.timeElapsedQueries.length >= this.queryPoolSize) {
const oldestQueryIndex = this.getOldestQueryIndexByFrameNumber();
if (oldestQueryIndex !== undefined) {
const oldestQuery = this.timeElapsedQueries.splice(
oldestQueryIndex,
1,
)[0];
gl.deleteQuery(oldestQuery.glQuery);
}
}
const queryInfo: QueryInfo = {
glQuery: query,
frameNumber: frameNumber,
wasStarted: true,
wasEnded: false,
};
this.timeElapsedQueries.push(queryInfo);
}
return query;
}

endFrameTimeQuery(
gl: WebGL2RenderingContext,
ext: any,
query: WebGLQuery | null,
) {
if (ext !== null && query !== null) {
gl.endQuery(ext.TIME_ELAPSED_EXT);
}
if (this.timeElapsedQueries.length >= this.queryPoolSize) {
const oldestQuery = this.timeElapsedQueries.shift();
if (oldestQuery !== null && oldestQuery !== undefined) {
gl.deleteQuery(oldestQuery);
endLastTimeQuery(gl: WebGL2RenderingContext, ext: any) {
if (ext !== null) {
const currentQuery =
this.timeElapsedQueries[this.timeElapsedQueries.length - 1];
if (!currentQuery.wasEnded && currentQuery.wasStarted) {
gl.endQuery(ext.TIME_ELAPSED_EXT);
currentQuery.wasEnded = true;
}
}
this.timeElapsedQueries.push(query);
}

grabAnyFinishedQueryResults(gl: WebGL2RenderingContext) {
const deletedQueryIndices: number[] = [];
for (let i = 0; i < this.timeElapsedQueries.length; i++) {
const query = this.timeElapsedQueries[i];
if (query !== null) {
// Error checking: if the query was not started or ended, just delete it.
// This can happen from errors in the rendering
if (!query.wasEnded || !query.wasStarted) {
gl.deleteQuery(query.glQuery);
deletedQueryIndices.push(i);
} else {
const available = gl.getQueryParameter(
query,
query.glQuery,
gl.QUERY_RESULT_AVAILABLE,
);
if (available) {
const result = gl.getQueryParameter(query, gl.QUERY_RESULT) / 1e6;
// If the result is null, then something went wrong and we should just delete the query.
if (available === null) {
gl.deleteQuery(query.glQuery);
deletedQueryIndices.push(i);
} else if (available) {
const result =
gl.getQueryParameter(query.glQuery, gl.QUERY_RESULT) / 1e6;
this.storedTimeDeltas.push(result);
gl.deleteQuery(query);
gl.deleteQuery(query.glQuery);
deletedQueryIndices.push(i);
}
}
Expand Down

0 comments on commit 9e4e07d

Please sign in to comment.