From 58eaad2e35261f2faaba2aac8e34e609daaf23af Mon Sep 17 00:00:00 2001 From: wouterlucas Date: Mon, 21 Oct 2024 18:01:01 +0200 Subject: [PATCH 1/4] Platform wip --- src/core/platforms/CorePlatform.ts | 18 ++++++++++++++++++ .../web}/WebGlContextWrapper.ts | 4 ++-- .../webgl/WebGlCoreCtxRenderTexture.ts | 2 +- .../renderers/webgl/WebGlCoreCtxSubTexture.ts | 2 +- .../renderers/webgl/WebGlCoreCtxTexture.ts | 2 +- src/core/renderers/webgl/WebGlCoreRenderOp.ts | 2 +- src/core/renderers/webgl/WebGlCoreRenderer.ts | 2 +- src/core/renderers/webgl/WebGlCoreShader.ts | 2 +- .../renderers/webgl/internal/RendererUtils.ts | 2 +- .../renderers/webgl/internal/ShaderUtils.ts | 2 +- .../renderers/webgl/shaders/DynamicShader.ts | 2 +- .../webgl/shaders/effects/ShaderEffect.ts | 2 +- 12 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/core/platforms/CorePlatform.ts rename src/core/{lib => platforms/web}/WebGlContextWrapper.ts (99%) diff --git a/src/core/platforms/CorePlatform.ts b/src/core/platforms/CorePlatform.ts new file mode 100644 index 00000000..8288d746 --- /dev/null +++ b/src/core/platforms/CorePlatform.ts @@ -0,0 +1,18 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 Comcast Cable Communications Management, LLC. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/src/core/lib/WebGlContextWrapper.ts b/src/core/platforms/web/WebGlContextWrapper.ts similarity index 99% rename from src/core/lib/WebGlContextWrapper.ts rename to src/core/platforms/web/WebGlContextWrapper.ts index ce923459..584c45f2 100644 --- a/src/core/lib/WebGlContextWrapper.ts +++ b/src/core/platforms/web/WebGlContextWrapper.ts @@ -2,8 +2,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -import { assertTruthy } from '../../utils.js'; -import { isWebGl2 } from '../renderers/webgl/internal/WebGlUtils.js'; +import { assertTruthy } from '../../../utils.js'; +import { isWebGl2 } from '../../renderers/webgl/internal/WebGlUtils.js'; /** * Optimized WebGL Context Wrapper diff --git a/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts b/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts index 90809c27..a670ad05 100644 --- a/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts +++ b/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts @@ -20,7 +20,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 { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import type { RenderTexture } from '../../textures/RenderTexture.js'; import { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js'; diff --git a/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts b/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts index 3b30b796..1db0271d 100644 --- a/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +++ b/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts @@ -19,7 +19,7 @@ import type { Dimensions } from '../../../common/CommonTypes.js'; import type { TextureMemoryManager } from '../../TextureMemoryManager.js'; -import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js'; +import type { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import type { SubTexture } from '../../textures/SubTexture.js'; import { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js'; diff --git a/src/core/renderers/webgl/WebGlCoreCtxTexture.ts b/src/core/renderers/webgl/WebGlCoreCtxTexture.ts index e09c9969..c82cc858 100644 --- a/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +++ b/src/core/renderers/webgl/WebGlCoreCtxTexture.ts @@ -20,7 +20,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 { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import type { Texture } from '../../textures/Texture.js'; import { isPowerOfTwo } from '../../utils.js'; import { CoreContextTexture } from '../CoreContextTexture.js'; diff --git a/src/core/renderers/webgl/WebGlCoreRenderOp.ts b/src/core/renderers/webgl/WebGlCoreRenderOp.ts index f816e60a..d6b007a8 100644 --- a/src/core/renderers/webgl/WebGlCoreRenderOp.ts +++ b/src/core/renderers/webgl/WebGlCoreRenderOp.ts @@ -24,7 +24,7 @@ import type { WebGlCoreRendererOptions } from './WebGlCoreRenderer.js'; import type { BufferCollection } from './internal/BufferCollection.js'; import type { Dimensions } from '../../../common/CommonTypes.js'; import type { Rect, RectWithValid } from '../../lib/utils.js'; -import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js'; +import type { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; const MAX_TEXTURES = 8; // TODO: get from gl diff --git a/src/core/renderers/webgl/WebGlCoreRenderer.ts b/src/core/renderers/webgl/WebGlCoreRenderer.ts index d3fe40b4..b0faf4ca 100644 --- a/src/core/renderers/webgl/WebGlCoreRenderer.ts +++ b/src/core/renderers/webgl/WebGlCoreRenderer.ts @@ -47,7 +47,7 @@ import { } from '../../lib/utils.js'; import type { Dimensions } from '../../../common/CommonTypes.js'; import { WebGlCoreShader } from './WebGlCoreShader.js'; -import { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js'; +import { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import { RenderTexture } from '../../textures/RenderTexture.js'; import type { CoreNode } from '../../CoreNode.js'; import { WebGlCoreCtxRenderTexture } from './WebGlCoreCtxRenderTexture.js'; diff --git a/src/core/renderers/webgl/WebGlCoreShader.ts b/src/core/renderers/webgl/WebGlCoreShader.ts index f6f463bf..0f5432d4 100644 --- a/src/core/renderers/webgl/WebGlCoreShader.ts +++ b/src/core/renderers/webgl/WebGlCoreShader.ts @@ -19,7 +19,7 @@ import type { Dimensions } from '../../../common/CommonTypes.js'; import { assertTruthy, hasOwn } from '../../../utils.js'; -import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js'; +import type { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import { CoreShader } from '../CoreShader.js'; import type { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js'; import type { WebGlCoreRenderOp } from './WebGlCoreRenderOp.js'; diff --git a/src/core/renderers/webgl/internal/RendererUtils.ts b/src/core/renderers/webgl/internal/RendererUtils.ts index 78efb1cb..c4cea25e 100644 --- a/src/core/renderers/webgl/internal/RendererUtils.ts +++ b/src/core/renderers/webgl/internal/RendererUtils.ts @@ -17,7 +17,7 @@ * limitations under the License. */ -import type { WebGlContextWrapper } from '../../../lib/WebGlContextWrapper.js'; +import type { WebGlContextWrapper } from '../../../platforms/web/WebGlContextWrapper.js'; export interface CoreWebGlParameters { MAX_RENDERBUFFER_SIZE: number; diff --git a/src/core/renderers/webgl/internal/ShaderUtils.ts b/src/core/renderers/webgl/internal/ShaderUtils.ts index fd59e78b..91cbea2b 100644 --- a/src/core/renderers/webgl/internal/ShaderUtils.ts +++ b/src/core/renderers/webgl/internal/ShaderUtils.ts @@ -17,7 +17,7 @@ * limitations under the License. */ -import type { WebGlContextWrapper } from '../../../lib/WebGlContextWrapper.js'; +import type { WebGlContextWrapper } from '../../../platforms/web/WebGlContextWrapper.js'; import type { WebGlCoreRenderer } from '../WebGlCoreRenderer.js'; //#region Types diff --git a/src/core/renderers/webgl/shaders/DynamicShader.ts b/src/core/renderers/webgl/shaders/DynamicShader.ts index afea023a..4536843a 100644 --- a/src/core/renderers/webgl/shaders/DynamicShader.ts +++ b/src/core/renderers/webgl/shaders/DynamicShader.ts @@ -33,7 +33,7 @@ import { } from './effects/ShaderEffect.js'; import type { EffectMap } from '../../../CoreShaderManager.js'; import { assertTruthy } from '../../../../utils.js'; -import type { UniformMethodMap } from '../../../lib/WebGlContextWrapper.js'; +import type { UniformMethodMap } from '../../../platforms/web/WebGlContextWrapper.js'; export interface DynamicShaderProps extends DimensionsShaderProp, diff --git a/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts b/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts index c3fd5296..eca1db65 100644 --- a/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts +++ b/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts @@ -1,6 +1,6 @@ import type { EffectMap } from '../../../../CoreShaderManager.js'; import type { ExtractProps } from '../../../../CoreTextureManager.js'; -import type { WebGlContextWrapper } from '../../../../lib/WebGlContextWrapper.js'; +import type { WebGlContextWrapper } from '../../../../platforms/web/WebGlContextWrapper.js'; import type { AlphaShaderProp, DimensionsShaderProp, From 78a89763cbda7c10f9ad998fcff580c3f102a9fb Mon Sep 17 00:00:00 2001 From: wouterlucas Date: Thu, 24 Oct 2024 15:50:50 +0200 Subject: [PATCH 2/4] feat: Implement CorePlatform class and add abstract methods. Add Web implementation --- src/core/platforms/CoreGlContext.ts | 543 ++++++++++ src/core/platforms/CorePlatform.ts | 94 ++ src/core/platforms/web/WebGlContext.ts | 1308 ++++++++++++++++++++++++ src/core/platforms/web/WebPlatform.ts | 126 +++ 4 files changed, 2071 insertions(+) create mode 100644 src/core/platforms/CoreGlContext.ts create mode 100644 src/core/platforms/web/WebGlContext.ts create mode 100644 src/core/platforms/web/WebPlatform.ts diff --git a/src/core/platforms/CoreGlContext.ts b/src/core/platforms/CoreGlContext.ts new file mode 100644 index 00000000..698dd623 --- /dev/null +++ b/src/core/platforms/CoreGlContext.ts @@ -0,0 +1,543 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 Comcast Cable Communications Management, LLC. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export abstract class CoreGlContext { + /** + * Sets the active texture unit. + * @param textureUnit - The index of the texture unit to activate. + */ + abstract activeTexture(textureUnit: number): void; + + /** + * Binds a WebGL texture to the current texture unit. + * @param texture - The texture to bind, or null to unbind. + */ + abstract bindTexture(texture: WebGLTexture | null): void; + + /** + * Creates a new WebGL texture object. + * @returns The newly created texture object, or null if the creation fails. + */ + abstract createTexture(): WebGLTexture | null; + + /** + * Deletes the specified WebGL texture object. + * @param texture - The texture to delete, or null to unbind. + */ + abstract deleteTexture(texture: WebGLTexture | null): void; + + /** + * Uploads a 2D texture image to the currently bound texture. + * @param level - The mipmap level to assign the image to. + * @param internalformat - The format of the texture data. + * @param width - The width of the image. + * @param height - The height of the image. + * @param border - The width of the border. Must be 0. + * @param format - The format of the texel data. + * @param type - The data type of the texel data. + * @param pixels - The pixel data to upload, or null to allocate uninitialized texture memory. + */ + abstract texImage2D( + level: GLint, + internalformat: GLint, + width: GLsizei, + height: GLsizei, + border: GLint, + format: GLenum, + type: GLenum, + pixels: ArrayBufferView | null, + ): void; + + /** + * Uploads a 2D texture image from an HTML image element, canvas, or video. + * @param level - The mipmap level to assign the image to. + * @param internalformat - The internal format of the texture (e.g., `gl.RGBA`). + * @param format - The format of the texel data (e.g., `gl.RGBA`). + * @param type - The data type of the texel data (e.g., `gl.UNSIGNED_BYTE`). + * @param source - The source of the texel data, typically an HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, or ImageBitmap. + */ + abstract texImage2D( + level: GLint, + internalformat: GLint, + format: GLenum, + type: GLenum, + source: TexImageSource, + ): void; + + /** + * Uploads compressed 2D texture data to the currently bound texture. + * @param level - The mipmap level to assign the compressed image to. + * @param internalformat - The format of the compressed texture data (e.g., `gl.COMPRESSED_RGBA_S3TC_DXT1_EXT`). + * @param width - The width of the texture image. + * @param height - The height of the texture image. + * @param border - The border width. Must be 0. + * @param data - The compressed image data to upload. + */ + abstract compressedTexImage2D( + level: GLint, + internalformat: GLenum, + width: GLsizei, + height: GLsizei, + border: GLint, + data?: ArrayBufferView, + ): void; + + /** + * Sets the texture parameters for the currently bound texture. + * @param pname - The name of the texture parameter to set (e.g., `gl.TEXTURE_WRAP_S` or `gl.TEXTURE_MIN_FILTER`). + * @param param - The value to set the texture parameter to (e.g., `gl.REPEAT` or `gl.LINEAR`). + */ + abstract texParameteri(pname: number, param: number): void; + + /** + * Generates mipmaps for the currently bound texture. + * Automatically generates a series of texture images with progressively lower resolution. + */ + abstract generateMipmap(): void; + + ////////////////////// + // Buffer Management + ////////////////////// + + /** + * Creates a new buffer object for storing vertex or index data. + * @returns The newly created WebGLBuffer object, or null if creation fails. + */ + abstract createBuffer(): WebGLBuffer | null; + + /** + * Allocates and initializes buffer data for the currently bound buffer object. + * @param target - The target to which the buffer is bound (e.g., `gl.ARRAY_BUFFER`). + * @param data - The data to initialize the buffer with (can be a typed array or ArrayBuffer). + * @param usage - The expected usage pattern of the buffer (e.g., `gl.STATIC_DRAW`, `gl.DYNAMIC_DRAW`). + */ + // L3 does not use this + // abstract bufferData(target: GLenum, data: ArrayBufferView, usage: GLenum): void; + + ////////////////////////// + // Framebuffer Management + ////////////////////////// + + /** + * Creates a new framebuffer object. + * Framebuffers are used to render to textures rather than directly to the screen. + * @returns The created WebGLFramebuffer object, or null if creation fails. + */ + abstract createFramebuffer(): WebGLFramebuffer | null; + + /** + * Binds a framebuffer to the specified target (e.g., `gl.FRAMEBUFFER`). + * @param framebuffer - The framebuffer to bind, or null to unbind the current framebuffer. + */ + abstract bindFramebuffer(framebuffer: WebGLFramebuffer | null): void; + + /** + * Attaches a texture to a framebuffer object at the specified attachment point. + * @param attachment - The attachment point to attach the texture to (e.g., `gl.COLOR_ATTACHMENT0`). + * @param texture - The texture to attach to the framebuffer. + * @param level - The mipmap level of the texture to attach. + */ + abstract framebufferTexture2D( + attachment: GLenum, + texture: WebGLTexture | null, + level: GLint, + ): void; + + //////////////////////// + // Shader Management + //////////////////////// + + /** + * Creates a new shader object. + * @param type - The type of the shader to create (e.g., `gl.VERTEX_SHADER` or `gl.FRAGMENT_SHADER`). + * @returns The created WebGLShader object, or null if the creation fails. + */ + abstract createShader(type: GLenum): WebGLShader | null; + + /** + * Sets the source code for a shader object. + * @param shader - The shader object for which to set the source code. + * @param source - The source code for the shader as a string. + */ + abstract shaderSource(shader: WebGLShader, source: string): void; + + /** + * Compiles the specified shader object. + * After compilation, you can use `getShaderParameter` to check if the compilation was successful. + * @param shader - The shader object to compile. + */ + abstract compileShader(shader: WebGLShader): void; + + /** + * Returns a parameter from a shader object. + * @param shader - The shader object to query. + * @param pname - The parameter to query (e.g., `gl.COMPILE_STATUS` to check if the shader compiled successfully). + * @returns The value of the requested parameter. + */ + abstract getShaderParameter(shader: WebGLShader, pname: GLenum): any; + + /** + * Returns the information log for a shader object. + * This log contains messages from the shader compiler, such as errors or warnings during compilation. + * @param shader - The shader object to query. + * @returns The information log as a string. + */ + abstract getShaderInfoLog(shader: WebGLShader): string | null; + + /** + * Deletes a shader object. + * This frees the memory and resources associated with the shader. + * @param shader - The shader object to delete. + */ + abstract deleteShader(shader: WebGLShader): void; + + //////////////////////// + // Program Management + //////////////////////// + + /** + * Creates a new program object. + * A program links together multiple shaders (typically a vertex and fragment shader) into a single executable. + * @returns The created WebGLProgram object, or null if the creation fails. + */ + abstract createProgram(): WebGLProgram | null; + + /** + * Attaches a shader object to a program object. + * @param program - The program object to attach the shader to. + * @param shader - The shader object to attach. + */ + abstract attachShader(program: WebGLProgram, shader: WebGLShader): void; + + /** + * Links the program object. + * This connects the attached shaders into a complete executable. + * You can check if the linking succeeded using `getProgramParameter`. + * @param program - The program object to link. + */ + abstract linkProgram(program: WebGLProgram): void; + + /** + * Sets the specified program object as the current active program for rendering. + * @param program - The program object to use, or null to stop using the current program. + */ + abstract useProgram(program: WebGLProgram | null): void; + + /** + * Returns a parameter from a program object. + * @param program - The program object to query. + * @param pname - The parameter to query (e.g., `gl.LINK_STATUS` to check if the program linked successfully). + * @returns The value of the requested parameter. + */ + abstract getProgramParameter(program: WebGLProgram, pname: GLenum): any; + + /** + * Returns the information log for a program object. + * This log contains messages from the program linker, such as errors or warnings during linking. + * @param program - The program object to query. + * @returns The information log as a string. + */ + abstract getProgramInfoLog(program: WebGLProgram): string | null; + + /** + * Deletes a program object. + * This frees the memory and resources associated with the program. + * @param program - The program object to delete. + */ + abstract deleteProgram(program: WebGLProgram): void; + + //////////////////////// + // Uniform Management + //////////////////////// + + /** + * Retrieves the location of a uniform variable within a program. + * @param program - The program containing the uniform. + * @param name - The name of the uniform variable. + * @returns The location of the uniform variable, or null if the uniform does not exist. + */ + abstract getUniformLocation( + program: WebGLProgram, + name: string, + ): WebGLUniformLocation | null; + + /** + * Sets the value of a float uniform variable. + * @param location - The location of the uniform variable. + * @param v0 - The float value to set. + */ + abstract uniform1f(location: WebGLUniformLocation | null, v0: number): void; + + /** + * Sets the value of a vec2 (2-component float vector) uniform variable. + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + */ + abstract uniform2f( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + ): void; + + /** + * Sets the value of a vec3 (3-component float vector) uniform variable. + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + * @param v2 - The third component of the vector. + */ + abstract uniform3f( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + v2: number, + ): void; + + /** + * Sets the value of a vec4 (4-component float vector) uniform variable. + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + * @param v2 - The third component of the vector. + * @param v3 - The fourth component of the vector. + */ + abstract uniform4f( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + v2: number, + v3: number, + ): void; + + /** + * Sets the value of a float array uniform variable. + * @param location - The location of the uniform variable. + * @param value - The array of float values to set. + */ + abstract uniform1fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ): void; + + /** + * Sets the value of a 2x2 float matrix uniform variable. + * @param location - The location of the uniform variable. + * @param value - The matrix to set (must be 2x2). + */ + abstract uniformMatrix2fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ): void; + + /** + * Sets the value of a 3x3 float matrix uniform variable. + * @param location - The location of the uniform variable. + * @param value - The matrix to set (must be 3x3). + */ + abstract uniformMatrix3fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ): void; + + /** + * Sets the value of a 4x4 float matrix uniform variable. + * @param location - The location of the uniform variable. + * @param value - The matrix to set (must be 4x4). + */ + abstract uniformMatrix4fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ): void; + + /** + * Sets the value of an integer uniform variable. + * @param location - The location of the uniform variable. + * @param v0 - The integer value to set. + */ + abstract uniform1i(location: WebGLUniformLocation | null, v0: number): void; + + /** + * Sets the value of a vec2 (2-component integer vector) uniform variable. + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + */ + abstract uniform2i( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + ): void; + + /** + * Sets the value of a vec3 (3-component integer vector) uniform variable. + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + * @param v2 - The third component of the vector. + */ + abstract uniform3i( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + v2: number, + ): void; + + /** + * Sets the value of a vec4 (4-component integer vector) uniform variable. + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + * @param v2 - The third component of the vector. + * @param v3 - The fourth component of the vector. + */ + abstract uniform4i( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + v2: number, + v3: number, + ): void; + + /** + * Sets the value of an integer array uniform variable. + * @param location - The location of the uniform variable. + * @param value - The array of integer values to set. + */ + abstract uniform1iv( + location: WebGLUniformLocation | null, + value: Int32Array | number[], + ): void; + + //////////////////////// + // Viewport and Scissor Management + //////////////////////// + + /** + * Sets the viewport transformation. + * @param x - The x-coordinate of the lower-left corner of the viewport. + * @param y - The y-coordinate of the lower-left corner of the viewport. + * @param width - The width of the viewport. + * @param height - The height of the viewport. + */ + abstract viewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei): void; + + /** + * Defines a scissor box, which restricts drawing to a specific rectangular area. + * @param x - The x-coordinate of the lower-left corner of the scissor box. + * @param y - The y-coordinate of the lower-left corner of the scissor box. + * @param width - The width of the scissor box. + * @param height - The height of the scissor box. + */ + abstract scissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei): void; + + /** + * Enables or disables the scissor test. + * @param enable - Whether to enable or disable the scissor test. + */ + abstract setScissorTest(enable: boolean): void; + + //////////////////////// + // Blending and Drawing + //////////////////////// + + /** + * Enables or disables blending. + * @param blend - Whether to enable or disable blending. + */ + abstract setBlend(blend: boolean): void; + + /** + * Specifies the pixel arithmetic for blending. + * @param src - The source blending factor (e.g., `gl.SRC_ALPHA`). + * @param dst - The destination blending factor (e.g., `gl.ONE_MINUS_SRC_ALPHA`). + */ + abstract blendFunc(src: GLenum, dst: GLenum): void; + + /** + * Sets the color values used when clearing the color buffer. + * @param red - The red component of the clear color. + * @param green - The green component of the clear color. + * @param blue - The blue component of the clear color. + * @param alpha - The alpha component of the clear color. + */ + abstract clearColor( + red: GLclampf, + green: GLclampf, + blue: GLclampf, + alpha: GLclampf, + ): void; + + /** + * Clears buffers to preset values. + * @param mask - A bitmask indicating which buffer(s) to clear (e.g., `gl.COLOR_BUFFER_BIT`). + */ + abstract clear(mask: GLbitfield): void; + + /** + * Renders primitives from array data. + * @param mode - The kind of primitives to render (e.g., `gl.TRIANGLES`). + * @param count - The number of elements to be rendered. + * @param type - The data type of the elements in the element array buffer (e.g., `gl.UNSIGNED_SHORT`). + * @param offset - The offset in the element array buffer where the vertex data starts. + */ + abstract drawElements( + mode: GLenum, + count: GLsizei, + type: GLenum, + offset: GLintptr, + ): void; + + /** + * Renders primitives from the currently bound array data. + * @param mode - The kind of primitives to render (e.g., `gl.TRIANGLES`). + * @param first - The starting index in the array. + * @param count - The number of vertices to be rendered. + */ + // Currently not used in L3 renderer + // abstract drawArrays(mode: GLenum, first: GLint, count: GLsizei): void; + + //////////////////////// + // Extension handling + //////////////////////// + + /** + * Returns the WebGL extension with the specified name, or null if the extension is not supported. + * @param name - The name of the extension to retrieve. + * @returns The extension object, or null if the extension is not supported. + */ + abstract getExtension(name: string): any; + + //////////////////////// + // Query parameters and states + //////////////////////// + + /** + * Retrieves the value of a WebGL parameter. + * @param pname - The name of the parameter to query (e.g., `gl.MAX_TEXTURE_SIZE`). + * @returns The value of the requested parameter. + */ + abstract getParameter(pname: GLenum): any; + + /** + * Determines if the current WebGL context is WebGL2. + * @returns True if the context is WebGL2, otherwise false. + */ + abstract isWebGl2(): boolean; +} diff --git a/src/core/platforms/CorePlatform.ts b/src/core/platforms/CorePlatform.ts index 8288d746..65d6242f 100644 --- a/src/core/platforms/CorePlatform.ts +++ b/src/core/platforms/CorePlatform.ts @@ -16,3 +16,97 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { CoreGlContext } from './CoreGlContext.js'; +import { Stage } from '../Stage.js'; + +export abstract class CorePlatform { + /** + * Gets the WebGL context wrapper that provides access to WebGL-specific functions + * such as creating textures, binding buffers, etc. + * @returns {WebGLFunctions} An instance of WebGLFunctions that wraps the WebGL context. + */ + abstract get gl(): CoreGlContext; + + /** + * Creates a new canvas element. + * @returns The created HTMLCanvasElement. + */ + abstract createCanvas(): HTMLCanvasElement; + + /** + * Creates a WebGL context (either WebGL1 or WebGL2) for a given canvas. + * @param canvas - The canvas element to create the context for. + * @returns The created WebGLRenderingContext or WebGL2RenderingContext. + */ + abstract createWebGLContext( + canvas: HTMLCanvasElement, + ): WebGLRenderingContext | WebGL2RenderingContext; + + /** + * Creates a 2D canvas rendering context. + * @param canvas - The canvas element. + * @returns The 2D rendering context. + */ + abstract createCanvasRenderingContext2D( + canvas: HTMLCanvasElement, + ): CanvasRenderingContext2D; + + /** + * Sets the pixel ratio for the canvas. + * @param pixelRatio - The pixel ratio to set. + */ + abstract setCanvasPixelRatio(pixelRatio: number): void; + + /** + * Sets the clear color for the canvas. + * @param context - The 2D rendering context. + * @param color - The color to set. + */ + abstract setCanvasClearColor( + context: CanvasRenderingContext2D, + color: string, + ): void; + + /** + * Starts the main rendering loop, calling the provided update function every frame. + * @param Stage - The stage for rendering + */ + abstract startLoop(stage: Stage): void; + + /** + * Creates an ImageBitmap from an image source with specified cropping coordinates. + * @param image - The source image or blob to create an image bitmap from. + * @param sx - The x-coordinate of the sub-rectangle of the image. + * @param sy - The y-coordinate of the sub-rectangle of the image. + * @param sw - The width of the sub-rectangle of the image. + * @param sh - The height of the sub-rectangle of the image. + * @param options - Image bitmap options like premultiplyAlpha, colorSpaceConversion, etc. + * @returns A promise that resolves to an ImageBitmap. + */ + abstract createImageBitmap( + image: ImageBitmapSource, + sx: number, + sy: number, + sw: number, + sh: number, + options?: ImageBitmapOptions, + ): Promise; + + /** + * Creates an ImageBitmap from an image source without cropping. + * @param image - The source image or blob to create an image bitmap from. + * @param options - Image bitmap options like premultiplyAlpha, colorSpaceConversion, etc. + * @returns A promise that resolves to an ImageBitmap. + */ + abstract createImageBitmap( + image: ImageBitmapSource, + options?: ImageBitmapOptions, + ): Promise; + + /** + * Retrieves the current timestamp. + * @returns The current timestamp. + */ + abstract getTimeStamp(): number; +} diff --git a/src/core/platforms/web/WebGlContext.ts b/src/core/platforms/web/WebGlContext.ts new file mode 100644 index 00000000..54682e13 --- /dev/null +++ b/src/core/platforms/web/WebGlContext.ts @@ -0,0 +1,1308 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ + +import { assertTruthy } from '../../../utils.js'; +import { isWebGl2 } from '../../renderers/webgl/internal/WebGlUtils.js'; + +import { CoreGlContext } from '../CoreGlContext.js'; + +/** + * Optimized WebGL Context Wrapper + * + * @remarks + * This class contains the subset of the WebGLRenderingContext & WebGL2RenderingContext + * API that is used by the renderer. Select high volume WebGL methods include + * caching optimizations to avoid making WebGL calls if the state is already set + * to the desired value. + * + * While most methods contained are direct passthroughs to the WebGL context, + * some methods combine multiple WebGL calls into one for convenience, modify + * arguments to be more convenient, or are replaced by more specific methods. + * + * Not all methods are optimized. Only methods that are called frequently + * and/or have a high cost are optimized. + * + * A subset of GLenum constants are also exposed as properties on this class + * for convenience. + */ +export class WebGlContext extends CoreGlContext { + //#region Cached WebGL State + private activeTextureUnit = 0; + private texture2dUnits: Array; + private texture2dParams: WeakMap< + WebGLTexture, + Record + > = new WeakMap(); + private scissorEnabled; + private scissorX: number; + private scissorY: number; + private scissorWidth: number; + private scissorHeight: number; + private blendEnabled; + private blendSrcRgb: number; + private blendDstRgb: number; + private blendSrcAlpha: number; + private blendDstAlpha: number; + private boundArrayBuffer: WebGLBuffer | null; + private boundElementArrayBuffer: WebGLBuffer | null; + private curProgram: WebGLProgram | null; + //#endregion Cached WebGL State + + //#region Canvas + public readonly canvas; + //#endregion Canvas + + //#region WebGL Enums + public readonly MAX_RENDERBUFFER_SIZE; + public readonly MAX_TEXTURE_SIZE; + public readonly MAX_VIEWPORT_DIMS; + public readonly MAX_VERTEX_TEXTURE_IMAGE_UNITS; + public readonly MAX_TEXTURE_IMAGE_UNITS; + public readonly MAX_COMBINED_TEXTURE_IMAGE_UNITS; + public readonly MAX_VERTEX_ATTRIBS; + public readonly MAX_VARYING_VECTORS; + public readonly MAX_VERTEX_UNIFORM_VECTORS; + public readonly MAX_FRAGMENT_UNIFORM_VECTORS; + public readonly TEXTURE_MAG_FILTER; + public readonly TEXTURE_MIN_FILTER; + public readonly TEXTURE_WRAP_S; + public readonly TEXTURE_WRAP_T; + public readonly LINEAR; + public readonly CLAMP_TO_EDGE; + public readonly RGBA; + public readonly UNSIGNED_BYTE; + public readonly UNPACK_PREMULTIPLY_ALPHA_WEBGL; + public readonly UNPACK_FLIP_Y_WEBGL; + public readonly FLOAT; + public readonly TRIANGLES; + public readonly UNSIGNED_SHORT; + public readonly ONE; + public readonly ONE_MINUS_SRC_ALPHA; + public readonly VERTEX_SHADER; + public readonly FRAGMENT_SHADER; + public readonly STATIC_DRAW; + public readonly COMPILE_STATUS; + public readonly LINK_STATUS; + public readonly DYNAMIC_DRAW; + public readonly COLOR_ATTACHMENT0; + //#endregion WebGL Enums + + constructor(private gl: WebGLRenderingContext | WebGL2RenderingContext) { + // The following code extracts the current state of the WebGL context + // to our local JavaScript cached version of it. This is so we can + // avoid making WebGL calls if we don't need to. + // We could assume that the WebGL context is in a default state, but + // in the future we may want to support restoring a broken WebGL context + // and this will help with that. + super(); + this.activeTextureUnit = + (gl.getParameter(gl.ACTIVE_TEXTURE) as number) - gl.TEXTURE0; + const maxTextureUnits = gl.getParameter( + gl.MAX_TEXTURE_IMAGE_UNITS, + ) as number; + // save current texture units + this.texture2dUnits = new Array(maxTextureUnits) + .fill(undefined) + .map((_, i) => { + this.activeTexture(i); + return gl.getParameter(gl.TEXTURE_BINDING_2D) as WebGLTexture; + }); + // restore active texture unit + this.activeTexture(this.activeTextureUnit); + this.scissorEnabled = gl.isEnabled(gl.SCISSOR_TEST); + + const scissorBox = gl.getParameter(gl.SCISSOR_BOX) as [ + number, + number, + number, + number, + ]; + this.scissorX = scissorBox[0]; + this.scissorY = scissorBox[1]; + this.scissorWidth = scissorBox[2]; + this.scissorHeight = scissorBox[3]; + + this.blendEnabled = gl.isEnabled(gl.BLEND); + this.blendSrcRgb = gl.getParameter(gl.BLEND_SRC_RGB) as number; + this.blendDstRgb = gl.getParameter(gl.BLEND_DST_RGB) as number; + this.blendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA) as number; + this.blendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA) as number; + + this.boundArrayBuffer = gl.getParameter( + gl.ARRAY_BUFFER_BINDING, + ) as WebGLBuffer; + this.boundElementArrayBuffer = gl.getParameter( + gl.ELEMENT_ARRAY_BUFFER_BINDING, + ) as WebGLBuffer; + + this.curProgram = gl.getParameter( + gl.CURRENT_PROGRAM, + ) as WebGLProgram | null; + + this.canvas = gl.canvas; + + // Extract GLenums + this.MAX_RENDERBUFFER_SIZE = gl.MAX_RENDERBUFFER_SIZE; + this.MAX_TEXTURE_SIZE = gl.MAX_TEXTURE_SIZE; + this.MAX_VIEWPORT_DIMS = gl.MAX_VIEWPORT_DIMS; + this.MAX_VERTEX_TEXTURE_IMAGE_UNITS = gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS; + this.MAX_TEXTURE_IMAGE_UNITS = gl.MAX_TEXTURE_IMAGE_UNITS; + this.MAX_COMBINED_TEXTURE_IMAGE_UNITS = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS; + this.MAX_VERTEX_ATTRIBS = gl.MAX_VERTEX_ATTRIBS; + this.MAX_VARYING_VECTORS = gl.MAX_VARYING_VECTORS; + this.MAX_VERTEX_UNIFORM_VECTORS = gl.MAX_VERTEX_UNIFORM_VECTORS; + this.MAX_FRAGMENT_UNIFORM_VECTORS = gl.MAX_FRAGMENT_UNIFORM_VECTORS; + this.TEXTURE_MAG_FILTER = gl.TEXTURE_MAG_FILTER; + this.TEXTURE_MIN_FILTER = gl.TEXTURE_MIN_FILTER; + this.TEXTURE_WRAP_S = gl.TEXTURE_WRAP_S; + this.TEXTURE_WRAP_T = gl.TEXTURE_WRAP_T; + this.LINEAR = gl.LINEAR; + this.CLAMP_TO_EDGE = gl.CLAMP_TO_EDGE; + this.RGBA = gl.RGBA; + this.UNSIGNED_BYTE = gl.UNSIGNED_BYTE; + this.UNPACK_PREMULTIPLY_ALPHA_WEBGL = gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL; + this.UNPACK_FLIP_Y_WEBGL = gl.UNPACK_FLIP_Y_WEBGL; + this.FLOAT = gl.FLOAT; + this.TRIANGLES = gl.TRIANGLES; + this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT; + this.ONE = gl.ONE; + this.ONE_MINUS_SRC_ALPHA = gl.ONE_MINUS_SRC_ALPHA; + this.MAX_VERTEX_TEXTURE_IMAGE_UNITS = gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS; + this.TRIANGLES = gl.TRIANGLES; + this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT; + this.VERTEX_SHADER = gl.VERTEX_SHADER; + this.FRAGMENT_SHADER = gl.FRAGMENT_SHADER; + this.STATIC_DRAW = gl.STATIC_DRAW; + this.COMPILE_STATUS = gl.COMPILE_STATUS; + this.LINK_STATUS = gl.LINK_STATUS; + this.DYNAMIC_DRAW = gl.DYNAMIC_DRAW; + this.COLOR_ATTACHMENT0 = gl.COLOR_ATTACHMENT0; + } + /** + * Returns true if the WebGL context is WebGL2 + * + * @returns + */ + isWebGl2() { + return isWebGl2(this.gl); + } + + /** + * ``` + * gl.activeTexture(textureUnit + gl.TEXTURE0); + * ``` + * + * @remarks + * **WebGL Difference**: `textureUnit` is based from 0, not `gl.TEXTURE0`. + * + * @param textureUnit + */ + activeTexture(textureUnit: number) { + const { gl } = this; + if (this.activeTextureUnit !== textureUnit) { + gl.activeTexture(textureUnit + gl.TEXTURE0); + this.activeTextureUnit = textureUnit; + } + } + + /** + * ``` + * gl.bindTexture(gl.TEXTURE_2D, texture); + * ``` + * @remarks + * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` + * + * @param texture + */ + bindTexture(texture: WebGLTexture | null) { + const { gl, activeTextureUnit, texture2dUnits } = this; + + if (texture2dUnits[activeTextureUnit] === texture) { + return; + } + texture2dUnits[activeTextureUnit] = texture; + + gl.bindTexture(this.gl.TEXTURE_2D, texture); + } + + private _getActiveTexture(): WebGLTexture | null { + const { activeTextureUnit, texture2dUnits } = this; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return texture2dUnits[activeTextureUnit]!; + } + + /** + * ``` + * gl.texParameteri(gl.TEXTURE_2D, pname, param); + * ``` + * @remarks + * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` + * + * @param pname + * @param param + * @returns + */ + texParameteri(pname: number, param: number) { + const { gl, texture2dParams } = this; + + const activeTexture = this._getActiveTexture(); + if (!activeTexture) { + throw new Error('No active texture'); + } + let textureParams = texture2dParams.get(activeTexture); + if (!textureParams) { + textureParams = {}; + texture2dParams.set(activeTexture, textureParams); + } + if (textureParams[pname] === param) { + return; + } + textureParams[pname] = param; + gl.texParameteri(gl.TEXTURE_2D, pname, param); + } + + /** + * ``` + * gl.texImage2D( + * gl.TEXTURE_2D, + * level, + * internalFormat, + * width, + * height, + * border, + * format, + * type, + * pixels, + * ); + * ``` + * @remarks + * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` + * + * @param level + * @param internalFormat + * @param width + * @param height + * @param border + * @param format + * @param type + * @param pixels + */ + texImage2D( + level: GLint, + internalformat: GLint, + width: GLsizei, + height: GLsizei, + border: GLint, + format: GLenum, + type: GLenum, + pixels: ArrayBufferView | null, + ): void; + texImage2D( + level: GLint, + internalformat: GLint, + format: GLenum, + type: GLenum, + source: TexImageSource, + ): void; + texImage2D( + level: any, + internalFormat: any, + widthOrFormat: any, + heightOrType: any, + borderOrSource: any, + format?: any, + type?: any, + pixels?: any, + ) { + const { gl } = this; + if (format) { + gl.texImage2D( + gl.TEXTURE_2D, + level, + internalFormat, + widthOrFormat, + heightOrType, + borderOrSource, + format, + type, + pixels, + ); + } else { + gl.texImage2D( + gl.TEXTURE_2D, + level, + internalFormat, + widthOrFormat, + heightOrType, + borderOrSource, + ); + } + } + /** + * ``` + * gl.compressedTexImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, data); + * ``` + * + * @remarks + * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` + */ + + compressedTexImage2D( + level: GLint, + internalformat: GLenum, + width: GLsizei, + height: GLsizei, + border: GLint, + data?: ArrayBufferView, + ): void { + const { gl } = this; + gl.compressedTexImage2D( + gl.TEXTURE_2D, + level, + internalformat, + width, + height, + border, + data as ArrayBufferView, + ); + } + /** + * ``` + * gl.pixelStorei(pname, param); + * ``` + * + * @param pname + * @param param + */ + pixelStorei(pname: GLenum, param: GLint | GLboolean) { + const { gl } = this; + 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(); + * ``` + * + * @returns + */ + createTexture() { + const { gl } = this; + return gl.createTexture(); + } + + /** + * ``` + * gl.deleteTexture(texture); + * ``` + * + * @param texture + */ + deleteTexture(texture: WebGLTexture | null) { + const { gl } = this; + if (texture) { + this.texture2dParams.delete(texture); + } + gl.deleteTexture(texture); + } + + /** + * ``` + * gl.viewport(x, y, width, height); + * ``` + */ + viewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei) { + const { gl } = this; + gl.viewport(x, y, width, height); + } + + /** + * ``` + * gl.clearColor(red, green, blue, alpha); + * ``` + * + * @param red + * @param green + * @param blue + * @param alpha + */ + clearColor(red: GLclampf, green: GLclampf, blue: GLclampf, alpha: GLclampf) { + const { gl } = this; + gl.clearColor(red, green, blue, alpha); + } + + /** + * ``` + * gl["enable"|"disable"](gl.SCISSOR_TEST); + * ``` + * @param enable + */ + setScissorTest(enable: boolean) { + const { gl, scissorEnabled } = this; + if (enable === scissorEnabled) { + return; + } + if (enable) { + gl.enable(gl.SCISSOR_TEST); + } else { + gl.disable(gl.SCISSOR_TEST); + } + this.scissorEnabled = enable; + } + + /** + * ``` + * gl.scissor(x, y, width, height); + * ``` + * + * @param x + * @param y + * @param width + * @param height + */ + scissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei) { + const { gl, scissorX, scissorY, scissorWidth, scissorHeight } = this; + if ( + x !== scissorX || + y !== scissorY || + width !== scissorWidth || + height !== scissorHeight + ) { + gl.scissor(x, y, width, height); + this.scissorX = x; + this.scissorY = y; + this.scissorWidth = width; + this.scissorHeight = height; + } + } + + /** + * ``` + * gl["enable"|"disable"](gl.BLEND); + * ``` + * + * @param blend + * @returns + */ + setBlend(blend: boolean) { + const { gl, blendEnabled } = this; + if (blend === blendEnabled) { + return; + } + if (blend) { + gl.enable(gl.BLEND); + } else { + gl.disable(gl.BLEND); + } + this.blendEnabled = blend; + } + + /** + * ``` + * gl.blendFunc(src, dst); + * ``` + * + * @param src + * @param dst + */ + blendFunc(src: GLenum, dst: GLenum) { + const { gl, blendSrcRgb, blendDstRgb, blendSrcAlpha, blendDstAlpha } = this; + if ( + src !== blendSrcRgb || + dst !== blendDstRgb || + src !== blendSrcAlpha || + dst !== blendDstAlpha + ) { + gl.blendFunc(src, dst); + this.blendSrcRgb = src; + this.blendDstRgb = dst; + this.blendSrcAlpha = src; + this.blendDstAlpha = dst; + } + } + + /** + * ``` + * gl.createBuffer(); + * ``` + * + * @returns + */ + createBuffer() { + const { gl } = this; + return gl.createBuffer(); + } + + /** + * ``` + * gl.createFramebuffer(); + * ``` + * @returns + */ + createFramebuffer() { + const { gl } = this; + return gl.createFramebuffer(); + } + + /** + * ``` + * gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + * ``` + * + * @param framebuffer + */ + bindFramebuffer(framebuffer: WebGLFramebuffer | null) { + const { gl } = this; + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + } + + /** + * ``` + * gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + * ``` + * @remarks + * **WebGL Difference**: Bind target is always `gl.FRAMEBUFFER` and textarget is always `gl.TEXTURE_2D` + */ + + framebufferTexture2D( + attachment: GLenum, + texture: WebGLTexture | null, + level: GLint, + ) { + const { gl } = this; + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + attachment, + gl.TEXTURE_2D, + texture, + level, + ); + } + + /** + * ``` + * gl.clear(gl.COLOR_BUFFER_BIT); + * ``` + * + * @remarks + * **WebGL Difference**: Clear mask is always `gl.COLOR_BUFFER_BIT` + */ + clear() { + const { gl } = this; + gl.clear(gl.COLOR_BUFFER_BIT); + } + + /** + * ``` + * gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + * gl.bufferData(gl.ARRAY_BUFFER, data, usage); + * ``` + * + * @remarks + * **WebGL Combo**: `gl.bindBuffer` and `gl.bufferData` are combined into one function. + * + * @param buffer + * @param data + * @param usage + */ + arrayBufferData( + buffer: WebGLBuffer | null, + data: ArrayBufferView, + usage: GLenum, + ) { + const { gl, boundArrayBuffer } = this; + if (boundArrayBuffer !== buffer) { + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + this.boundArrayBuffer = buffer; + } + gl.bufferData(gl.ARRAY_BUFFER, data, usage); + } + + /** + * ``` + * gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); + * gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, usage); + * ``` + * @remarks + * **WebGL Combo**: `gl.bindBuffer` and `gl.bufferData` are combined into one function. + * + * @param buffer + * @param data + * @param usage + */ + elementArrayBufferData( + buffer: WebGLBuffer | null, + data: ArrayBufferView, + usage: GLenum, + ) { + const { gl, boundElementArrayBuffer } = this; + if (boundElementArrayBuffer !== buffer) { + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); + this.boundElementArrayBuffer = buffer; + } + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, usage); + } + + /** + * ``` + * gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + * gl.vertexAttribPointer(index, size, type, normalized, stride, offset); + * ``` + * + * @remarks + * **WebGL Combo**: `gl.bindBuffer` and `gl.vertexAttribPointer` are combined into one function. + * + * @param buffer + * @param index + * @param size + * @param type + * @param normalized + * @param stride + * @param offset + */ + vertexAttribPointer( + buffer: WebGLBuffer, + index: GLuint, + size: GLint, + type: GLenum, + normalized: GLboolean, + stride: GLsizei, + offset: GLintptr, + ) { + const { gl, boundArrayBuffer } = this; + if (boundArrayBuffer !== buffer) { + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + this.boundArrayBuffer = buffer; + } + gl.vertexAttribPointer(index, size, type, normalized, stride, offset); + } + + /** + * ``` + * gl.useProgram(program); + * ``` + * + * @param program + * @returns + */ + useProgram(program: WebGLProgram | null) { + const { gl, curProgram } = this; + if (curProgram === program) { + return; + } + gl.useProgram(program); + this.curProgram = program; + } + + /** + * Sets the value of a single float uniform variable. + * + * @param location - The location of the uniform variable. + * @param v0 - The value to set. + */ + uniform1f(location: WebGLUniformLocation | null, v0: number) { + const { gl } = this; + gl.uniform1f(location, v0); + } + + /** + * Sets the value of a float array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of values to set. + */ + uniform1fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ) { + const { gl } = this; + gl.uniform1fv(location, value); + } + + /** + * Sets the value of a single integer uniform variable. + * + * @param location - The location of the uniform variable. + * @param v0 - The value to set. + */ + uniform1i(location: WebGLUniformLocation | null, v0: number) { + const { gl } = this; + gl.uniform1i(location, v0); + } + + /** + * Sets the value of an integer array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of values to set. + */ + uniform1iv( + location: WebGLUniformLocation | null, + value: Int32Array | number[], + ) { + const { gl } = this; + gl.uniform1iv(location, value); + } + + /** + * Sets the value of a vec2 uniform variable. + * + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + */ + uniform2f(location: WebGLUniformLocation | null, v0: number, v1: number) { + const { gl } = this; + gl.uniform2f(location, v0, v1); + } + + /** + * Sets the value of a vec2 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of vec2 values to set. + */ + uniform2fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ) { + const { gl } = this; + gl.uniform2fv(location, value); + } + + /** + * Sets the value of a ivec2 uniform variable. + * + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + */ + uniform2i(location: WebGLUniformLocation | null, v0: number, v1: number) { + const { gl } = this; + gl.uniform2i(location, v0, v1); + } + + /** + * Sets the value of an ivec2 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of ivec2 values to set. + */ + uniform2iv( + location: WebGLUniformLocation | null, + value: Int32Array | number[], + ) { + const { gl } = this; + gl.uniform2iv(location, value); + } + + /** + * Sets the value of a vec3 uniform variable. + * + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + * @param v2 - The third component of the vector. + */ + uniform3f( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + v2: number, + ) { + const { gl } = this; + gl.uniform3f(location, v0, v1, v2); + } + + /** + * Sets the value of a vec3 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of vec3 values to set. + */ + uniform3fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ) { + const { gl } = this; + gl.uniform3fv(location, value); + } + + /** + * Sets the value of a ivec3 uniform variable. + * + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + * @param v2 - The third component of the vector. + */ + uniform3i( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + v2: number, + ) { + const { gl } = this; + gl.uniform3i(location, v0, v1, v2); + } + + /** + * Sets the value of an ivec3 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of ivec3 values to set. + */ + uniform3iv( + location: WebGLUniformLocation | null, + value: Int32Array | number[], + ) { + const { gl } = this; + gl.uniform3iv(location, value); + } + + /** + * Sets the value of a vec4 uniform variable. + * + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + * @param v2 - The third component of the vector. + * @param v3 - The fourth component of the vector. + */ + uniform4f( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + v2: number, + v3: number, + ) { + const { gl } = this; + gl.uniform4f(location, v0, v1, v2, v3); + } + + /** + * Sets the value of a vec4 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of vec4 values to set. + */ + uniform4fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ) { + const { gl } = this; + gl.uniform4fv(location, value); + } + + /** + * Sets the value of a ivec4 uniform variable. + * + * @param location - The location of the uniform variable. + * @param v0 - The first component of the vector. + * @param v1 - The second component of the vector. + * @param v2 - The third component of the vector. + * @param v3 - The fourth component of the vector. + */ + uniform4i( + location: WebGLUniformLocation | null, + v0: number, + v1: number, + v2: number, + v3: number, + ) { + const { gl } = this; + gl.uniform4i(location, v0, v1, v2, v3); + } + + /** + * Sets the value of an ivec4 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of ivec4 values to set. + */ + uniform4iv( + location: WebGLUniformLocation | null, + value: Int32Array | number[], + ) { + const { gl } = this; + gl.uniform4iv(location, value); + } + + /** + * Sets the value of a mat2 uniform variable. + * + * @param location - The location of the uniform variable. + * @param transpose - Whether to transpose the matrix. + * @param value - The array of mat2 values to set. + */ + uniformMatrix2fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ) { + const { gl } = this; + gl.uniformMatrix2fv(location, false, value); + } + + /** + * Sets the value of a mat2 uniform variable. + * @param location - The location of the uniform variable. + * @param value - The array of mat2 values to set. + */ + uniformMatrix3fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ) { + const { gl } = this; + gl.uniformMatrix3fv(location, false, value); + } + + /** + * Sets the value of a mat4 uniform variable. + * @param location - The location of the uniform variable. + * @param value - The array of mat4 values to set. + */ + uniformMatrix4fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ) { + const { gl } = this; + gl.uniformMatrix4fv(location, false, value); + } + + /** + * ``` + * gl.getParameter(pname); + * ``` + * + * @param pname + * @returns + */ + getParameter(pname: GLenum): any { + const { gl } = this; + return gl.getParameter(pname); + } + + /** + * ``` + * gl.drawElements(mode, count, type, offset); + * ``` + * + * @param mode + * @param count + * @param type + * @param offset + */ + drawElements(mode: GLenum, count: GLsizei, type: GLenum, offset: GLintptr) { + const { gl } = this; + gl.drawElements(mode, count, type, offset); + } + + /** + * ``` + * gl.drawArrays(mode, first, count); + * ``` + * + * @param name + * @returns + */ + getExtension(name: string) { + const { gl } = this; + return gl.getExtension(name); + } + + /** + * ``` + * gl.createVertexArray(); + * ``` + * + * @returns + */ + createVertexArray() { + const { gl } = this; + assertTruthy(gl instanceof WebGL2RenderingContext); + return gl.createVertexArray(); + } + + /** + * ``` + * gl.bindVertexArray(vertexArray); + * ``` + * + * @param vertexArray + */ + bindVertexArray(vertexArray: WebGLVertexArrayObject | null) { + const { gl } = this; + assertTruthy(gl instanceof WebGL2RenderingContext); + gl.bindVertexArray(vertexArray); + } + + /** + * ``` + * gl.getAttribLocation(program, name); + * ``` + * + * @param program + * @param name + * @returns + */ + getAttribLocation(program: WebGLProgram, name: string) { + const { gl } = this; + return gl.getAttribLocation(program, name); + } + + /** + * ``` + * gl.getUniformLocation(program, name); + * ``` + * + * @param program + * @param name + * @returns + */ + getUniformLocation(program: WebGLProgram, name: string) { + const { gl } = this; + return gl.getUniformLocation(program, name); + } + + /** + * ``` + * gl.enableVertexAttribArray(index); + * ``` + * + * @param index + */ + enableVertexAttribArray(index: number) { + const { gl } = this; + gl.enableVertexAttribArray(index); + } + + /** + * ``` + * gl.disableVertexAttribArray(index); + * ``` + * + * @param index + */ + disableVertexAttribArray(index: number) { + const { gl } = this; + gl.disableVertexAttribArray(index); + } + + /** + * ``` + * gl.createShader(type); + * ``` + * + * @param type + * @returns + */ + createShader(type: number) { + const { gl } = this; + return gl.createShader(type); + } + + /** + * ``` + * gl.compileShader(shader); + * ``` + * + * @param shader + * @returns + */ + compileShader(shader: WebGLShader) { + const { gl } = this; + gl.compileShader(shader); + } + + /** + * ``` + * gl.attachShader(program, shader); + * ``` + * + * @param program + * @param shader + */ + attachShader(program: WebGLProgram, shader: WebGLShader) { + const { gl } = this; + gl.attachShader(program, shader); + } + + /** + * ``` + * gl.linkProgram(program); + * ``` + * + * @param program + */ + linkProgram(program: WebGLProgram) { + const { gl } = this; + gl.linkProgram(program); + } + + /** + * ``` + * gl.deleteProgram(shader); + * ``` + * + * @param shader + */ + deleteProgram(shader: WebGLProgram) { + const { gl } = this; + gl.deleteProgram(shader); + } + + /** + * ``` + * gl.getShaderParameter(shader, pname); + * ``` + * + * @param shader + * @param pname + */ + getShaderParameter(shader: WebGLShader, pname: GLenum) { + const { gl } = this; + return gl.getShaderParameter(shader, pname); + } + + /** + * ``` + * gl.getShaderInfoLog(shader); + * ``` + * + * @param shader + */ + getShaderInfoLog(shader: WebGLShader) { + const { gl } = this; + return gl.getShaderInfoLog(shader); + } + + /** + * ``` + * gl.createProgram(); + * ``` + * + * @returns + */ + createProgram() { + const { gl } = this; + return gl.createProgram(); + } + + /** + * ``` + * gl.getProgramParameter(program, pname); + * ``` + * + * @param program + * @param pname + * @returns + */ + getProgramParameter(program: WebGLProgram, pname: GLenum) { + const { gl } = this; + return gl.getProgramParameter(program, pname); + } + + /** + * ``` + * gl.getProgramInfoLog(program); + * ``` + * + * @param program + * @returns + */ + getProgramInfoLog(program: WebGLProgram) { + const { gl } = this; + return gl.getProgramInfoLog(program); + } + + /** + * ``` + * gl.shaderSource(shader, source); + * ``` + * + * @param shader + * @param source + */ + shaderSource(shader: WebGLShader, source: string) { + const { gl } = this; + gl.shaderSource(shader, source); + } + + /** + * ``` + * gl.deleteShader(shader); + * ``` + * + * @param shader + */ + deleteShader(shader: WebGLShader) { + const { gl } = this; + gl.deleteShader(shader); + } +} + +// prettier-ignore +type IsUniformMethod = MethodName extends `uniform${string}` + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + MethodType extends (location: WebGLUniformLocation | null, ...args: any[]) => void + ? true + : false + : false; + +// prettier-ignore +export type UniformMethodMap = { + [Key in keyof WebGLRenderingContext as IsUniformMethod extends true ? Key : never]: WebGLRenderingContext[Key] extends ( + location: WebGLUniformLocation | null, + ...args: infer T + ) => void + ? T + : never; +}; + +/** + * Compare two arrays for equality. + * + * @remarks + * This function will not try to compare nested arrays or Float32Arrays and + * instead will always return false when they are encountered. + * + * @param a + * @param b + * @returns + */ +export function compareArrays(a: T[], b: T[]): boolean { + if (a.length !== b.length) { + return false; + } + + let result = false; + for (let i = 0; i < a.length; i++) { + if (Array.isArray(a[i]) || a[i] instanceof Float32Array) { + result = false; + break; + } + + if (a[i] !== b[i]) { + result = false; + break; + } + + result = true; + } + + return result; +} diff --git a/src/core/platforms/web/WebPlatform.ts b/src/core/platforms/web/WebPlatform.ts new file mode 100644 index 00000000..af1d22f2 --- /dev/null +++ b/src/core/platforms/web/WebPlatform.ts @@ -0,0 +1,126 @@ +import { CorePlatform } from '../CorePlatform.js'; +import { WebGlContext } from './WebGlContext.js'; +import { Stage } from '../../Stage.js'; + +export class WebPlatform extends CorePlatform { + private glContextWrapper!: WebGlContext; + + constructor() { + super(); + const canvas = this.createCanvas(); + this.glContextWrapper = new WebGlContext(this.createWebGLContext(canvas)); + } + + //////////////////////// + // WebGL Wrapper + //////////////////////// + + override get gl() { + return this.glContextWrapper; + } + + //////////////////////// + // Platform-specific methods + //////////////////////// + + override createCanvas(): HTMLCanvasElement { + const canvas = document.createElement('canvas'); + return canvas; + } + + override createWebGLContext( + canvas: HTMLCanvasElement, + ): WebGLRenderingContext | WebGL2RenderingContext { + const gl = canvas.getContext('webgl2') || canvas.getContext('webgl'); + if (!gl) { + throw new Error('WebGL not supported'); + } + return gl; + } + + override createCanvasRenderingContext2D( + canvas: HTMLCanvasElement, + ): CanvasRenderingContext2D { + const context = canvas.getContext('2d'); + if (!context) { + throw new Error('2D rendering context not supported'); + } + return context; + } + + override setCanvasPixelRatio(pixelRatio: number): void { + const devicePixelRatio = window.devicePixelRatio || 1; + document.body.style.zoom = (pixelRatio * devicePixelRatio).toString(); + } + + override setCanvasClearColor( + context: CanvasRenderingContext2D, + color: string, + ): void { + context.fillStyle = color; + context.fillRect(0, 0, context.canvas.width, context.canvas.height); + } + + //////////////////////// + // Update loop + //////////////////////// + + override startLoop(stage: Stage): void { + let isIdle = false; + const runLoop = () => { + stage.updateFrameTime(); + stage.updateAnimations(); + + if (!stage.hasSceneUpdates()) { + // We still need to calculate the fps else it looks like the app is frozen + stage.calculateFps(); + setTimeout(runLoop, 16.666666666666668); + + if (!isIdle) { + if (stage.txMemManager.checkCleanup()) { + stage.txMemManager.cleanup(); + } + stage.eventBus.emit('idle'); + isIdle = true; + } + stage.flushFrameEvents(); + return; + } + + isIdle = false; + stage.drawFrame(); + stage.flushFrameEvents(); + requestAnimationFrame(runLoop); + }; + requestAnimationFrame(runLoop); + } + + /** + * Implementation that supports both overloaded signatures for creating an ImageBitmap. + */ + createImageBitmap( + image: ImageBitmapSource, + sxOrOptions?: number | ImageBitmapOptions, + sy?: number, + sw?: number, + sh?: number, + options?: ImageBitmapOptions, + ): Promise { + // Check if the second argument is a number, meaning it's using the cropping version + if ( + typeof sxOrOptions === 'number' && + sy !== undefined && + sw !== undefined && + sh !== undefined + ) { + return window.createImageBitmap(image, sxOrOptions, sy, sw, sh, options); + } else { + // Otherwise, assume it's using the non-cropping version + return window.createImageBitmap(image, sxOrOptions as ImageBitmapOptions); + } + } + + getTimeStamp(): number { + return performance ? performance.now() : Date.now(); + } +} From 12b62db4f2086b750769cf7606e5135ebdc09b97 Mon Sep 17 00:00:00 2001 From: wouterlucas Date: Sun, 27 Oct 2024 11:13:38 +0100 Subject: [PATCH 3/4] refactor: (WIP) Update WebGlCoreRenderer to use CoreGlContext and WebGlContext types --- src/core/Stage.ts | 15 +- src/core/platform.ts | 61 - src/core/platforms/CorePlatform.ts | 80 +- src/core/platforms/web/WebGlContextWrapper.ts | 1304 ----------------- src/core/platforms/web/WebPlatform.ts | 109 +- src/core/renderers/CoreRenderer.ts | 3 + src/core/renderers/webgl/WebGlCoreRenderer.ts | 12 +- src/main-api/Renderer.ts | 30 +- src/utils.ts | 47 - 9 files changed, 113 insertions(+), 1548 deletions(-) delete mode 100644 src/core/platform.ts delete mode 100644 src/core/platforms/web/WebGlContextWrapper.ts diff --git a/src/core/Stage.ts b/src/core/Stage.ts index 811da222..2da105b7 100644 --- a/src/core/Stage.ts +++ b/src/core/Stage.ts @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { startLoop, getTimeStamp } from './platform.js'; + import { assertTruthy, setPremultiplyMode } from '../utils.js'; import { AnimationManager } from './animations/AnimationManager.js'; import { @@ -52,6 +52,8 @@ import { CoreTextNode, type CoreTextNodeProps } from './CoreTextNode.js'; import { santizeCustomDataMap } from '../main-api/utils.js'; import type { SdfTextRenderer } from './text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js'; import type { CanvasTextRenderer } from './text-rendering/renderers/CanvasTextRenderer.js'; +import type { CorePlatform } from './platforms/CorePlatform.js'; +import type { WebPlatform } from './platforms/web/WebPlatform.js'; export interface StageOptions { appWidth: number; @@ -71,6 +73,7 @@ export interface StageOptions { quadBufferSize: number; fontEngines: (typeof CanvasTextRenderer | typeof SdfTextRenderer)[]; inspector: boolean; + platform: CorePlatform | WebPlatform; } export type StageFpsUpdateHandler = ( @@ -119,6 +122,7 @@ export class Stage { private frameEventQueue: [name: string, payload: unknown][] = []; private fontResolveMap: Record = {}; + private platform: CorePlatform; /// Debug data contextSpy: ContextSpy | null = null; @@ -139,14 +143,16 @@ export class Stage { textureMemory, renderEngine, fontEngines, + platform, } = options; this.eventBus = options.eventBus; - this.txManager = new CoreTextureManager(numImageWorkers); + this.txManager = new CoreTextureManager(numImageWorkers, platform); this.txMemManager = new TextureMemoryManager(this, textureMemory); this.shManager = new CoreShaderManager(); this.animationManager = new AnimationManager(); this.contextSpy = enableContextSpy ? new ContextSpy() : null; + this.platform = platform; let bm = [0, 0, 0, 0] as [number, number, number, number]; if (boundsMargin) { @@ -158,6 +164,7 @@ export class Stage { const rendererOptions: CoreRendererOptions = { stage: this, + platform, canvas, pixelRatio: options.devicePhysicalPixelRatio * options.deviceLogicalPixelRatio, @@ -252,12 +259,12 @@ export class Stage { // execute platform start loop if (autoStart) { - startLoop(this); + platform.startLoop(this); } } updateFrameTime() { - const newFrameTime = getTimeStamp(); + const newFrameTime = this.platform.getTimeStamp(); this.lastFrameTime = this.currentFrameTime; this.currentFrameTime = newFrameTime; this.deltaTime = !this.lastFrameTime diff --git a/src/core/platform.ts b/src/core/platform.ts deleted file mode 100644 index 5f54236f..00000000 --- a/src/core/platform.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2023 Comcast Cable Communications Management, LLC. - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Stage } from './Stage.js'; - -/** - * Platform render loop initiator - */ -export const startLoop = (stage: Stage) => { - let isIdle = false; - const runLoop = () => { - stage.updateFrameTime(); - stage.updateAnimations(); - - if (!stage.hasSceneUpdates()) { - // We still need to calculate the fps else it looks like the app is frozen - stage.calculateFps(); - setTimeout(runLoop, 16.666666666666668); - - if (!isIdle) { - if (stage.txMemManager.checkCleanup()) { - stage.txMemManager.cleanup(); - } - stage.eventBus.emit('idle'); - isIdle = true; - } - stage.flushFrameEvents(); - return; - } - - isIdle = false; - stage.drawFrame(); - stage.flushFrameEvents(); - requestAnimationFrame(runLoop); - }; - requestAnimationFrame(runLoop); -}; - -/** - * Return unix timestamp - * @return {number} - */ -export const getTimeStamp = () => { - return performance ? performance.now() : Date.now(); -}; diff --git a/src/core/platforms/CorePlatform.ts b/src/core/platforms/CorePlatform.ts index 65d6242f..4c2a54c3 100644 --- a/src/core/platforms/CorePlatform.ts +++ b/src/core/platforms/CorePlatform.ts @@ -19,15 +19,9 @@ import { CoreGlContext } from './CoreGlContext.js'; import { Stage } from '../Stage.js'; +import type { ContextSpy } from '../lib/ContextSpy.js'; export abstract class CorePlatform { - /** - * Gets the WebGL context wrapper that provides access to WebGL-specific functions - * such as creating textures, binding buffers, etc. - * @returns {WebGLFunctions} An instance of WebGLFunctions that wraps the WebGL context. - */ - abstract get gl(): CoreGlContext; - /** * Creates a new canvas element. * @returns The created HTMLCanvasElement. @@ -35,38 +29,24 @@ export abstract class CorePlatform { abstract createCanvas(): HTMLCanvasElement; /** - * Creates a WebGL context (either WebGL1 or WebGL2) for a given canvas. - * @param canvas - The canvas element to create the context for. - * @returns The created WebGLRenderingContext or WebGL2RenderingContext. - */ - abstract createWebGLContext( - canvas: HTMLCanvasElement, - ): WebGLRenderingContext | WebGL2RenderingContext; - - /** - * Creates a 2D canvas rendering context. - * @param canvas - The canvas element. - * @returns The 2D rendering context. + * Get a DOM element by ID + * @returns The DOM element (or null) */ - abstract createCanvasRenderingContext2D( - canvas: HTMLCanvasElement, - ): CanvasRenderingContext2D; + abstract getElementById(id: string): HTMLElement | null; /** - * Sets the pixel ratio for the canvas. - * @param pixelRatio - The pixel ratio to set. + * Creates a WebGL context from a given canvas element. + * + * @param {HTMLCanvasElement} canvas - The canvas element to create the WebGL context from. + * @param {boolean} [forceWebGL2=false] - Whether to force the use of WebGL2 if available. + * @param {boolean} [contextSpy=false] - Whether to enable context spying for debugging purposes. + * @returns {WebGLRenderingContext | WebGL2RenderingContext} The created WebGL context. */ - abstract setCanvasPixelRatio(pixelRatio: number): void; - - /** - * Sets the clear color for the canvas. - * @param context - The 2D rendering context. - * @param color - The color to set. - */ - abstract setCanvasClearColor( - context: CanvasRenderingContext2D, - color: string, - ): void; + abstract createWebGLContext( + canvas: HTMLCanvasElement | OffscreenCanvas, + forceWebGL2: boolean, + contextSpy: ContextSpy | null, + ): CoreGlContext; /** * Starts the main rendering loop, calling the provided update function every frame. @@ -75,34 +55,10 @@ export abstract class CorePlatform { abstract startLoop(stage: Stage): void; /** - * Creates an ImageBitmap from an image source with specified cropping coordinates. - * @param image - The source image or blob to create an image bitmap from. - * @param sx - The x-coordinate of the sub-rectangle of the image. - * @param sy - The y-coordinate of the sub-rectangle of the image. - * @param sw - The width of the sub-rectangle of the image. - * @param sh - The height of the sub-rectangle of the image. - * @param options - Image bitmap options like premultiplyAlpha, colorSpaceConversion, etc. - * @returns A promise that resolves to an ImageBitmap. - */ - abstract createImageBitmap( - image: ImageBitmapSource, - sx: number, - sy: number, - sw: number, - sh: number, - options?: ImageBitmapOptions, - ): Promise; - - /** - * Creates an ImageBitmap from an image source without cropping. - * @param image - The source image or blob to create an image bitmap from. - * @param options - Image bitmap options like premultiplyAlpha, colorSpaceConversion, etc. - * @returns A promise that resolves to an ImageBitmap. + * Returns the platform createImageBitmap function + * @returns {createImageBitmap} */ - abstract createImageBitmap( - image: ImageBitmapSource, - options?: ImageBitmapOptions, - ): Promise; + abstract get createImageBitmap(): typeof createImageBitmap; /** * Retrieves the current timestamp. diff --git a/src/core/platforms/web/WebGlContextWrapper.ts b/src/core/platforms/web/WebGlContextWrapper.ts deleted file mode 100644 index 584c45f2..00000000 --- a/src/core/platforms/web/WebGlContextWrapper.ts +++ /dev/null @@ -1,1304 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ - -import { assertTruthy } from '../../../utils.js'; -import { isWebGl2 } from '../../renderers/webgl/internal/WebGlUtils.js'; - -/** - * Optimized WebGL Context Wrapper - * - * @remarks - * This class contains the subset of the WebGLRenderingContext & WebGL2RenderingContext - * API that is used by the renderer. Select high volume WebGL methods include - * caching optimizations to avoid making WebGL calls if the state is already set - * to the desired value. - * - * While most methods contained are direct passthroughs to the WebGL context, - * some methods combine multiple WebGL calls into one for convenience, modify - * arguments to be more convenient, or are replaced by more specific methods. - * - * Not all methods are optimized. Only methods that are called frequently - * and/or have a high cost are optimized. - * - * A subset of GLenum constants are also exposed as properties on this class - * for convenience. - */ -export class WebGlContextWrapper { - //#region Cached WebGL State - private activeTextureUnit = 0; - private texture2dUnits: Array; - private texture2dParams: WeakMap< - WebGLTexture, - Record - > = new WeakMap(); - private scissorEnabled; - private scissorX: number; - private scissorY: number; - private scissorWidth: number; - private scissorHeight: number; - private blendEnabled; - private blendSrcRgb: number; - private blendDstRgb: number; - private blendSrcAlpha: number; - private blendDstAlpha: number; - private boundArrayBuffer: WebGLBuffer | null; - private boundElementArrayBuffer: WebGLBuffer | null; - private curProgram: WebGLProgram | null; - //#endregion Cached WebGL State - - //#region Canvas - public readonly canvas; - //#endregion Canvas - - //#region WebGL Enums - public readonly MAX_RENDERBUFFER_SIZE; - public readonly MAX_TEXTURE_SIZE; - public readonly MAX_VIEWPORT_DIMS; - public readonly MAX_VERTEX_TEXTURE_IMAGE_UNITS; - public readonly MAX_TEXTURE_IMAGE_UNITS; - public readonly MAX_COMBINED_TEXTURE_IMAGE_UNITS; - public readonly MAX_VERTEX_ATTRIBS; - public readonly MAX_VARYING_VECTORS; - public readonly MAX_VERTEX_UNIFORM_VECTORS; - public readonly MAX_FRAGMENT_UNIFORM_VECTORS; - public readonly TEXTURE_MAG_FILTER; - public readonly TEXTURE_MIN_FILTER; - public readonly TEXTURE_WRAP_S; - public readonly TEXTURE_WRAP_T; - public readonly LINEAR; - public readonly CLAMP_TO_EDGE; - public readonly RGBA; - public readonly UNSIGNED_BYTE; - public readonly UNPACK_PREMULTIPLY_ALPHA_WEBGL; - public readonly UNPACK_FLIP_Y_WEBGL; - public readonly FLOAT; - public readonly TRIANGLES; - public readonly UNSIGNED_SHORT; - public readonly ONE; - public readonly ONE_MINUS_SRC_ALPHA; - public readonly VERTEX_SHADER; - public readonly FRAGMENT_SHADER; - public readonly STATIC_DRAW; - public readonly COMPILE_STATUS; - public readonly LINK_STATUS; - public readonly DYNAMIC_DRAW; - public readonly COLOR_ATTACHMENT0; - //#endregion WebGL Enums - - constructor(private gl: WebGLRenderingContext | WebGL2RenderingContext) { - // The following code extracts the current state of the WebGL context - // to our local JavaScript cached version of it. This is so we can - // avoid making WebGL calls if we don't need to. - // We could assume that the WebGL context is in a default state, but - // in the future we may want to support restoring a broken WebGL context - // and this will help with that. - this.activeTextureUnit = - (gl.getParameter(gl.ACTIVE_TEXTURE) as number) - gl.TEXTURE0; - const maxTextureUnits = gl.getParameter( - gl.MAX_TEXTURE_IMAGE_UNITS, - ) as number; - // save current texture units - this.texture2dUnits = new Array(maxTextureUnits) - .fill(undefined) - .map((_, i) => { - this.activeTexture(i); - return gl.getParameter(gl.TEXTURE_BINDING_2D) as WebGLTexture; - }); - // restore active texture unit - this.activeTexture(this.activeTextureUnit); - this.scissorEnabled = gl.isEnabled(gl.SCISSOR_TEST); - - const scissorBox = gl.getParameter(gl.SCISSOR_BOX) as [ - number, - number, - number, - number, - ]; - this.scissorX = scissorBox[0]; - this.scissorY = scissorBox[1]; - this.scissorWidth = scissorBox[2]; - this.scissorHeight = scissorBox[3]; - - this.blendEnabled = gl.isEnabled(gl.BLEND); - this.blendSrcRgb = gl.getParameter(gl.BLEND_SRC_RGB) as number; - this.blendDstRgb = gl.getParameter(gl.BLEND_DST_RGB) as number; - this.blendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA) as number; - this.blendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA) as number; - - this.boundArrayBuffer = gl.getParameter( - gl.ARRAY_BUFFER_BINDING, - ) as WebGLBuffer; - this.boundElementArrayBuffer = gl.getParameter( - gl.ELEMENT_ARRAY_BUFFER_BINDING, - ) as WebGLBuffer; - - this.curProgram = gl.getParameter( - gl.CURRENT_PROGRAM, - ) as WebGLProgram | null; - - this.canvas = gl.canvas; - - // Extract GLenums - this.MAX_RENDERBUFFER_SIZE = gl.MAX_RENDERBUFFER_SIZE; - this.MAX_TEXTURE_SIZE = gl.MAX_TEXTURE_SIZE; - this.MAX_VIEWPORT_DIMS = gl.MAX_VIEWPORT_DIMS; - this.MAX_VERTEX_TEXTURE_IMAGE_UNITS = gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS; - this.MAX_TEXTURE_IMAGE_UNITS = gl.MAX_TEXTURE_IMAGE_UNITS; - this.MAX_COMBINED_TEXTURE_IMAGE_UNITS = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS; - this.MAX_VERTEX_ATTRIBS = gl.MAX_VERTEX_ATTRIBS; - this.MAX_VARYING_VECTORS = gl.MAX_VARYING_VECTORS; - this.MAX_VERTEX_UNIFORM_VECTORS = gl.MAX_VERTEX_UNIFORM_VECTORS; - this.MAX_FRAGMENT_UNIFORM_VECTORS = gl.MAX_FRAGMENT_UNIFORM_VECTORS; - this.TEXTURE_MAG_FILTER = gl.TEXTURE_MAG_FILTER; - this.TEXTURE_MIN_FILTER = gl.TEXTURE_MIN_FILTER; - this.TEXTURE_WRAP_S = gl.TEXTURE_WRAP_S; - this.TEXTURE_WRAP_T = gl.TEXTURE_WRAP_T; - this.LINEAR = gl.LINEAR; - this.CLAMP_TO_EDGE = gl.CLAMP_TO_EDGE; - this.RGBA = gl.RGBA; - this.UNSIGNED_BYTE = gl.UNSIGNED_BYTE; - this.UNPACK_PREMULTIPLY_ALPHA_WEBGL = gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL; - this.UNPACK_FLIP_Y_WEBGL = gl.UNPACK_FLIP_Y_WEBGL; - this.FLOAT = gl.FLOAT; - this.TRIANGLES = gl.TRIANGLES; - this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT; - this.ONE = gl.ONE; - this.ONE_MINUS_SRC_ALPHA = gl.ONE_MINUS_SRC_ALPHA; - this.MAX_VERTEX_TEXTURE_IMAGE_UNITS = gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS; - this.TRIANGLES = gl.TRIANGLES; - this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT; - this.VERTEX_SHADER = gl.VERTEX_SHADER; - this.FRAGMENT_SHADER = gl.FRAGMENT_SHADER; - this.STATIC_DRAW = gl.STATIC_DRAW; - this.COMPILE_STATUS = gl.COMPILE_STATUS; - this.LINK_STATUS = gl.LINK_STATUS; - this.DYNAMIC_DRAW = gl.DYNAMIC_DRAW; - this.COLOR_ATTACHMENT0 = gl.COLOR_ATTACHMENT0; - } - /** - * Returns true if the WebGL context is WebGL2 - * - * @returns - */ - isWebGl2() { - return isWebGl2(this.gl); - } - - /** - * ``` - * gl.activeTexture(textureUnit + gl.TEXTURE0); - * ``` - * - * @remarks - * **WebGL Difference**: `textureUnit` is based from 0, not `gl.TEXTURE0`. - * - * @param textureUnit - */ - activeTexture(textureUnit: number) { - const { gl } = this; - if (this.activeTextureUnit !== textureUnit) { - gl.activeTexture(textureUnit + gl.TEXTURE0); - this.activeTextureUnit = textureUnit; - } - } - - /** - * ``` - * gl.bindTexture(gl.TEXTURE_2D, texture); - * ``` - * @remarks - * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` - * - * @param texture - */ - bindTexture(texture: WebGLTexture | null) { - const { gl, activeTextureUnit, texture2dUnits } = this; - - if (texture2dUnits[activeTextureUnit] === texture) { - return; - } - texture2dUnits[activeTextureUnit] = texture; - - gl.bindTexture(this.gl.TEXTURE_2D, texture); - } - - private _getActiveTexture(): WebGLTexture | null { - const { activeTextureUnit, texture2dUnits } = this; - return texture2dUnits[activeTextureUnit]!; - } - - /** - * ``` - * gl.texParameteri(gl.TEXTURE_2D, pname, param); - * ``` - * @remarks - * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` - * - * @param pname - * @param param - * @returns - */ - texParameteri(pname: number, param: number) { - const { gl, texture2dParams } = this; - - const activeTexture = this._getActiveTexture(); - if (!activeTexture) { - throw new Error('No active texture'); - } - let textureParams = texture2dParams.get(activeTexture); - if (!textureParams) { - textureParams = {}; - texture2dParams.set(activeTexture, textureParams); - } - if (textureParams[pname] === param) { - return; - } - textureParams[pname] = param; - gl.texParameteri(gl.TEXTURE_2D, pname, param); - } - - /** - * ``` - * gl.texImage2D( - * gl.TEXTURE_2D, - * level, - * internalFormat, - * width, - * height, - * border, - * format, - * type, - * pixels, - * ); - * ``` - * @remarks - * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` - * - * @param level - * @param internalFormat - * @param width - * @param height - * @param border - * @param format - * @param type - * @param pixels - */ - texImage2D( - level: GLint, - internalformat: GLint, - width: GLsizei, - height: GLsizei, - border: GLint, - format: GLenum, - type: GLenum, - pixels: ArrayBufferView | null, - ): void; - texImage2D( - level: GLint, - internalformat: GLint, - format: GLenum, - type: GLenum, - source: TexImageSource, - ): void; - texImage2D( - level: any, - internalFormat: any, - widthOrFormat: any, - heightOrType: any, - borderOrSource: any, - format?: any, - type?: any, - pixels?: any, - ) { - const { gl } = this; - if (format) { - gl.texImage2D( - gl.TEXTURE_2D, - level, - internalFormat, - widthOrFormat, - heightOrType, - borderOrSource, - format, - type, - pixels, - ); - } else { - gl.texImage2D( - gl.TEXTURE_2D, - level, - internalFormat, - widthOrFormat, - heightOrType, - borderOrSource, - ); - } - } - /** - * ``` - * gl.compressedTexImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, data); - * ``` - * - * @remarks - * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` - */ - - compressedTexImage2D( - level: GLint, - internalformat: GLenum, - width: GLsizei, - height: GLsizei, - border: GLint, - data?: ArrayBufferView, - ): void { - const { gl } = this; - gl.compressedTexImage2D( - gl.TEXTURE_2D, - level, - internalformat, - width, - height, - border, - data as ArrayBufferView, - ); - } - /** - * ``` - * gl.pixelStorei(pname, param); - * ``` - * - * @param pname - * @param param - */ - pixelStorei(pname: GLenum, param: GLint | GLboolean) { - const { gl } = this; - 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(); - * ``` - * - * @returns - */ - createTexture() { - const { gl } = this; - return gl.createTexture(); - } - - /** - * ``` - * gl.deleteTexture(texture); - * ``` - * - * @param texture - */ - deleteTexture(texture: WebGLTexture | null) { - const { gl } = this; - if (texture) { - this.texture2dParams.delete(texture); - } - gl.deleteTexture(texture); - } - - /** - * ``` - * gl.viewport(x, y, width, height); - * ``` - */ - viewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei) { - const { gl } = this; - gl.viewport(x, y, width, height); - } - - /** - * ``` - * gl.clearColor(red, green, blue, alpha); - * ``` - * - * @param red - * @param green - * @param blue - * @param alpha - */ - clearColor(red: GLclampf, green: GLclampf, blue: GLclampf, alpha: GLclampf) { - const { gl } = this; - gl.clearColor(red, green, blue, alpha); - } - - /** - * ``` - * gl["enable"|"disable"](gl.SCISSOR_TEST); - * ``` - * @param enable - */ - setScissorTest(enable: boolean) { - const { gl, scissorEnabled } = this; - if (enable === scissorEnabled) { - return; - } - if (enable) { - gl.enable(gl.SCISSOR_TEST); - } else { - gl.disable(gl.SCISSOR_TEST); - } - this.scissorEnabled = enable; - } - - /** - * ``` - * gl.scissor(x, y, width, height); - * ``` - * - * @param x - * @param y - * @param width - * @param height - */ - scissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei) { - const { gl, scissorX, scissorY, scissorWidth, scissorHeight } = this; - if ( - x !== scissorX || - y !== scissorY || - width !== scissorWidth || - height !== scissorHeight - ) { - gl.scissor(x, y, width, height); - this.scissorX = x; - this.scissorY = y; - this.scissorWidth = width; - this.scissorHeight = height; - } - } - - /** - * ``` - * gl["enable"|"disable"](gl.BLEND); - * ``` - * - * @param blend - * @returns - */ - setBlend(blend: boolean) { - const { gl, blendEnabled } = this; - if (blend === blendEnabled) { - return; - } - if (blend) { - gl.enable(gl.BLEND); - } else { - gl.disable(gl.BLEND); - } - this.blendEnabled = blend; - } - - /** - * ``` - * gl.blendFunc(src, dst); - * ``` - * - * @param src - * @param dst - */ - blendFunc(src: GLenum, dst: GLenum) { - const { gl, blendSrcRgb, blendDstRgb, blendSrcAlpha, blendDstAlpha } = this; - if ( - src !== blendSrcRgb || - dst !== blendDstRgb || - src !== blendSrcAlpha || - dst !== blendDstAlpha - ) { - gl.blendFunc(src, dst); - this.blendSrcRgb = src; - this.blendDstRgb = dst; - this.blendSrcAlpha = src; - this.blendDstAlpha = dst; - } - } - - /** - * ``` - * gl.createBuffer(); - * ``` - * - * @returns - */ - createBuffer() { - const { gl } = this; - return gl.createBuffer(); - } - - /** - * ``` - * gl.createFramebuffer(); - * ``` - * @returns - */ - createFramebuffer() { - const { gl } = this; - return gl.createFramebuffer(); - } - - /** - * ``` - * gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); - * ``` - * - * @param framebuffer - */ - bindFramebuffer(framebuffer: WebGLFramebuffer | null) { - const { gl } = this; - gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); - } - - /** - * ``` - * gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); - * ``` - * @remarks - * **WebGL Difference**: Bind target is always `gl.FRAMEBUFFER` and textarget is always `gl.TEXTURE_2D` - */ - - framebufferTexture2D( - attachment: GLenum, - texture: WebGLTexture | null, - level: GLint, - ) { - const { gl } = this; - gl.framebufferTexture2D( - gl.FRAMEBUFFER, - attachment, - gl.TEXTURE_2D, - texture, - level, - ); - } - - /** - * ``` - * gl.clear(gl.COLOR_BUFFER_BIT); - * ``` - * - * @remarks - * **WebGL Difference**: Clear mask is always `gl.COLOR_BUFFER_BIT` - */ - clear() { - const { gl } = this; - gl.clear(gl.COLOR_BUFFER_BIT); - } - - /** - * ``` - * gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - * gl.bufferData(gl.ARRAY_BUFFER, data, usage); - * ``` - * - * @remarks - * **WebGL Combo**: `gl.bindBuffer` and `gl.bufferData` are combined into one function. - * - * @param buffer - * @param data - * @param usage - */ - arrayBufferData( - buffer: WebGLBuffer | null, - data: ArrayBufferView, - usage: GLenum, - ) { - const { gl, boundArrayBuffer } = this; - if (boundArrayBuffer !== buffer) { - gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - this.boundArrayBuffer = buffer; - } - gl.bufferData(gl.ARRAY_BUFFER, data, usage); - } - - /** - * ``` - * gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); - * gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, usage); - * ``` - * @remarks - * **WebGL Combo**: `gl.bindBuffer` and `gl.bufferData` are combined into one function. - * - * @param buffer - * @param data - * @param usage - */ - elementArrayBufferData( - buffer: WebGLBuffer | null, - data: ArrayBufferView, - usage: GLenum, - ) { - const { gl, boundElementArrayBuffer } = this; - if (boundElementArrayBuffer !== buffer) { - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); - this.boundElementArrayBuffer = buffer; - } - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, usage); - } - - /** - * ``` - * gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - * gl.vertexAttribPointer(index, size, type, normalized, stride, offset); - * ``` - * - * @remarks - * **WebGL Combo**: `gl.bindBuffer` and `gl.vertexAttribPointer` are combined into one function. - * - * @param buffer - * @param index - * @param size - * @param type - * @param normalized - * @param stride - * @param offset - */ - vertexAttribPointer( - buffer: WebGLBuffer, - index: GLuint, - size: GLint, - type: GLenum, - normalized: GLboolean, - stride: GLsizei, - offset: GLintptr, - ) { - const { gl, boundArrayBuffer } = this; - if (boundArrayBuffer !== buffer) { - gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - this.boundArrayBuffer = buffer; - } - gl.vertexAttribPointer(index, size, type, normalized, stride, offset); - } - - /** - * ``` - * gl.useProgram(program); - * ``` - * - * @param program - * @returns - */ - useProgram(program: WebGLProgram | null) { - const { gl, curProgram } = this; - if (curProgram === program) { - return; - } - gl.useProgram(program); - this.curProgram = program; - } - - /** - * Sets the value of a single float uniform variable. - * - * @param location - The location of the uniform variable. - * @param v0 - The value to set. - */ - uniform1f(location: WebGLUniformLocation | null, v0: number) { - const { gl } = this; - gl.uniform1f(location, v0); - } - - /** - * Sets the value of a float array uniform variable. - * - * @param location - The location of the uniform variable. - * @param value - The array of values to set. - */ - uniform1fv( - location: WebGLUniformLocation | null, - value: Float32Array | number[], - ) { - const { gl } = this; - gl.uniform1fv(location, value); - } - - /** - * Sets the value of a single integer uniform variable. - * - * @param location - The location of the uniform variable. - * @param v0 - The value to set. - */ - uniform1i(location: WebGLUniformLocation | null, v0: number) { - const { gl } = this; - gl.uniform1i(location, v0); - } - - /** - * Sets the value of an integer array uniform variable. - * - * @param location - The location of the uniform variable. - * @param value - The array of values to set. - */ - uniform1iv( - location: WebGLUniformLocation | null, - value: Int32Array | number[], - ) { - const { gl } = this; - gl.uniform1iv(location, value); - } - - /** - * Sets the value of a vec2 uniform variable. - * - * @param location - The location of the uniform variable. - * @param v0 - The first component of the vector. - * @param v1 - The second component of the vector. - */ - uniform2f(location: WebGLUniformLocation | null, v0: number, v1: number) { - const { gl } = this; - gl.uniform2f(location, v0, v1); - } - - /** - * Sets the value of a vec2 array uniform variable. - * - * @param location - The location of the uniform variable. - * @param value - The array of vec2 values to set. - */ - uniform2fv( - location: WebGLUniformLocation | null, - value: Float32Array | number[], - ) { - const { gl } = this; - gl.uniform2fv(location, value); - } - - /** - * Sets the value of a ivec2 uniform variable. - * - * @param location - The location of the uniform variable. - * @param v0 - The first component of the vector. - * @param v1 - The second component of the vector. - */ - uniform2i(location: WebGLUniformLocation | null, v0: number, v1: number) { - const { gl } = this; - gl.uniform2i(location, v0, v1); - } - - /** - * Sets the value of an ivec2 array uniform variable. - * - * @param location - The location of the uniform variable. - * @param value - The array of ivec2 values to set. - */ - uniform2iv( - location: WebGLUniformLocation | null, - value: Int32Array | number[], - ) { - const { gl } = this; - gl.uniform2iv(location, value); - } - - /** - * Sets the value of a vec3 uniform variable. - * - * @param location - The location of the uniform variable. - * @param v0 - The first component of the vector. - * @param v1 - The second component of the vector. - * @param v2 - The third component of the vector. - */ - uniform3f( - location: WebGLUniformLocation | null, - v0: number, - v1: number, - v2: number, - ) { - const { gl } = this; - gl.uniform3f(location, v0, v1, v2); - } - - /** - * Sets the value of a vec3 array uniform variable. - * - * @param location - The location of the uniform variable. - * @param value - The array of vec3 values to set. - */ - uniform3fv( - location: WebGLUniformLocation | null, - value: Float32Array | number[], - ) { - const { gl } = this; - gl.uniform3fv(location, value); - } - - /** - * Sets the value of a ivec3 uniform variable. - * - * @param location - The location of the uniform variable. - * @param v0 - The first component of the vector. - * @param v1 - The second component of the vector. - * @param v2 - The third component of the vector. - */ - uniform3i( - location: WebGLUniformLocation | null, - v0: number, - v1: number, - v2: number, - ) { - const { gl } = this; - gl.uniform3i(location, v0, v1, v2); - } - - /** - * Sets the value of an ivec3 array uniform variable. - * - * @param location - The location of the uniform variable. - * @param value - The array of ivec3 values to set. - */ - uniform3iv( - location: WebGLUniformLocation | null, - value: Int32Array | number[], - ) { - const { gl } = this; - gl.uniform3iv(location, value); - } - - /** - * Sets the value of a vec4 uniform variable. - * - * @param location - The location of the uniform variable. - * @param v0 - The first component of the vector. - * @param v1 - The second component of the vector. - * @param v2 - The third component of the vector. - * @param v3 - The fourth component of the vector. - */ - uniform4f( - location: WebGLUniformLocation | null, - v0: number, - v1: number, - v2: number, - v3: number, - ) { - const { gl } = this; - gl.uniform4f(location, v0, v1, v2, v3); - } - - /** - * Sets the value of a vec4 array uniform variable. - * - * @param location - The location of the uniform variable. - * @param value - The array of vec4 values to set. - */ - uniform4fv( - location: WebGLUniformLocation | null, - value: Float32Array | number[], - ) { - const { gl } = this; - gl.uniform4fv(location, value); - } - - /** - * Sets the value of a ivec4 uniform variable. - * - * @param location - The location of the uniform variable. - * @param v0 - The first component of the vector. - * @param v1 - The second component of the vector. - * @param v2 - The third component of the vector. - * @param v3 - The fourth component of the vector. - */ - uniform4i( - location: WebGLUniformLocation | null, - v0: number, - v1: number, - v2: number, - v3: number, - ) { - const { gl } = this; - gl.uniform4i(location, v0, v1, v2, v3); - } - - /** - * Sets the value of an ivec4 array uniform variable. - * - * @param location - The location of the uniform variable. - * @param value - The array of ivec4 values to set. - */ - uniform4iv( - location: WebGLUniformLocation | null, - value: Int32Array | number[], - ) { - const { gl } = this; - gl.uniform4iv(location, value); - } - - /** - * Sets the value of a mat2 uniform variable. - * - * @param location - The location of the uniform variable. - * @param transpose - Whether to transpose the matrix. - * @param value - The array of mat2 values to set. - */ - uniformMatrix2fv( - location: WebGLUniformLocation | null, - value: Float32Array | number[], - ) { - const { gl } = this; - gl.uniformMatrix2fv(location, false, value); - } - - /** - * Sets the value of a mat2 uniform variable. - * @param location - The location of the uniform variable. - * @param value - The array of mat2 values to set. - */ - uniformMatrix3fv( - location: WebGLUniformLocation | null, - value: Float32Array | number[], - ) { - const { gl } = this; - gl.uniformMatrix3fv(location, false, value); - } - - /** - * Sets the value of a mat4 uniform variable. - * @param location - The location of the uniform variable. - * @param value - The array of mat4 values to set. - */ - uniformMatrix4fv( - location: WebGLUniformLocation | null, - value: Float32Array | number[], - ) { - const { gl } = this; - gl.uniformMatrix4fv(location, false, value); - } - - /** - * ``` - * gl.getParameter(pname); - * ``` - * - * @param pname - * @returns - */ - getParameter(pname: GLenum): any { - const { gl } = this; - return gl.getParameter(pname); - } - - /** - * ``` - * gl.drawElements(mode, count, type, offset); - * ``` - * - * @param mode - * @param count - * @param type - * @param offset - */ - drawElements(mode: GLenum, count: GLsizei, type: GLenum, offset: GLintptr) { - const { gl } = this; - gl.drawElements(mode, count, type, offset); - } - - /** - * ``` - * gl.drawArrays(mode, first, count); - * ``` - * - * @param name - * @returns - */ - getExtension(name: string) { - const { gl } = this; - return gl.getExtension(name); - } - - /** - * ``` - * gl.createVertexArray(); - * ``` - * - * @returns - */ - createVertexArray() { - const { gl } = this; - assertTruthy(gl instanceof WebGL2RenderingContext); - return gl.createVertexArray(); - } - - /** - * ``` - * gl.bindVertexArray(vertexArray); - * ``` - * - * @param vertexArray - */ - bindVertexArray(vertexArray: WebGLVertexArrayObject | null) { - const { gl } = this; - assertTruthy(gl instanceof WebGL2RenderingContext); - gl.bindVertexArray(vertexArray); - } - - /** - * ``` - * gl.getAttribLocation(program, name); - * ``` - * - * @param program - * @param name - * @returns - */ - getAttribLocation(program: WebGLProgram, name: string) { - const { gl } = this; - return gl.getAttribLocation(program, name); - } - - /** - * ``` - * gl.getUniformLocation(program, name); - * ``` - * - * @param program - * @param name - * @returns - */ - getUniformLocation(program: WebGLProgram, name: string) { - const { gl } = this; - return gl.getUniformLocation(program, name); - } - - /** - * ``` - * gl.enableVertexAttribArray(index); - * ``` - * - * @param index - */ - enableVertexAttribArray(index: number) { - const { gl } = this; - gl.enableVertexAttribArray(index); - } - - /** - * ``` - * gl.disableVertexAttribArray(index); - * ``` - * - * @param index - */ - disableVertexAttribArray(index: number) { - const { gl } = this; - gl.disableVertexAttribArray(index); - } - - /** - * ``` - * gl.createShader(type); - * ``` - * - * @param type - * @returns - */ - createShader(type: number) { - const { gl } = this; - return gl.createShader(type); - } - - /** - * ``` - * gl.compileShader(shader); - * ``` - * - * @param shader - * @returns - */ - compileShader(shader: WebGLShader) { - const { gl } = this; - gl.compileShader(shader); - } - - /** - * ``` - * gl.attachShader(program, shader); - * ``` - * - * @param program - * @param shader - */ - attachShader(program: WebGLProgram, shader: WebGLShader) { - const { gl } = this; - gl.attachShader(program, shader); - } - - /** - * ``` - * gl.linkProgram(program); - * ``` - * - * @param program - */ - linkProgram(program: WebGLProgram) { - const { gl } = this; - gl.linkProgram(program); - } - - /** - * ``` - * gl.deleteProgram(shader); - * ``` - * - * @param shader - */ - deleteProgram(shader: WebGLProgram) { - const { gl } = this; - gl.deleteProgram(shader); - } - - /** - * ``` - * gl.getShaderParameter(shader, pname); - * ``` - * - * @param shader - * @param pname - */ - getShaderParameter(shader: WebGLShader, pname: GLenum) { - const { gl } = this; - return gl.getShaderParameter(shader, pname); - } - - /** - * ``` - * gl.getShaderInfoLog(shader); - * ``` - * - * @param shader - */ - getShaderInfoLog(shader: WebGLShader) { - const { gl } = this; - return gl.getShaderInfoLog(shader); - } - - /** - * ``` - * gl.createProgram(); - * ``` - * - * @returns - */ - createProgram() { - const { gl } = this; - return gl.createProgram(); - } - - /** - * ``` - * gl.getProgramParameter(program, pname); - * ``` - * - * @param program - * @param pname - * @returns - */ - getProgramParameter(program: WebGLProgram, pname: GLenum) { - const { gl } = this; - return gl.getProgramParameter(program, pname); - } - - /** - * ``` - * gl.getProgramInfoLog(program); - * ``` - * - * @param program - * @returns - */ - getProgramInfoLog(program: WebGLProgram) { - const { gl } = this; - return gl.getProgramInfoLog(program); - } - - /** - * ``` - * gl.shaderSource(shader, source); - * ``` - * - * @param shader - * @param source - */ - shaderSource(shader: WebGLShader, source: string) { - const { gl } = this; - gl.shaderSource(shader, source); - } - - /** - * ``` - * gl.deleteShader(shader); - * ``` - * - * @param shader - */ - deleteShader(shader: WebGLShader) { - const { gl } = this; - gl.deleteShader(shader); - } -} - -// prettier-ignore -type IsUniformMethod = MethodName extends `uniform${string}` - ? // eslint-disable-next-line @typescript-eslint/no-explicit-any - MethodType extends (location: WebGLUniformLocation | null, ...args: any[]) => void - ? true - : false - : false; - -// prettier-ignore -export type UniformMethodMap = { - [Key in keyof WebGLRenderingContext as IsUniformMethod extends true ? Key : never]: WebGLRenderingContext[Key] extends ( - location: WebGLUniformLocation | null, - ...args: infer T - ) => void - ? T - : never; -}; - -/** - * Compare two arrays for equality. - * - * @remarks - * This function will not try to compare nested arrays or Float32Arrays and - * instead will always return false when they are encountered. - * - * @param a - * @param b - * @returns - */ -export function compareArrays(a: T[], b: T[]): boolean { - if (a.length !== b.length) { - return false; - } - - let result = false; - for (let i = 0; i < a.length; i++) { - if (Array.isArray(a[i]) || a[i] instanceof Float32Array) { - result = false; - break; - } - - if (a[i] !== b[i]) { - result = false; - break; - } - - result = true; - } - - return result; -} diff --git a/src/core/platforms/web/WebPlatform.ts b/src/core/platforms/web/WebPlatform.ts index af1d22f2..9acf5be9 100644 --- a/src/core/platforms/web/WebPlatform.ts +++ b/src/core/platforms/web/WebPlatform.ts @@ -1,22 +1,11 @@ import { CorePlatform } from '../CorePlatform.js'; import { WebGlContext } from './WebGlContext.js'; -import { Stage } from '../../Stage.js'; +import type { Stage } from '../../Stage.js'; +import { ContextSpy } from '../../lib/ContextSpy.js'; export class WebPlatform extends CorePlatform { - private glContextWrapper!: WebGlContext; - constructor() { super(); - const canvas = this.createCanvas(); - this.glContextWrapper = new WebGlContext(this.createWebGLContext(canvas)); - } - - //////////////////////// - // WebGL Wrapper - //////////////////////// - - override get gl() { - return this.glContextWrapper; } //////////////////////// @@ -28,37 +17,54 @@ export class WebPlatform extends CorePlatform { return canvas; } + override getElementById(id: string): HTMLElement | null { + return document.getElementById(id); + } + override createWebGLContext( - canvas: HTMLCanvasElement, - ): WebGLRenderingContext | WebGL2RenderingContext { - const gl = canvas.getContext('webgl2') || canvas.getContext('webgl'); + canvas: HTMLCanvasElement | OffscreenCanvas, + forceWebGL2 = false, + contextSpy: ContextSpy | null, + ): WebGlContext { + const config: WebGLContextAttributes = { + alpha: true, + antialias: false, + depth: false, + stencil: true, + desynchronized: false, + // Disabled because it prevents Visual Regression Tests from working + // failIfMajorPerformanceCaveat: true, + powerPreference: 'high-performance', + premultipliedAlpha: true, + preserveDrawingBuffer: false, + }; + const gl = + // TODO: Remove this assertion once this issue is fixed in TypeScript + // https://github.com/microsoft/TypeScript/issues/53614 + (canvas.getContext(forceWebGL2 ? 'webgl2' : 'webgl', config) || + canvas.getContext( + 'experimental-webgl' as 'webgl', + config, + )) as unknown as WebGLRenderingContext | null; if (!gl) { - throw new Error('WebGL not supported'); + throw new Error('Unable to create WebGL context'); } - return gl; - } - - override createCanvasRenderingContext2D( - canvas: HTMLCanvasElement, - ): CanvasRenderingContext2D { - const context = canvas.getContext('2d'); - if (!context) { - throw new Error('2D rendering context not supported'); + if (contextSpy) { + // Proxy the GL context to log all GL calls + return new Proxy(new WebGlContext(gl), { + get(target, prop) { + const value = target[prop as never] as unknown; + if (typeof value === 'function') { + contextSpy.increment(String(prop)); + return value.bind(target); + } + return value; + }, + }); } - return context; - } - - override setCanvasPixelRatio(pixelRatio: number): void { - const devicePixelRatio = window.devicePixelRatio || 1; - document.body.style.zoom = (pixelRatio * devicePixelRatio).toString(); - } - override setCanvasClearColor( - context: CanvasRenderingContext2D, - color: string, - ): void { - context.fillStyle = color; - context.fillRect(0, 0, context.canvas.width, context.canvas.height); + // return WebGL Context Wrapper + return new WebGlContext(gl); } //////////////////////// @@ -95,29 +101,8 @@ export class WebPlatform extends CorePlatform { requestAnimationFrame(runLoop); } - /** - * Implementation that supports both overloaded signatures for creating an ImageBitmap. - */ - createImageBitmap( - image: ImageBitmapSource, - sxOrOptions?: number | ImageBitmapOptions, - sy?: number, - sw?: number, - sh?: number, - options?: ImageBitmapOptions, - ): Promise { - // Check if the second argument is a number, meaning it's using the cropping version - if ( - typeof sxOrOptions === 'number' && - sy !== undefined && - sw !== undefined && - sh !== undefined - ) { - return window.createImageBitmap(image, sxOrOptions, sy, sw, sh, options); - } else { - // Otherwise, assume it's using the non-cropping version - return window.createImageBitmap(image, sxOrOptions as ImageBitmapOptions); - } + get createImageBitmap() { + return self.createImageBitmap; } getTimeStamp(): number { diff --git a/src/core/renderers/CoreRenderer.ts b/src/core/renderers/CoreRenderer.ts index 84691ebf..f8114968 100644 --- a/src/core/renderers/CoreRenderer.ts +++ b/src/core/renderers/CoreRenderer.ts @@ -30,6 +30,8 @@ import type { TextureMemoryManager } from '../TextureMemoryManager.js'; import type { ContextSpy } from '../lib/ContextSpy.js'; import type { RenderCoords } from '../lib/RenderCoords.js'; import type { RectWithValid } from '../lib/utils.js'; +import type { CorePlatform } from '../platforms/CorePlatform.js'; +import type { WebPlatform } from '../platforms/web/WebPlatform.js'; import type { Texture } from '../textures/Texture.js'; import { CoreContextTexture } from './CoreContextTexture.js'; import type { CoreShader } from './CoreShader.js'; @@ -71,6 +73,7 @@ export interface CoreRendererOptions { bufferMemory: number; contextSpy: ContextSpy | null; forceWebGL2: boolean; + platform: WebPlatform; } export interface BufferInfo { diff --git a/src/core/renderers/webgl/WebGlCoreRenderer.ts b/src/core/renderers/webgl/WebGlCoreRenderer.ts index 185b7ba8..a968f552 100644 --- a/src/core/renderers/webgl/WebGlCoreRenderer.ts +++ b/src/core/renderers/webgl/WebGlCoreRenderer.ts @@ -47,12 +47,12 @@ import { } from '../../lib/utils.js'; import type { Dimensions } from '../../../common/CommonTypes.js'; import { WebGlCoreShader } from './WebGlCoreShader.js'; -import { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import { RenderTexture } from '../../textures/RenderTexture.js'; import type { CoreNode } from '../../CoreNode.js'; import { WebGlCoreCtxRenderTexture } from './WebGlCoreCtxRenderTexture.js'; import type { BaseShaderController } from '../../../main-api/ShaderController.js'; -import { ImageTexture } from '../../textures/ImageTexture.js'; +import type { CoreGlContext } from '../../platforms/CoreGlContext.js'; +import type { WebGlContext } from '../../platforms/web/WebGlContext.js'; const WORDS_PER_QUAD = 24; // const BYTES_PER_QUAD = WORDS_PER_QUAD * 4; @@ -66,7 +66,7 @@ interface CoreWebGlSystem { export class WebGlCoreRenderer extends CoreRenderer { //// WebGL Native Context and Data - glw: WebGlContextWrapper; + glw: WebGlContext; system: CoreWebGlSystem; //// Persistent data @@ -106,7 +106,7 @@ export class WebGlCoreRenderer extends CoreRenderer { this.mode = 'webgl'; - const { canvas, clearColor, bufferMemory } = options; + const { canvas, clearColor, bufferMemory, platform } = options; this.defaultTexture = new ColorTexture(this.txManager); @@ -121,12 +121,12 @@ export class WebGlCoreRenderer extends CoreRenderer { this.stage.requestRender(); }); - const gl = createWebGLContext( + const glw = platform.createWebGLContext( canvas, options.forceWebGL2, options.contextSpy, ); - const glw = (this.glw = new WebGlContextWrapper(gl)); + this.glw = glw; const color = getNormalizedRgbaComponents(clearColor); glw.viewport(0, 0, canvas.width, canvas.height); diff --git a/src/main-api/Renderer.ts b/src/main-api/Renderer.ts index c5c8636b..c00e10aa 100644 --- a/src/main-api/Renderer.ts +++ b/src/main-api/Renderer.ts @@ -44,6 +44,8 @@ import type { SdfTextRenderer } from '../core/text-rendering/renderers/SdfTextRe import type { WebGlCoreRenderer } from '../core/renderers/webgl/WebGlCoreRenderer.js'; import type { CanvasCoreRenderer } from '../core/renderers/canvas/CanvasCoreRenderer.js'; import type { Inspector } from './Inspector.js'; +import { WebPlatform } from '../core/platforms/web/WebPlatform.js'; +import { CorePlatform } from '../core/platforms/CorePlatform.js'; /** * An immutable reference to a specific Shader type @@ -256,6 +258,18 @@ export interface RendererMainSettings { * @defaultValue `false` */ forceWebGL2?: boolean; + + /** + * Provide an alternative platform abstraction layer + * + * @remarks + * By default the Lightning 3 renderer will load a webplatform, assuming it runs + * inside a web browsr. However for special cases there might be a need to provide + * an abstracted platform layer to run on non-web or non-standard JS engines + * + * @defaultValue `null` + */ + platform?: typeof CorePlatform | null; } /** @@ -346,6 +360,7 @@ export class RendererMain extends EventEmitter { renderEngine: settings.renderEngine, quadBufferSize: settings.quadBufferSize ?? 4 * 1024 * 1024, fontEngines: settings.fontEngines, + platform: settings.platform || null, }; this.settings = resolvedSettings; @@ -357,10 +372,20 @@ export class RendererMain extends EventEmitter { inspector, } = resolvedSettings; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const platform = (new settings.platform() || + new WebPlatform()) as CorePlatform; + assertTruthy( + platform instanceof CorePlatform, + 'Platform is not a core platform immplementation', + ); + const deviceLogicalWidth = appWidth * deviceLogicalPixelRatio; const deviceLogicalHeight = appHeight * deviceLogicalPixelRatio; - const canvas = document.createElement('canvas'); + const canvas = platform.createCanvas(); this.canvas = canvas; canvas.width = deviceLogicalWidth * devicePhysicalPixelRatio; canvas.height = deviceLogicalHeight * devicePhysicalPixelRatio; @@ -387,6 +412,7 @@ export class RendererMain extends EventEmitter { quadBufferSize: this.settings.quadBufferSize, fontEngines: this.settings.fontEngines, inspector: this.settings.inspector !== null, + platform: platform, }); // Extract the root node @@ -397,7 +423,7 @@ export class RendererMain extends EventEmitter { // Get the target element and attach the canvas to it let targetEl: HTMLElement | null; if (typeof target === 'string') { - targetEl = document.getElementById(target); + targetEl = platform.getElementById(target); } else { targetEl = target; } diff --git a/src/utils.ts b/src/utils.ts index a802b4de..e51d16f7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -17,53 +17,6 @@ * limitations under the License. */ -import type { ContextSpy } from './core/lib/ContextSpy.js'; - -export function createWebGLContext( - canvas: HTMLCanvasElement | OffscreenCanvas, - forceWebGL2 = false, - contextSpy: ContextSpy | null, -): WebGLRenderingContext { - const config: WebGLContextAttributes = { - alpha: true, - antialias: false, - depth: false, - stencil: true, - desynchronized: false, - // Disabled because it prevents Visual Regression Tests from working - // failIfMajorPerformanceCaveat: true, - powerPreference: 'high-performance', - premultipliedAlpha: true, - preserveDrawingBuffer: false, - }; - const gl = - // TODO: Remove this assertion once this issue is fixed in TypeScript - // https://github.com/microsoft/TypeScript/issues/53614 - (canvas.getContext(forceWebGL2 ? 'webgl2' : 'webgl', config) || - canvas.getContext( - 'experimental-webgl' as 'webgl', - config, - )) as unknown as WebGLRenderingContext | null; - if (!gl) { - throw new Error('Unable to create WebGL context'); - } - if (contextSpy) { - // Proxy the GL context to log all GL calls - return new Proxy(gl, { - get(target, prop) { - const value = target[prop as never] as unknown; - if (typeof value === 'function') { - contextSpy.increment(String(prop)); - return value.bind(target); - } - return value; - }, - }); - } - - return gl; -} - /** * Asserts a condition is truthy, otherwise throws an error * From fa7d8abe709401e5f506e987b09d5b469ac21dd5 Mon Sep 17 00:00:00 2001 From: wouterlucas Date: Tue, 29 Oct 2024 20:25:21 +0100 Subject: [PATCH 4/4] refactor: Update CoreRendererOptions platform type to include CorePlatform --- src/core/CoreTextureManager.ts | 15 +- src/core/Stage.ts | 10 +- src/core/lib/utils.ts | 34 ++ src/core/platforms/CoreGlContext.ts | 270 ++++++++++++++- src/core/platforms/web/WebGlContext.ts | 318 +++++++----------- src/core/renderers/CoreRenderer.ts | 2 +- .../webgl/WebGlCoreCtxRenderTexture.ts | 5 +- .../renderers/webgl/WebGlCoreCtxSubTexture.ts | 5 +- .../renderers/webgl/WebGlCoreCtxTexture.ts | 10 +- src/core/renderers/webgl/WebGlCoreRenderOp.ts | 7 +- src/core/renderers/webgl/WebGlCoreRenderer.ts | 32 +- src/core/renderers/webgl/WebGlCoreShader.ts | 5 +- .../renderers/webgl/internal/RendererUtils.ts | 12 +- .../renderers/webgl/internal/ShaderUtils.ts | 7 +- .../renderers/webgl/shaders/DynamicShader.ts | 2 +- .../webgl/shaders/effects/ShaderEffect.ts | 2 +- src/core/textures/ImageTexture.ts | 19 +- src/main-api/Renderer.ts | 14 +- 18 files changed, 511 insertions(+), 258 deletions(-) diff --git a/src/core/CoreTextureManager.ts b/src/core/CoreTextureManager.ts index 8934e1ed..cab7c89a 100644 --- a/src/core/CoreTextureManager.ts +++ b/src/core/CoreTextureManager.ts @@ -25,6 +25,8 @@ import { NoiseTexture } from './textures/NoiseTexture.js'; import { SubTexture } from './textures/SubTexture.js'; import { RenderTexture } from './textures/RenderTexture.js'; import type { Texture } from './textures/Texture.js'; +import type { CorePlatform } from './platforms/CorePlatform.js'; +import type { WebPlatform } from './platforms/web/WebPlatform.js'; /** * Augmentable map of texture class types @@ -139,6 +141,10 @@ export interface TextureOptions { } export class CoreTextureManager { + private platform: CorePlatform | WebPlatform; + public createImageBitmap: typeof createImageBitmap; + public hasCreateImageBitmap = false; + /** * Map of textures by cache key */ @@ -155,8 +161,8 @@ export class CoreTextureManager { txConstructors: Partial = {}; imageWorkerManager: ImageWorkerManager | null = null; - hasCreateImageBitmap = !!self.createImageBitmap; hasWorker = !!self.Worker; + /** * Renderer that this texture manager is associated with * @@ -177,7 +183,12 @@ export class CoreTextureManager { */ frameTime = 0; - constructor(numImageWorkers: number) { + constructor(numImageWorkers: number, platform: CorePlatform | WebPlatform) { + this.platform = platform; + + this.createImageBitmap = platform.createImageBitmap; + this.hasCreateImageBitmap = !!this.createImageBitmap; + // Register default known texture types if (this.hasCreateImageBitmap && this.hasWorker && numImageWorkers > 0) { this.imageWorkerManager = new ImageWorkerManager(numImageWorkers); diff --git a/src/core/Stage.ts b/src/core/Stage.ts index 2da105b7..323a04d9 100644 --- a/src/core/Stage.ts +++ b/src/core/Stage.ts @@ -122,7 +122,7 @@ export class Stage { private frameEventQueue: [name: string, payload: unknown][] = []; private fontResolveMap: Record = {}; - private platform: CorePlatform; + private platform: CorePlatform | WebPlatform | null = null; /// Debug data contextSpy: ContextSpy | null = null; @@ -152,7 +152,6 @@ export class Stage { this.shManager = new CoreShaderManager(); this.animationManager = new AnimationManager(); this.contextSpy = enableContextSpy ? new ContextSpy() : null; - this.platform = platform; let bm = [0, 0, 0, 0] as [number, number, number, number]; if (boundsMargin) { @@ -162,6 +161,11 @@ export class Stage { } this.boundsMargin = bm; + assertTruthy( + platform !== null, + 'A CorePlatform is not provided in the options', + ); + const rendererOptions: CoreRendererOptions = { stage: this, platform, @@ -264,7 +268,7 @@ export class Stage { } updateFrameTime() { - const newFrameTime = this.platform.getTimeStamp(); + const newFrameTime = this.platform?.getTimeStamp() || Date.now(); this.lastFrameTime = this.currentFrameTime; this.currentFrameTime = newFrameTime; this.deltaTime = !this.lastFrameTime diff --git a/src/core/lib/utils.ts b/src/core/lib/utils.ts index 0a5dc386..eb73a715 100644 --- a/src/core/lib/utils.ts +++ b/src/core/lib/utils.ts @@ -285,3 +285,37 @@ export function convertUrlToAbsolute(url: string): string { const absoluteUrl = new URL(url, self.location.href); return absoluteUrl.href; } + +/** + * Compare two arrays for equality. + * + * @remarks + * This function will not try to compare nested arrays or Float32Arrays and + * instead will always return false when they are encountered. + * + * @param a + * @param b + * @returns + */ +export function compareArrays(a: T[], b: T[]): boolean { + if (a.length !== b.length) { + return false; + } + + let result = false; + for (let i = 0; i < a.length; i++) { + if (Array.isArray(a[i]) || a[i] instanceof Float32Array) { + result = false; + break; + } + + if (a[i] !== b[i]) { + result = false; + break; + } + + result = true; + } + + return result; +} diff --git a/src/core/platforms/CoreGlContext.ts b/src/core/platforms/CoreGlContext.ts index 698dd623..f9b41fc0 100644 --- a/src/core/platforms/CoreGlContext.ts +++ b/src/core/platforms/CoreGlContext.ts @@ -18,6 +18,67 @@ */ export abstract class CoreGlContext { + //#region Cached WebGL State + protected abstract activeTextureUnit: number | undefined; + protected abstract texture2dUnits: Array; + protected abstract texture2dParams: WeakMap< + WebGLTexture, + Record + >; + protected abstract scissorEnabled: boolean; + protected abstract scissorX: number; + protected abstract scissorY: number; + protected abstract scissorWidth: number; + protected abstract scissorHeight: number; + protected abstract blendEnabled: boolean; + protected abstract blendSrcRgb: number; + protected abstract blendDstRgb: number; + protected abstract blendSrcAlpha: number; + protected abstract blendDstAlpha: number; + protected abstract boundArrayBuffer: WebGLBuffer | null; + protected abstract boundElementArrayBuffer: WebGLBuffer | null; + protected abstract curProgram: WebGLProgram | null; + //#endregion Cached WebGL State + + //#region Canvas + public abstract readonly canvas: HTMLCanvasElement | OffscreenCanvas; + //#endregion Canvas + + //#region WebGL Enums + public abstract readonly MAX_RENDERBUFFER_SIZE: GLenum; + public abstract readonly MAX_TEXTURE_SIZE: GLenum; + public abstract readonly MAX_VIEWPORT_DIMS: GLenum; + public abstract readonly MAX_VERTEX_TEXTURE_IMAGE_UNITS: GLenum; + public abstract readonly MAX_TEXTURE_IMAGE_UNITS: GLenum; + public abstract readonly MAX_COMBINED_TEXTURE_IMAGE_UNITS: GLenum; + public abstract readonly MAX_VERTEX_ATTRIBS: GLenum; + public abstract readonly MAX_VARYING_VECTORS: GLenum; + public abstract readonly MAX_VERTEX_UNIFORM_VECTORS: GLenum; + public abstract readonly MAX_FRAGMENT_UNIFORM_VECTORS: GLenum; + public abstract readonly TEXTURE_MAG_FILTER: GLenum; + public abstract readonly TEXTURE_MIN_FILTER: GLenum; + public abstract readonly TEXTURE_WRAP_S: GLenum; + public abstract readonly TEXTURE_WRAP_T: GLenum; + public abstract readonly LINEAR: GLenum; + public abstract readonly CLAMP_TO_EDGE: GLenum; + public abstract readonly RGBA: GLenum; + public abstract readonly UNSIGNED_BYTE: GLenum; + public abstract readonly UNPACK_PREMULTIPLY_ALPHA_WEBGL: GLenum; + public abstract readonly UNPACK_FLIP_Y_WEBGL: GLenum; + public abstract readonly FLOAT: GLenum; + public abstract readonly TRIANGLES: GLenum; + public abstract readonly UNSIGNED_SHORT: GLenum; + public abstract readonly ONE: GLenum; + public abstract readonly ONE_MINUS_SRC_ALPHA: GLenum; + public abstract readonly VERTEX_SHADER: GLenum; + public abstract readonly FRAGMENT_SHADER: GLenum; + public abstract readonly STATIC_DRAW: GLenum; + public abstract readonly COMPILE_STATUS: GLenum; + public abstract readonly LINK_STATUS: GLenum; + public abstract readonly DYNAMIC_DRAW: GLenum; + public abstract readonly COLOR_ATTACHMENT0: GLenum; + //#endregion WebGL Enums + /** * Sets the active texture unit. * @param textureUnit - The index of the texture unit to activate. @@ -310,6 +371,17 @@ export abstract class CoreGlContext { v2: number, ): void; + /** + * Sets the value of a vec3 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of vec3 values to set. + */ + abstract uniform3fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ): void; + /** * Sets the value of a vec4 (4-component float vector) uniform variable. * @param location - The location of the uniform variable. @@ -357,7 +429,7 @@ export abstract class CoreGlContext { ): void; /** - * Sets the value of a 4x4 float matrix uniform variable. + * Sets the value of a 4x4 float matrix uniform variablisWebGl2e. * @param location - The location of the uniform variable. * @param value - The matrix to set (must be 4x4). */ @@ -385,6 +457,28 @@ export abstract class CoreGlContext { v1: number, ): void; + /** + * Sets the value of a vec2 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of vec2 values to set. + */ + abstract uniform2fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ): void; + + /** + * Sets the value of an ivec2 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of ivec2 values to set. + */ + abstract uniform2iv( + location: WebGLUniformLocation | null, + value: Int32Array | number[], + ): void; + /** * Sets the value of a vec3 (3-component integer vector) uniform variable. * @param location - The location of the uniform variable. @@ -399,6 +493,17 @@ export abstract class CoreGlContext { v2: number, ): void; + /** + * Sets the value of an ivec3 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of ivec3 values to set. + */ + abstract uniform3iv( + location: WebGLUniformLocation | null, + value: Int32Array | number[], + ): void; + /** * Sets the value of a vec4 (4-component integer vector) uniform variable. * @param location - The location of the uniform variable. @@ -425,6 +530,28 @@ export abstract class CoreGlContext { value: Int32Array | number[], ): void; + /** + * Sets the value of an ivec4 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of ivec4 values to set. + */ + abstract uniform4iv( + location: WebGLUniformLocation | null, + value: Int32Array | number[], + ): void; + + /** + * Sets the value of a vec4 array uniform variable. + * + * @param location - The location of the uniform variable. + * @param value - The array of vec4 values to set. + */ + abstract uniform4fv( + location: WebGLUniformLocation | null, + value: Float32Array | number[], + ): void; + //////////////////////// // Viewport and Scissor Management //////////////////////// @@ -470,6 +597,10 @@ export abstract class CoreGlContext { */ abstract blendFunc(src: GLenum, dst: GLenum): void; + //////////////////////// + // Main + //////////////////////// + /** * Sets the color values used when clearing the color buffer. * @param red - The red component of the clear color. @@ -488,7 +619,7 @@ export abstract class CoreGlContext { * Clears buffers to preset values. * @param mask - A bitmask indicating which buffer(s) to clear (e.g., `gl.COLOR_BUFFER_BIT`). */ - abstract clear(mask: GLbitfield): void; + abstract clear(mask?: GLbitfield): void; /** * Renders primitives from array data. @@ -504,6 +635,109 @@ export abstract class CoreGlContext { offset: GLintptr, ): void; + /** + * ``` + * gl.getAttribLocation(program, name); + * ``` + * + * @param program + * @param name + * @returns + */ + abstract getAttribLocation(program: WebGLProgram, name: string): GLint; + + /** + * ``` + * gl.enableVertexAttribArray(index); + * ``` + * + * @param index + */ + abstract enableVertexAttribArray(index: number): void; + + /** + * ``` + * gl.disableVertexAttribArray(index); + * ``` + * + * @param index + */ + abstract disableVertexAttribArray(index: number): void; + + /** + * ``` + * gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + * gl.bufferData(gl.ARRAY_BUFFER, data, usage); + * ``` + * + * @remarks + * **WebGL Combo**: `gl.bindBuffer` and `gl.bufferData` are combined into one function. + * + * @param buffer + * @param data + * @param usage + */ + abstract arrayBufferData( + buffer: WebGLBuffer | null, + data: ArrayBufferView, + usage: GLenum, + ): void; + + /** + * ``` + * gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); + * gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, usage); + * ``` + * @remarks + * **WebGL Combo**: `gl.bindBuffer` and `gl.bufferData` are combined into one function. + * + * @param buffer + * @param data + * @param usage + */ + abstract elementArrayBufferData( + buffer: WebGLBuffer | null, + data: ArrayBufferView, + usage: GLenum, + ): void; + + /** + * ``` + * gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + * gl.vertexAttribPointer(index, size, type, normalized, stride, offset); + * ``` + * + * @remarks + * **WebGL Combo**: `gl.bindBuffer` and `gl.vertexAttribPointer` are combined into one function. + * + * @param buffer + * @param index + * @param size + * @param type + * @param normalized + * @param stride + * @param offset + */ + abstract vertexAttribPointer( + buffer: WebGLBuffer, + index: GLuint, + size: GLint, + type: GLenum, + normalized: GLboolean, + stride: GLsizei, + offset: GLintptr, + ): void; + + /** + * ``` + * gl.pixelStorei(pname, param); + * ``` + * + * @param pname + * @param param + */ + abstract pixelStorei(pname: GLenum, param: GLint | GLboolean): void; + /** * Renders primitives from the currently bound array data. * @param mode - The kind of primitives to render (e.g., `gl.TRIANGLES`). @@ -513,6 +747,34 @@ export abstract class CoreGlContext { // Currently not used in L3 renderer // abstract drawArrays(mode: GLenum, first: GLint, count: GLsizei): void; + //////////////////////// + // WebGL2 + //////////////////////// + + /** + * ``` + * gl.createVertexArray(); + * ``` + * + * @remarks + * This is a WebGL2 only function + * + * @returns + */ + abstract createVertexArray(): void; + + /** + * ``` + * gl.bindVertexArray(vertexArray); + * ``` + * + * @remarks + * This is a WebGL2 only function + * + * @param vertexArray + */ + abstract bindVertexArray(vertexArray: WebGLVertexArrayObject | null): void; + //////////////////////// // Extension handling //////////////////////// @@ -522,7 +784,7 @@ export abstract class CoreGlContext { * @param name - The name of the extension to retrieve. * @returns The extension object, or null if the extension is not supported. */ - abstract getExtension(name: string): any; + abstract getExtension(name: string): string | null; //////////////////////// // Query parameters and states @@ -533,7 +795,7 @@ export abstract class CoreGlContext { * @param pname - The name of the parameter to query (e.g., `gl.MAX_TEXTURE_SIZE`). * @returns The value of the requested parameter. */ - abstract getParameter(pname: GLenum): any; + abstract getParameter(pname: GLenum): GLenum; /** * Determines if the current WebGL context is WebGL2. diff --git a/src/core/platforms/web/WebGlContext.ts b/src/core/platforms/web/WebGlContext.ts index 54682e13..f1522f6e 100644 --- a/src/core/platforms/web/WebGlContext.ts +++ b/src/core/platforms/web/WebGlContext.ts @@ -28,29 +28,29 @@ import { CoreGlContext } from '../CoreGlContext.js'; */ export class WebGlContext extends CoreGlContext { //#region Cached WebGL State - private activeTextureUnit = 0; - private texture2dUnits: Array; - private texture2dParams: WeakMap< + protected activeTextureUnit = 0; + protected texture2dUnits: Array; + protected texture2dParams: WeakMap< WebGLTexture, Record > = new WeakMap(); - private scissorEnabled; - private scissorX: number; - private scissorY: number; - private scissorWidth: number; - private scissorHeight: number; - private blendEnabled; - private blendSrcRgb: number; - private blendDstRgb: number; - private blendSrcAlpha: number; - private blendDstAlpha: number; - private boundArrayBuffer: WebGLBuffer | null; - private boundElementArrayBuffer: WebGLBuffer | null; - private curProgram: WebGLProgram | null; + protected scissorEnabled; + protected scissorX: number; + protected scissorY: number; + protected scissorWidth: number; + protected scissorHeight: number; + protected blendEnabled; + protected blendSrcRgb: number; + protected blendDstRgb: number; + protected blendSrcAlpha: number; + protected blendDstAlpha: number; + protected boundArrayBuffer: WebGLBuffer | null; + protected boundElementArrayBuffer: WebGLBuffer | null; + protected curProgram: WebGLProgram | null; //#endregion Cached WebGL State //#region Canvas - public readonly canvas; + public readonly canvas: HTMLCanvasElement | OffscreenCanvas; //#endregion Canvas //#region WebGL Enums @@ -199,9 +199,8 @@ export class WebGlContext extends CoreGlContext { * @param textureUnit */ activeTexture(textureUnit: number) { - const { gl } = this; if (this.activeTextureUnit !== textureUnit) { - gl.activeTexture(textureUnit + gl.TEXTURE0); + this.gl.activeTexture(textureUnit + this.gl.TEXTURE0); this.activeTextureUnit = textureUnit; } } @@ -227,9 +226,8 @@ export class WebGlContext extends CoreGlContext { } private _getActiveTexture(): WebGLTexture | null { - const { activeTextureUnit, texture2dUnits } = this; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return texture2dUnits[activeTextureUnit]!; + return this.texture2dUnits[this.activeTextureUnit]!; } /** @@ -244,22 +242,23 @@ export class WebGlContext extends CoreGlContext { * @returns */ texParameteri(pname: number, param: number) { - const { gl, texture2dParams } = this; - const activeTexture = this._getActiveTexture(); - if (!activeTexture) { + if (activeTexture === null) { throw new Error('No active texture'); } - let textureParams = texture2dParams.get(activeTexture); - if (!textureParams) { + + let textureParams = this.texture2dParams.get(activeTexture); + if (textureParams === undefined) { textureParams = {}; - texture2dParams.set(activeTexture, textureParams); + this.texture2dParams.set(activeTexture, textureParams); } + if (textureParams[pname] === param) { return; } + textureParams[pname] = param; - gl.texParameteri(gl.TEXTURE_2D, pname, param); + this.gl.texParameteri(this.gl.TEXTURE_2D, pname, param); } /** @@ -315,10 +314,9 @@ export class WebGlContext extends CoreGlContext { type?: any, pixels?: any, ) { - const { gl } = this; if (format) { - gl.texImage2D( - gl.TEXTURE_2D, + this.gl.texImage2D( + this.gl.TEXTURE_2D, level, internalFormat, widthOrFormat, @@ -329,8 +327,8 @@ export class WebGlContext extends CoreGlContext { pixels, ); } else { - gl.texImage2D( - gl.TEXTURE_2D, + this.gl.texImage2D( + this.gl.TEXTURE_2D, level, internalFormat, widthOrFormat, @@ -356,9 +354,8 @@ export class WebGlContext extends CoreGlContext { border: GLint, data?: ArrayBufferView, ): void { - const { gl } = this; - gl.compressedTexImage2D( - gl.TEXTURE_2D, + this.gl.compressedTexImage2D( + this.gl.TEXTURE_2D, level, internalformat, width, @@ -367,6 +364,7 @@ export class WebGlContext extends CoreGlContext { data as ArrayBufferView, ); } + /** * ``` * gl.pixelStorei(pname, param); @@ -376,8 +374,7 @@ export class WebGlContext extends CoreGlContext { * @param param */ pixelStorei(pname: GLenum, param: GLint | GLboolean) { - const { gl } = this; - gl.pixelStorei(pname, param); + this.gl.pixelStorei(pname, param); } /** @@ -389,8 +386,7 @@ export class WebGlContext extends CoreGlContext { * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D` */ generateMipmap() { - const { gl } = this; - gl.generateMipmap(gl.TEXTURE_2D); + this.gl.generateMipmap(this.gl.TEXTURE_2D); } /** @@ -401,8 +397,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ createTexture() { - const { gl } = this; - return gl.createTexture(); + return this.gl.createTexture(); } /** @@ -413,11 +408,11 @@ export class WebGlContext extends CoreGlContext { * @param texture */ deleteTexture(texture: WebGLTexture | null) { - const { gl } = this; - if (texture) { + if (texture !== null) { this.texture2dParams.delete(texture); } - gl.deleteTexture(texture); + + this.gl.deleteTexture(texture); } /** @@ -426,8 +421,7 @@ export class WebGlContext extends CoreGlContext { * ``` */ viewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei) { - const { gl } = this; - gl.viewport(x, y, width, height); + this.gl.viewport(x, y, width, height); } /** @@ -441,8 +435,7 @@ export class WebGlContext extends CoreGlContext { * @param alpha */ clearColor(red: GLclampf, green: GLclampf, blue: GLclampf, alpha: GLclampf) { - const { gl } = this; - gl.clearColor(red, green, blue, alpha); + this.gl.clearColor(red, green, blue, alpha); } /** @@ -452,15 +445,16 @@ export class WebGlContext extends CoreGlContext { * @param enable */ setScissorTest(enable: boolean) { - const { gl, scissorEnabled } = this; - if (enable === scissorEnabled) { + if (enable === this.scissorEnabled) { return; } - if (enable) { - gl.enable(gl.SCISSOR_TEST); + + if (enable === true) { + this.gl.enable(this.gl.SCISSOR_TEST); } else { - gl.disable(gl.SCISSOR_TEST); + this.gl.disable(this.gl.SCISSOR_TEST); } + this.scissorEnabled = enable; } @@ -475,14 +469,13 @@ export class WebGlContext extends CoreGlContext { * @param height */ scissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei) { - const { gl, scissorX, scissorY, scissorWidth, scissorHeight } = this; if ( - x !== scissorX || - y !== scissorY || - width !== scissorWidth || - height !== scissorHeight + x !== this.scissorX || + y !== this.scissorY || + width !== this.scissorWidth || + height !== this.scissorHeight ) { - gl.scissor(x, y, width, height); + this.gl.scissor(x, y, width, height); this.scissorX = x; this.scissorY = y; this.scissorWidth = width; @@ -499,15 +492,16 @@ export class WebGlContext extends CoreGlContext { * @returns */ setBlend(blend: boolean) { - const { gl, blendEnabled } = this; - if (blend === blendEnabled) { + if (blend === this.blendEnabled) { return; } - if (blend) { - gl.enable(gl.BLEND); + + if (blend === true) { + this.gl.enable(this.gl.BLEND); } else { - gl.disable(gl.BLEND); + this.gl.disable(this.gl.BLEND); } + this.blendEnabled = blend; } @@ -543,8 +537,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ createBuffer() { - const { gl } = this; - return gl.createBuffer(); + return this.gl.createBuffer(); } /** @@ -554,8 +547,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ createFramebuffer() { - const { gl } = this; - return gl.createFramebuffer(); + return this.gl.createFramebuffer(); } /** @@ -566,8 +558,7 @@ export class WebGlContext extends CoreGlContext { * @param framebuffer */ bindFramebuffer(framebuffer: WebGLFramebuffer | null) { - const { gl } = this; - gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer); } /** @@ -583,11 +574,10 @@ export class WebGlContext extends CoreGlContext { texture: WebGLTexture | null, level: GLint, ) { - const { gl } = this; - gl.framebufferTexture2D( - gl.FRAMEBUFFER, + this.gl.framebufferTexture2D( + this.gl.FRAMEBUFFER, attachment, - gl.TEXTURE_2D, + this.gl.TEXTURE_2D, texture, level, ); @@ -601,9 +591,8 @@ export class WebGlContext extends CoreGlContext { * @remarks * **WebGL Difference**: Clear mask is always `gl.COLOR_BUFFER_BIT` */ - clear() { - const { gl } = this; - gl.clear(gl.COLOR_BUFFER_BIT); + clear(mask?: GLbitfield) { + this.gl.clear(mask || this.gl.COLOR_BUFFER_BIT); } /** @@ -700,11 +689,10 @@ export class WebGlContext extends CoreGlContext { * @returns */ useProgram(program: WebGLProgram | null) { - const { gl, curProgram } = this; - if (curProgram === program) { + if (this.curProgram === program) { return; } - gl.useProgram(program); + this.gl.useProgram(program); this.curProgram = program; } @@ -715,8 +703,7 @@ export class WebGlContext extends CoreGlContext { * @param v0 - The value to set. */ uniform1f(location: WebGLUniformLocation | null, v0: number) { - const { gl } = this; - gl.uniform1f(location, v0); + this.gl.uniform1f(location, v0); } /** @@ -729,8 +716,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Float32Array | number[], ) { - const { gl } = this; - gl.uniform1fv(location, value); + this.gl.uniform1fv(location, value); } /** @@ -740,8 +726,7 @@ export class WebGlContext extends CoreGlContext { * @param v0 - The value to set. */ uniform1i(location: WebGLUniformLocation | null, v0: number) { - const { gl } = this; - gl.uniform1i(location, v0); + this.gl.uniform1i(location, v0); } /** @@ -754,8 +739,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Int32Array | number[], ) { - const { gl } = this; - gl.uniform1iv(location, value); + this.gl.uniform1iv(location, value); } /** @@ -766,8 +750,7 @@ export class WebGlContext extends CoreGlContext { * @param v1 - The second component of the vector. */ uniform2f(location: WebGLUniformLocation | null, v0: number, v1: number) { - const { gl } = this; - gl.uniform2f(location, v0, v1); + this.gl.uniform2f(location, v0, v1); } /** @@ -780,8 +763,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Float32Array | number[], ) { - const { gl } = this; - gl.uniform2fv(location, value); + this.gl.uniform2fv(location, value); } /** @@ -792,8 +774,7 @@ export class WebGlContext extends CoreGlContext { * @param v1 - The second component of the vector. */ uniform2i(location: WebGLUniformLocation | null, v0: number, v1: number) { - const { gl } = this; - gl.uniform2i(location, v0, v1); + this.gl.uniform2i(location, v0, v1); } /** @@ -806,8 +787,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Int32Array | number[], ) { - const { gl } = this; - gl.uniform2iv(location, value); + this.gl.uniform2iv(location, value); } /** @@ -824,8 +804,7 @@ export class WebGlContext extends CoreGlContext { v1: number, v2: number, ) { - const { gl } = this; - gl.uniform3f(location, v0, v1, v2); + this.gl.uniform3f(location, v0, v1, v2); } /** @@ -838,8 +817,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Float32Array | number[], ) { - const { gl } = this; - gl.uniform3fv(location, value); + this.gl.uniform3fv(location, value); } /** @@ -856,8 +834,7 @@ export class WebGlContext extends CoreGlContext { v1: number, v2: number, ) { - const { gl } = this; - gl.uniform3i(location, v0, v1, v2); + this.gl.uniform3i(location, v0, v1, v2); } /** @@ -870,8 +847,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Int32Array | number[], ) { - const { gl } = this; - gl.uniform3iv(location, value); + this.gl.uniform3iv(location, value); } /** @@ -890,8 +866,7 @@ export class WebGlContext extends CoreGlContext { v2: number, v3: number, ) { - const { gl } = this; - gl.uniform4f(location, v0, v1, v2, v3); + this.gl.uniform4f(location, v0, v1, v2, v3); } /** @@ -904,8 +879,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Float32Array | number[], ) { - const { gl } = this; - gl.uniform4fv(location, value); + this.gl.uniform4fv(location, value); } /** @@ -924,8 +898,7 @@ export class WebGlContext extends CoreGlContext { v2: number, v3: number, ) { - const { gl } = this; - gl.uniform4i(location, v0, v1, v2, v3); + this.gl.uniform4i(location, v0, v1, v2, v3); } /** @@ -938,8 +911,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Int32Array | number[], ) { - const { gl } = this; - gl.uniform4iv(location, value); + this.gl.uniform4iv(location, value); } /** @@ -953,8 +925,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Float32Array | number[], ) { - const { gl } = this; - gl.uniformMatrix2fv(location, false, value); + this.gl.uniformMatrix2fv(location, false, value); } /** @@ -966,8 +937,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Float32Array | number[], ) { - const { gl } = this; - gl.uniformMatrix3fv(location, false, value); + this.gl.uniformMatrix3fv(location, false, value); } /** @@ -979,8 +949,7 @@ export class WebGlContext extends CoreGlContext { location: WebGLUniformLocation | null, value: Float32Array | number[], ) { - const { gl } = this; - gl.uniformMatrix4fv(location, false, value); + this.gl.uniformMatrix4fv(location, false, value); } /** @@ -992,8 +961,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ getParameter(pname: GLenum): any { - const { gl } = this; - return gl.getParameter(pname); + return this.gl.getParameter(pname); } /** @@ -1007,8 +975,7 @@ export class WebGlContext extends CoreGlContext { * @param offset */ drawElements(mode: GLenum, count: GLsizei, type: GLenum, offset: GLintptr) { - const { gl } = this; - gl.drawElements(mode, count, type, offset); + this.gl.drawElements(mode, count, type, offset); } /** @@ -1020,8 +987,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ getExtension(name: string) { - const { gl } = this; - return gl.getExtension(name); + return this.gl.getExtension(name); } /** @@ -1032,9 +998,11 @@ export class WebGlContext extends CoreGlContext { * @returns */ createVertexArray() { - const { gl } = this; - assertTruthy(gl instanceof WebGL2RenderingContext); - return gl.createVertexArray(); + assertTruthy( + typeof (this.gl as WebGL2RenderingContext) === 'function', + 'createVertexArray is a WebGL2 only function and does not work on WebGL1', + ); + return (this.gl as WebGL2RenderingContext).createVertexArray(); } /** @@ -1045,9 +1013,11 @@ export class WebGlContext extends CoreGlContext { * @param vertexArray */ bindVertexArray(vertexArray: WebGLVertexArrayObject | null) { - const { gl } = this; - assertTruthy(gl instanceof WebGL2RenderingContext); - gl.bindVertexArray(vertexArray); + assertTruthy( + typeof (this.gl as WebGL2RenderingContext) === 'function', + 'bindVertexArray is a WebGL2 only function and does not work on WebGL1', + ); + (this.gl as WebGL2RenderingContext).bindVertexArray(vertexArray); } /** @@ -1057,11 +1027,10 @@ export class WebGlContext extends CoreGlContext { * * @param program * @param name - * @returns + * @returns {GLint} */ getAttribLocation(program: WebGLProgram, name: string) { - const { gl } = this; - return gl.getAttribLocation(program, name); + return this.gl.getAttribLocation(program, name); } /** @@ -1074,8 +1043,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ getUniformLocation(program: WebGLProgram, name: string) { - const { gl } = this; - return gl.getUniformLocation(program, name); + return this.gl.getUniformLocation(program, name); } /** @@ -1086,8 +1054,7 @@ export class WebGlContext extends CoreGlContext { * @param index */ enableVertexAttribArray(index: number) { - const { gl } = this; - gl.enableVertexAttribArray(index); + this.gl.enableVertexAttribArray(index); } /** @@ -1098,8 +1065,7 @@ export class WebGlContext extends CoreGlContext { * @param index */ disableVertexAttribArray(index: number) { - const { gl } = this; - gl.disableVertexAttribArray(index); + this.gl.disableVertexAttribArray(index); } /** @@ -1110,9 +1076,8 @@ export class WebGlContext extends CoreGlContext { * @param type * @returns */ - createShader(type: number) { - const { gl } = this; - return gl.createShader(type); + createShader(type: number): WebGLShader | null { + return this.gl.createShader(type); } /** @@ -1124,8 +1089,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ compileShader(shader: WebGLShader) { - const { gl } = this; - gl.compileShader(shader); + this.gl.compileShader(shader); } /** @@ -1137,8 +1101,7 @@ export class WebGlContext extends CoreGlContext { * @param shader */ attachShader(program: WebGLProgram, shader: WebGLShader) { - const { gl } = this; - gl.attachShader(program, shader); + this.gl.attachShader(program, shader); } /** @@ -1149,8 +1112,7 @@ export class WebGlContext extends CoreGlContext { * @param program */ linkProgram(program: WebGLProgram) { - const { gl } = this; - gl.linkProgram(program); + this.gl.linkProgram(program); } /** @@ -1161,8 +1123,7 @@ export class WebGlContext extends CoreGlContext { * @param shader */ deleteProgram(shader: WebGLProgram) { - const { gl } = this; - gl.deleteProgram(shader); + this.gl.deleteProgram(shader); } /** @@ -1174,8 +1135,7 @@ export class WebGlContext extends CoreGlContext { * @param pname */ getShaderParameter(shader: WebGLShader, pname: GLenum) { - const { gl } = this; - return gl.getShaderParameter(shader, pname); + return this.gl.getShaderParameter(shader, pname); } /** @@ -1186,8 +1146,7 @@ export class WebGlContext extends CoreGlContext { * @param shader */ getShaderInfoLog(shader: WebGLShader) { - const { gl } = this; - return gl.getShaderInfoLog(shader); + return this.gl.getShaderInfoLog(shader); } /** @@ -1198,8 +1157,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ createProgram() { - const { gl } = this; - return gl.createProgram(); + return this.gl.createProgram(); } /** @@ -1212,8 +1170,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ getProgramParameter(program: WebGLProgram, pname: GLenum) { - const { gl } = this; - return gl.getProgramParameter(program, pname); + return this.gl.getProgramParameter(program, pname); } /** @@ -1225,8 +1182,7 @@ export class WebGlContext extends CoreGlContext { * @returns */ getProgramInfoLog(program: WebGLProgram) { - const { gl } = this; - return gl.getProgramInfoLog(program); + return this.gl.getProgramInfoLog(program); } /** @@ -1238,8 +1194,7 @@ export class WebGlContext extends CoreGlContext { * @param source */ shaderSource(shader: WebGLShader, source: string) { - const { gl } = this; - gl.shaderSource(shader, source); + this.gl.shaderSource(shader, source); } /** @@ -1250,8 +1205,7 @@ export class WebGlContext extends CoreGlContext { * @param shader */ deleteShader(shader: WebGLShader) { - const { gl } = this; - gl.deleteShader(shader); + this.gl.deleteShader(shader); } } @@ -1272,37 +1226,3 @@ export type UniformMethodMap = { ? T : never; }; - -/** - * Compare two arrays for equality. - * - * @remarks - * This function will not try to compare nested arrays or Float32Arrays and - * instead will always return false when they are encountered. - * - * @param a - * @param b - * @returns - */ -export function compareArrays(a: T[], b: T[]): boolean { - if (a.length !== b.length) { - return false; - } - - let result = false; - for (let i = 0; i < a.length; i++) { - if (Array.isArray(a[i]) || a[i] instanceof Float32Array) { - result = false; - break; - } - - if (a[i] !== b[i]) { - result = false; - break; - } - - result = true; - } - - return result; -} diff --git a/src/core/renderers/CoreRenderer.ts b/src/core/renderers/CoreRenderer.ts index f8114968..4a5b710c 100644 --- a/src/core/renderers/CoreRenderer.ts +++ b/src/core/renderers/CoreRenderer.ts @@ -73,7 +73,7 @@ export interface CoreRendererOptions { bufferMemory: number; contextSpy: ContextSpy | null; forceWebGL2: boolean; - platform: WebPlatform; + platform: CorePlatform | WebPlatform; } export interface BufferInfo { diff --git a/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts b/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts index a670ad05..be28271b 100644 --- a/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts +++ b/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts @@ -19,8 +19,9 @@ import type { Dimensions } from '../../../common/CommonTypes.js'; import { assertTruthy } from '../../../utils.js'; +import type { CoreGlContext } from '../../platforms/CoreGlContext.js'; +import type { WebGlContext } from '../../platforms/web/WebGlContext.js'; import type { TextureMemoryManager } from '../../TextureMemoryManager.js'; -import type { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import type { RenderTexture } from '../../textures/RenderTexture.js'; import { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js'; @@ -30,7 +31,7 @@ export class WebGlCoreCtxRenderTexture extends WebGlCoreCtxTexture { readonly framebuffer: WebGLFramebuffer; constructor( - glw: WebGlContextWrapper, + glw: CoreGlContext | WebGlContext, memManager: TextureMemoryManager, textureSource: RenderTexture, ) { diff --git a/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts b/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts index 1db0271d..95980c73 100644 --- a/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +++ b/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts @@ -18,14 +18,15 @@ */ import type { Dimensions } from '../../../common/CommonTypes.js'; +import type { CoreGlContext } from '../../platforms/CoreGlContext.js'; +import type { WebGlContext } from '../../platforms/web/WebGlContext.js'; import type { TextureMemoryManager } from '../../TextureMemoryManager.js'; -import type { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import type { SubTexture } from '../../textures/SubTexture.js'; import { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js'; export class WebGlCoreCtxSubTexture extends WebGlCoreCtxTexture { constructor( - glw: WebGlContextWrapper, + glw: CoreGlContext | WebGlContext, memManager: TextureMemoryManager, textureSource: SubTexture, ) { diff --git a/src/core/renderers/webgl/WebGlCoreCtxTexture.ts b/src/core/renderers/webgl/WebGlCoreCtxTexture.ts index 851cbdcb..bae8cca1 100644 --- a/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +++ b/src/core/renderers/webgl/WebGlCoreCtxTexture.ts @@ -19,8 +19,9 @@ import type { Dimensions } from '../../../common/CommonTypes.js'; import { assertTruthy } from '../../../utils.js'; +import type { CoreGlContext } from '../../platforms/CoreGlContext.js'; +import type { WebGlContext } from '../../platforms/web/WebGlContext.js'; import type { TextureMemoryManager } from '../../TextureMemoryManager.js'; -import type { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; import type { Texture } from '../../textures/Texture.js'; import { isPowerOfTwo } from '../../utils.js'; import { CoreContextTexture } from '../CoreContextTexture.js'; @@ -46,7 +47,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture { private _h = 0; constructor( - protected glw: WebGlContextWrapper, + protected glw: CoreGlContext | WebGlContext, memManager: TextureMemoryManager, textureSource: Texture, ) { @@ -88,7 +89,10 @@ export class WebGlCoreCtxTexture extends CoreContextTexture { this._nativeCtxTexture = this.createNativeCtxTexture(); if (this._nativeCtxTexture === null) { this._state = 'failed'; - this.textureSource.setState('failed', new Error('Could not create WebGL Texture')); + this.textureSource.setState( + 'failed', + new Error('Could not create WebGL Texture'), + ); console.error('Could not create WebGL Texture'); return; } diff --git a/src/core/renderers/webgl/WebGlCoreRenderOp.ts b/src/core/renderers/webgl/WebGlCoreRenderOp.ts index 5a3b4b78..a2981926 100644 --- a/src/core/renderers/webgl/WebGlCoreRenderOp.ts +++ b/src/core/renderers/webgl/WebGlCoreRenderOp.ts @@ -23,8 +23,9 @@ import type { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js'; import type { WebGlCoreRendererOptions } from './WebGlCoreRenderer.js'; import type { BufferCollection } from './internal/BufferCollection.js'; import type { Dimensions } from '../../../common/CommonTypes.js'; -import type { Rect, RectWithValid } from '../../lib/utils.js'; -import type { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; +import type { RectWithValid } from '../../lib/utils.js'; +import type { CoreGlContext } from '../../platforms/CoreGlContext.js'; +import type { WebGlContext } from '../../platforms/web/WebGlContext.js'; const MAX_TEXTURES = 8; // TODO: get from gl @@ -39,7 +40,7 @@ export class WebGlCoreRenderOp extends CoreRenderOp { readonly maxTextures: number; constructor( - readonly glw: WebGlContextWrapper, + readonly glw: CoreGlContext | WebGlContext, readonly options: WebGlCoreRendererOptions, readonly buffers: BufferCollection, readonly shader: WebGlCoreShader, diff --git a/src/core/renderers/webgl/WebGlCoreRenderer.ts b/src/core/renderers/webgl/WebGlCoreRenderer.ts index a968f552..925b856a 100644 --- a/src/core/renderers/webgl/WebGlCoreRenderer.ts +++ b/src/core/renderers/webgl/WebGlCoreRenderer.ts @@ -17,7 +17,7 @@ * limitations under the License. */ -import { assertTruthy, createWebGLContext, hasOwn } from '../../../utils.js'; +import { assertTruthy, hasOwn } from '../../../utils.js'; import { CoreRenderer, type BufferInfo, @@ -66,7 +66,7 @@ interface CoreWebGlSystem { export class WebGlCoreRenderer extends CoreRenderer { //// WebGL Native Context and Data - glw: WebGlContext; + glw: CoreGlContext | WebGlContext; system: CoreWebGlSystem; //// Persistent data @@ -121,20 +121,19 @@ export class WebGlCoreRenderer extends CoreRenderer { this.stage.requestRender(); }); - const glw = platform.createWebGLContext( + this.glw = platform.createWebGLContext( canvas, options.forceWebGL2, options.contextSpy, ); - this.glw = glw; const color = getNormalizedRgbaComponents(clearColor); - glw.viewport(0, 0, canvas.width, canvas.height); - glw.clearColor(color[0]!, color[1]!, color[2]!, color[3]!); - glw.setBlend(true); - glw.blendFunc(glw.ONE, glw.ONE_MINUS_SRC_ALPHA); + this.glw.viewport(0, 0, canvas.width, canvas.height); + this.glw.clearColor(color[0]!, color[1]!, color[2]!, color[3]!); + this.glw.setBlend(true); + this.glw.blendFunc(this.glw.ONE, this.glw.ONE_MINUS_SRC_ALPHA); - createIndexBuffer(glw, bufferMemory); + createIndexBuffer(this.glw, bufferMemory); this.system = { parameters: getWebGlParameters(this.glw), @@ -143,7 +142,7 @@ export class WebGlCoreRenderer extends CoreRenderer { this.shManager.renderer = this; this.defShaderCtrl = this.shManager.loadShader('DefaultShader'); this.defaultShader = this.defShaderCtrl.shader as WebGlCoreShader; - const quadBuffer = glw.createBuffer(); + const quadBuffer = this.glw.createBuffer(); assertTruthy(quadBuffer); const stride = 6 * Float32Array.BYTES_PER_ELEMENT; this.quadBufferCollection = new BufferCollection([ @@ -153,7 +152,7 @@ export class WebGlCoreRenderer extends CoreRenderer { a_position: { name: 'a_position', size: 2, // 2 components per iteration - type: glw.FLOAT, // the data is 32bit floats + type: this.glw.FLOAT, // the data is 32bit floats normalized: false, // don't normalize the data stride, // 0 = move forward size * sizeof(type) each iteration to get the next position offset: 0, // start at the beginning of the buffer @@ -161,7 +160,7 @@ export class WebGlCoreRenderer extends CoreRenderer { a_textureCoordinate: { name: 'a_textureCoordinate', size: 2, - type: glw.FLOAT, + type: this.glw.FLOAT, normalized: false, stride, offset: 2 * Float32Array.BYTES_PER_ELEMENT, @@ -169,7 +168,7 @@ export class WebGlCoreRenderer extends CoreRenderer { a_color: { name: 'a_color', size: 4, - type: glw.UNSIGNED_BYTE, + type: this.glw.UNSIGNED_BYTE, normalized: true, stride, offset: 4 * Float32Array.BYTES_PER_ELEMENT, @@ -177,7 +176,7 @@ export class WebGlCoreRenderer extends CoreRenderer { a_textureIndex: { name: 'a_textureIndex', size: 1, - type: glw.FLOAT, + type: this.glw.FLOAT, normalized: false, stride, offset: 5 * Float32Array.BYTES_PER_ELEMENT, @@ -188,12 +187,11 @@ export class WebGlCoreRenderer extends CoreRenderer { } reset() { - const { glw } = this; this.curBufferIdx = 0; this.curRenderOp = null; this.renderOps.length = 0; - glw.setScissorTest(false); - glw.clear(); + this.glw.setScissorTest(false); + this.glw.clear(); } override getShaderManager(): CoreShaderManager { diff --git a/src/core/renderers/webgl/WebGlCoreShader.ts b/src/core/renderers/webgl/WebGlCoreShader.ts index 0f5432d4..c9fffb36 100644 --- a/src/core/renderers/webgl/WebGlCoreShader.ts +++ b/src/core/renderers/webgl/WebGlCoreShader.ts @@ -19,7 +19,8 @@ import type { Dimensions } from '../../../common/CommonTypes.js'; import { assertTruthy, hasOwn } from '../../../utils.js'; -import type { WebGlContextWrapper } from '../../platforms/web/WebGlContextWrapper.js'; +import type { CoreGlContext } from '../../platforms/CoreGlContext.js'; +import type { WebGlContext } from '../../platforms/web/WebGlContext.js'; import { CoreShader } from '../CoreShader.js'; import type { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js'; import type { WebGlCoreRenderOp } from './WebGlCoreRenderOp.js'; @@ -76,7 +77,7 @@ export abstract class WebGlCoreShader extends CoreShader { */ protected vao: WebGLVertexArrayObject | undefined; protected renderer: WebGlCoreRenderer; - protected glw: WebGlContextWrapper; + protected glw: CoreGlContext | WebGlContext; protected attributeBuffers: Record; protected attributeLocations: Record; protected attributeNames: string[]; diff --git a/src/core/renderers/webgl/internal/RendererUtils.ts b/src/core/renderers/webgl/internal/RendererUtils.ts index c4cea25e..9d93d603 100644 --- a/src/core/renderers/webgl/internal/RendererUtils.ts +++ b/src/core/renderers/webgl/internal/RendererUtils.ts @@ -17,7 +17,8 @@ * limitations under the License. */ -import type { WebGlContextWrapper } from '../../../platforms/web/WebGlContextWrapper.js'; +import type { CoreGlContext } from '../../../platforms/CoreGlContext.js'; +import type { WebGlContext } from '../../../platforms/web/WebGlContext.js'; export interface CoreWebGlParameters { MAX_RENDERBUFFER_SIZE: number; @@ -37,7 +38,7 @@ export interface CoreWebGlParameters { * @param glw */ export function getWebGlParameters( - glw: WebGlContextWrapper, + glw: CoreGlContext | WebGlContext, ): CoreWebGlParameters { const params: CoreWebGlParameters = { MAX_RENDERBUFFER_SIZE: 0, @@ -82,7 +83,7 @@ export interface CoreWebGlExtensions { * @param glw */ export function getWebGlExtensions( - glw: WebGlContextWrapper, + glw: CoreGlContext | WebGlContext, ): CoreWebGlExtensions { const extensions: CoreWebGlExtensions = { ANGLE_instanced_arrays: null, @@ -113,7 +114,10 @@ export function getWebGlExtensions( * @param glw * @param size */ -export function createIndexBuffer(glw: WebGlContextWrapper, size: number) { +export function createIndexBuffer( + glw: CoreGlContext | WebGlContext, + size: number, +) { const maxQuads = ~~(size / 80); const indices = new Uint16Array(maxQuads * 6); diff --git a/src/core/renderers/webgl/internal/ShaderUtils.ts b/src/core/renderers/webgl/internal/ShaderUtils.ts index 91cbea2b..67f4b4e4 100644 --- a/src/core/renderers/webgl/internal/ShaderUtils.ts +++ b/src/core/renderers/webgl/internal/ShaderUtils.ts @@ -17,7 +17,8 @@ * limitations under the License. */ -import type { WebGlContextWrapper } from '../../../platforms/web/WebGlContextWrapper.js'; +import type { CoreGlContext } from '../../../platforms/CoreGlContext.js'; +import type { WebGlContext } from '../../../platforms/web/WebGlContext.js'; import type { WebGlCoreRenderer } from '../WebGlCoreRenderer.js'; //#region Types @@ -94,7 +95,7 @@ export interface ShaderProgramSources { //#endregion Types export function createShader( - glw: WebGlContextWrapper, + glw: CoreGlContext | WebGlContext, type: number, source: string, ) { @@ -114,7 +115,7 @@ export function createShader( } export function createProgram( - glw: WebGlContextWrapper, + glw: CoreGlContext | WebGlContext, vertexShader: WebGLShader, fragmentShader: WebGLShader, ) { diff --git a/src/core/renderers/webgl/shaders/DynamicShader.ts b/src/core/renderers/webgl/shaders/DynamicShader.ts index 4536843a..c5789ed0 100644 --- a/src/core/renderers/webgl/shaders/DynamicShader.ts +++ b/src/core/renderers/webgl/shaders/DynamicShader.ts @@ -33,7 +33,7 @@ import { } from './effects/ShaderEffect.js'; import type { EffectMap } from '../../../CoreShaderManager.js'; import { assertTruthy } from '../../../../utils.js'; -import type { UniformMethodMap } from '../../../platforms/web/WebGlContextWrapper.js'; +import type { UniformMethodMap } from '../../../platforms/web/WebGlContext.js'; export interface DynamicShaderProps extends DimensionsShaderProp, diff --git a/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts b/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts index eca1db65..69c07926 100644 --- a/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts +++ b/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts @@ -1,6 +1,6 @@ import type { EffectMap } from '../../../../CoreShaderManager.js'; import type { ExtractProps } from '../../../../CoreTextureManager.js'; -import type { WebGlContextWrapper } from '../../../../platforms/web/WebGlContextWrapper.js'; + import type { AlphaShaderProp, DimensionsShaderProp, diff --git a/src/core/textures/ImageTexture.ts b/src/core/textures/ImageTexture.ts index 24efd7f6..40f4b37a 100644 --- a/src/core/textures/ImageTexture.ts +++ b/src/core/textures/ImageTexture.ts @@ -152,17 +152,24 @@ export class ImageTexture extends Texture { if (sw !== null && sh !== null) { return { - data: await createImageBitmap(blob, sx ?? 0, sy ?? 0, sw, sh, { - premultiplyAlpha: hasAlphaChannel ? 'premultiply' : 'none', - colorSpaceConversion: 'none', - imageOrientation: 'none', - }), + data: await this.txManager.createImageBitmap( + blob, + sx ?? 0, + sy ?? 0, + sw, + sh, + { + premultiplyAlpha: hasAlphaChannel ? 'premultiply' : 'none', + colorSpaceConversion: 'none', + imageOrientation: 'none', + }, + ), premultiplyAlpha: hasAlphaChannel, }; } return { - data: await createImageBitmap(blob, { + data: await this.txManager.createImageBitmap(blob, { premultiplyAlpha: hasAlphaChannel ? 'premultiply' : 'none', colorSpaceConversion: 'none', imageOrientation: 'none', diff --git a/src/main-api/Renderer.ts b/src/main-api/Renderer.ts index c00e10aa..2620d45f 100644 --- a/src/main-api/Renderer.ts +++ b/src/main-api/Renderer.ts @@ -372,11 +372,15 @@ export class RendererMain extends EventEmitter { inspector, } = resolvedSettings; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const platform = (new settings.platform() || - new WebPlatform()) as CorePlatform; + let platform; + if (settings.platform) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + platform = new settings.platform() as WebPlatform; + } else { + platform = new WebPlatform(); + } + assertTruthy( platform instanceof CorePlatform, 'Platform is not a core platform immplementation',