Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canvas2D Performance Experiments #322

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
14 changes: 7 additions & 7 deletions src/core/CoreNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1295,13 +1295,13 @@ export class CoreNode extends EventEmitter {

this.isRenderable = false;

delete this.renderCoords;
delete this.renderBound;
delete this.strictBound;
delete this.preloadBound;
delete this.globalTransform;
delete this.scaleRotateTransform;
delete this.localTransform;
this.renderCoords = undefined;
this.renderBound = undefined;
this.strictBound = undefined;
this.preloadBound = undefined;
this.globalTransform = undefined;
this.scaleRotateTransform = undefined;
this.localTransform = undefined;

this.props.texture = null;
this.props.shader = this.stage.defShaderCtr;
Expand Down
10 changes: 5 additions & 5 deletions src/core/Stage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,6 @@ export class Stage {
// Reset render operations and clear the canvas
renderer.reset();

// Check if we need to cleanup textures
if (this.txMemManager.criticalCleanupRequested) {
this.txMemManager.cleanup();
}

// If we have RTT nodes draw them first
// So we can use them as textures in the main scene
if (renderer.rttNodes.length > 0) {
Expand All @@ -296,6 +291,11 @@ export class Stage {
// Perform render pass
renderer?.render();

// Check if we need to cleanup textures
if (this.txMemManager.criticalCleanupRequested) {
this.txMemManager.cleanup();
}

this.calculateFps();

// Reset renderRequested flag if it was set
Expand Down
15 changes: 2 additions & 13 deletions src/core/lib/WebGlContextWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ export class WebGlContextWrapper {
) {
const { gl } = this;
if (format) {
// console.log('texImage2D-1', level, internalFormat, widthOrFormat, heightOrType, borderOrSource, format, type, pixels);
gl.texImage2D(
gl.TEXTURE_2D,
level,
Expand All @@ -329,6 +330,7 @@ export class WebGlContextWrapper {
pixels,
);
} else {
// console.log('texImage2D-2', level, internalFormat, widthOrFormat, heightOrType, borderOrSource);
gl.texImage2D(
gl.TEXTURE_2D,
level,
Expand Down Expand Up @@ -380,19 +382,6 @@ export class WebGlContextWrapper {
gl.pixelStorei(pname, param);
}

/**
* ```
* gl.generateMipmap(gl.TEXTURE_2D);
* ```
*
* @remarks
* **WebGL Difference**: Bind target is always `gl.TEXTURE_2D`
*/
generateMipmap() {
const { gl } = this;
gl.generateMipmap(gl.TEXTURE_2D);
}

/**
* ```
* gl.createTexture();
Expand Down
87 changes: 56 additions & 31 deletions src/core/renderers/webgl/WebGlCoreCtxTexture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { Dimensions } from '../../../common/CommonTypes.js';
import { assertTruthy } from '../../../utils.js';
import type { TextureMemoryManager } from '../../TextureMemoryManager.js';
import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
import type { Texture } from '../../textures/Texture.js';
import type { Texture, TextureData } from '../../textures/Texture.js';
import { isPowerOfTwo } from '../../utils.js';
import { CoreContextTexture } from '../CoreContextTexture.js';
import { isHTMLImageElement } from './internal/RendererUtils.js';
Expand Down Expand Up @@ -86,41 +86,70 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
this._state = 'loading';
this.textureSource.setState('loading');
this._nativeCtxTexture = this.createNativeCtxTexture();
this.onLoadRequest()
.then(({ width, height }) => {
// If the texture has been freed while loading, return early.
if (this._state === 'freed') {
return;
}
this._state = 'loaded';
this._w = width;
this._h = height;
// Update the texture source's width and height so that it can be used
// for rendering.
this.textureSource.setState('loaded', { width, height });
})
.catch((err) => {
// If the texture has been freed while loading, return early.
if (this._state === 'freed') {
return;
}
this._state = 'failed';
this.textureSource.setState('failed', err);
console.error(err);
});

let dimensions: Dimensions | Promise<Dimensions>;
try {
dimensions = this.onLoadRequest();
} catch (e: unknown) {
this.handleLoadError(e);
return;
}

if (dimensions instanceof Promise) {
dimensions
.then((dim) => this.handleLoad(dim))
.catch((err) => this.handleLoadError(err));
} else if (dimensions) {
this.handleLoad(dimensions);
}
}

handleLoad(dimensions: Dimensions) {
// If the texture has been freed while loading, return early.
if (this._state === 'freed') {
return;
}
const { width, height } = dimensions;
this._state = 'loaded';
this._w = width;
this._h = height;
// Update the texture source's width and height so that it can be used
// for rendering.
this.textureSource.setState('loaded', { width, height });
}

handleLoadError(err: unknown) {
// If the texture has been freed while loading, return early.
if (this._state === 'freed') {
return;
}
this._state = 'failed';
this.textureSource.setState('failed', err as Error);
console.error(err);
}

/**
* Called when the texture data needs to be loaded and uploaded to a texture
*/
async onLoadRequest(): Promise<Dimensions> {
onLoadRequest(): Promise<Dimensions> | Dimensions {
const { glw } = this;

// Set to a 1x1 transparent texture
glw.texImage2D(0, glw.RGBA, 1, 1, 0, glw.RGBA, glw.UNSIGNED_BYTE, null);
// glw.texImage2D(0, glw.RGBA, 1, 1, 0, glw.RGBA, glw.UNSIGNED_BYTE, null);
this.setTextureMemUse(TRANSPARENT_TEXTURE_DATA.byteLength);

const textureData = await this.textureSource?.getTextureData();
const textureData = this.textureSource?.getTextureData();

if (textureData instanceof Promise) {
return textureData.then((data) => this.handleTextureData(data));
} else {
return this.handleTextureData(textureData);
}
}

private handleTextureData(textureData: TextureData) {
const { glw } = this;

// If the texture has been freed while loading, return early.
if (!this._nativeCtxTexture) {
assertTruthy(this._state === 'freed');
Expand All @@ -136,6 +165,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
if (
textureData.data instanceof ImageBitmap ||
textureData.data instanceof ImageData ||
textureData.data instanceof HTMLCanvasElement ||
// not using typeof HTMLImageElement due to web worker
isHTMLImageElement(textureData.data)
) {
Expand All @@ -150,11 +180,6 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {

glw.texImage2D(0, glw.RGBA, glw.RGBA, glw.UNSIGNED_BYTE, data);
this.setTextureMemUse(width * height * 4);

// generate mipmaps for power-of-2 textures or in WebGL2RenderingContext
if (glw.isWebGl2() || (isPowerOfTwo(width) && isPowerOfTwo(height))) {
glw.generateMipmap();
}
} else if (textureData.data === null) {
width = 0;
height = 0;
Expand Down
6 changes: 3 additions & 3 deletions src/core/text-rendering/TextTextureRendererUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,21 @@ import type { WebTrFontFace } from './font-face-types/WebTrFontFace.js';
/**
* Returns CSS font setting string for use in canvas context.
*
* @param fontFace
* @param fontFamily
* @param fontStyle
* @param fontSize
* @param precision
* @param defaultFontFace
* @returns
*/
export function getFontSetting(
fontFace: string | string[],
fontFamily: string | string[],
fontStyle: string,
fontSize: number,
precision: number,
defaultFontFace: string,
): string {
let ff = fontFace;
let ff = fontFamily;

if (!Array.isArray(ff)) {
ff = [ff];
Expand Down
Loading
Loading