Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@luma.gl/webgl

Package Overview
Dependencies
Maintainers
6
Versions
234
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@luma.gl/webgl - npm Package Compare versions

Comparing version 9.1.0-alpha.15 to 9.1.0-alpha.16

6

dist/adapter/converters/device-parameters.d.ts

@@ -12,3 +12,3 @@ import type { CompareFunction } from '@luma.gl/core';

*/
export declare function withDeviceAndGLParameters<T = unknown>(device: Device, parameters: Parameters, glParameters: GLParameters, func: (device?: Device) => T): T;
export declare function withDeviceAndGLParameters<T = unknown>(device: Device, parameters: Parameters, glParameters: GLParameters, func: (_?: Device) => T): T;
/**

@@ -23,3 +23,3 @@ * Execute a function with a set of temporary WebGL parameter overrides

*/
export declare function withGLParameters<T = unknown>(device: Device, parameters: GLParameters, func: (device?: Device) => T): T;
export declare function withGLParameters<T = unknown>(device: Device, parameters: GLParameters, func: (_?: Device) => T): T;
/**

@@ -33,3 +33,3 @@ * Execute a function with a set of temporary WebGL parameter overrides

*/
export declare function withDeviceParameters<T = unknown>(device: Device, parameters: Parameters, func: (device?: Device) => T): T;
export declare function withDeviceParameters<T = unknown>(device: Device, parameters: Parameters, func: (_?: Device) => T): T;
/** Set WebGPU Style Parameters */

@@ -36,0 +36,0 @@ export declare function setDeviceParameters(device: Device, parameters: Parameters): void;

@@ -308,2 +308,23 @@ import type { ExternalImage } from '@luma.gl/core';

*/
export type ReadPixelsToArrayOptions = {
sourceX?: number;
sourceY?: number;
sourceFormat?: number;
sourceAttachment?: number;
target?: Uint8Array | Uint16Array | Float32Array;
sourceWidth?: number;
sourceHeight?: number;
sourceDepth?: number;
sourceType?: number;
};
export type ReadPixelsToBufferOptions = {
sourceX?: number;
sourceY?: number;
sourceFormat?: number;
target?: Buffer;
targetByteOffset?: number;
sourceWidth?: number;
sourceHeight?: number;
sourceType?: number;
};
/**

@@ -320,13 +341,3 @@ * Copies data from a type or a Texture object into ArrayBuffer object.

*/
export declare function readPixelsToArray(source: Framebuffer | Texture, options?: {
sourceX?: number;
sourceY?: number;
sourceFormat?: number;
sourceAttachment?: number;
target?: Uint8Array | Uint16Array | Float32Array;
sourceWidth?: number;
sourceHeight?: number;
sourceDepth?: number;
sourceType?: number;
}): Uint8Array | Uint16Array | Float32Array;
export declare function readPixelsToArray(source: Framebuffer | Texture, options?: ReadPixelsToArrayOptions): Uint8Array | Uint16Array | Float32Array;
/**

@@ -339,12 +350,3 @@ * Copies data from a Framebuffer or a Texture object into a Buffer object.

*/
export declare function readPixelsToBuffer(source: Framebuffer | Texture, options?: {
sourceX?: number;
sourceY?: number;
sourceFormat?: number;
target?: Buffer;
targetByteOffset?: number;
sourceWidth?: number;
sourceHeight?: number;
sourceType?: number;
}): WEBGLBuffer;
export declare function readPixelsToBuffer(source: Framebuffer | Texture, options?: ReadPixelsToBufferOptions): WEBGLBuffer;
/**

@@ -354,3 +356,3 @@ * Copy a rectangle from a Framebuffer or Texture object into a texture (at an offset)

*/
export declare function copyToTexture(source: Framebuffer | Texture, target: Texture | GL, options?: {
export declare function copyToTexture(sourceTexture: Framebuffer | Texture, destinationTexture: Texture | GL, options?: {
sourceX?: number;

@@ -357,0 +359,0 @@ sourceY?: number;

@@ -159,221 +159,3 @@ // luma.gl

}
// texImage methods
/**
* Clear a texture mip level.
* Wrapper for the messy WebGL texture API
*
export function clearMipLevel(gl: WebGL2RenderingContext, options: WebGLSetTextureOptions): void {
const {dimension, width, height, depth = 0, level = 0} = options;
const {glInternalFormat, glFormat, glType, compressed} = options;
const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);
switch (dimension) {
case '2d-array':
case '3d':
if (compressed) {
// prettier-ignore
gl.compressedTexImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, null);
} else {
// prettier-ignore
gl.texImage3D( glTarget, level, glInternalFormat, width, height, depth, BORDER, glFormat, glType, null);
}
break;
case '2d':
case 'cube':
if (compressed) {
// prettier-ignore
gl.compressedTexImage2D(glTarget, level, glInternalFormat, width, height, BORDER, null);
} else {
// prettier-ignore
gl.texImage2D(glTarget, level, glInternalFormat, width, height, BORDER, glFormat, glType, null);
}
break;
default:
throw new Error(dimension);
}
}
/**
* Set a texture mip level to the contents of an external image.
* Wrapper for the messy WebGL texture API
* @note Corresponds to WebGPU device.queue.copyExternalImageToTexture()
*
export function setMipLevelFromExternalImage(
gl: WebGL2RenderingContext,
image: ExternalImage,
options: WebGLSetTextureOptions
): void {
const {dimension, width, height, depth = 0, level = 0} = options;
const {glInternalFormat, glType} = options;
const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);
// TODO - we can't change texture width (due to WebGPU limitations) -
// and the width/heigh of an external image is implicit, so why do we need to extract it?
// So what width height do we supply? The image size or the texture size?
// const {width, height} = Texture.getExternalImageSize(image);
switch (dimension) {
case '2d-array':
case '3d':
// prettier-ignore
gl.texImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, glInternalFormat, glType, image);
break;
case '2d':
case 'cube':
// prettier-ignore
gl.texImage2D(glTarget, level, glInternalFormat, width, height, BORDER, glInternalFormat, glType, image);
break;
default:
throw new Error(dimension);
}
}
/**
* Set a texture mip level from CPU memory
* Wrapper for the messy WebGL texture API
* @note Not available (directly) in WebGPU
*
export function setMipLevelFromTypedArray(
gl: WebGL2RenderingContext,
data: TypedArray,
parameters: {},
options: {
dimension: '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d';
height: number;
width: number;
depth?: number;
level?: number;
offset?: number;
glTarget: GLTextureTarget;
glInternalFormat: GL;
glFormat: GL;
glType: GL;
compressed?: boolean;
}
): void {
const {dimension, width, height, depth = 0, level = 0, offset = 0} = options;
const {glInternalFormat, glFormat, glType, compressed} = options;
const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);
withGLParameters(gl, parameters, () => {
switch (dimension) {
case '2d-array':
case '3d':
if (compressed) {
// prettier-ignore
gl.compressedTexImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, data);
} else {
// prettier-ignore
gl.texImage3D( glTarget, level, glInternalFormat, width, height, depth, BORDER, glFormat, glType, data);
}
break;
case '2d':
if (compressed) {
// prettier-ignore
gl.compressedTexImage2D(glTarget, level, glInternalFormat, width, height, BORDER, data);
} else {
// prettier-ignore
gl.texImage2D( glTarget, level, glInternalFormat, width, height, BORDER, glFormat, glType, data, offset);
}
break;
default:
throw new Error(dimension);
}
});
}
/**
* Set a texture level from CPU memory
* @note Not available (directly) in WebGPU
_setMipLevelFromTypedArray(
depth: number,
level: number,
data: TextureLevelData,
offset = 0,
parameters
): void {
withGLParameters(this.gl, parameters, () => {
switch (this.props.dimension) {
case '2d-array':
case '3d':
if (this.compressed) {
// prettier-ignore
this.device.gl.compressedTexImage3D(this.glTarget, level, this.glInternalFormat, data.width, data.height, depth, BORDER, data.data);
} else {
// prettier-ignore
this.gl.texImage3D( this.glTarget, level, this.glInternalFormat, this.width, this.height, depth, BORDER, this.glFormat, this.glType, data.data);
}
break;
case '2d':
if (this.compressed) {
// prettier-ignore
this.device.gl.compressedTexImage2D(this.glTarget, level, this.glInternalFormat, data.width, data.height, BORDER, data.data);
} else {
// prettier-ignore
this.device.gl.texImage2D( this.glTarget, level, this.glInternalFormat, this.width, this.height, BORDER, this.glFormat, this.glType, data.data, offset);
}
break;
default:
throw new Error(this.props.dimension);
}
});
}
* Set a texture level from a GPU buffer
*
export function setMipLevelFromGPUBuffer(
gl: WebGL2RenderingContext,
buffer: Buffer,
options: WebGLSetTextureOptions
): void {
const {dimension, width, height, depth = 0, level = 0, byteOffset = 0} = options;
const {glInternalFormat, glFormat, glType, compressed} = options;
const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);
const webglBuffer = buffer as WEBGLBuffer;
const imageSize = buffer.byteLength;
// In WebGL the source buffer is not a parameter. Instead it needs to be bound to a special bind point
gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, webglBuffer.handle);
switch (dimension) {
case '2d-array':
case '3d':
if (compressed) {
// prettier-ignore
gl.compressedTexImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, imageSize, byteOffset);
} else {
// prettier-ignore
gl.texImage3D(glTarget, level, glInternalFormat, width, height, depth, BORDER, glFormat, glType, byteOffset);
}
break;
case '2d':
if (compressed) {
// prettier-ignore
gl.compressedTexImage2D(glTarget, level, glInternalFormat, width, height, BORDER, imageSize, byteOffset);
} else {
// prettier-ignore
gl.texImage2D(glTarget, level, glInternalFormat, width, height, BORDER, glFormat, glType, byteOffset);
}
break;
default:
throw new Error(dimension);
}
gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
}
*/
/**
* Copies data from a type or a Texture object into ArrayBuffer object.

@@ -452,7 +234,7 @@ * App can provide targetPixelArray or have it auto allocated by this method

commandEncoder.copyTextureToBuffer({
source: source,
sourceTexture: source,
width: sourceWidth,
height: sourceHeight,
origin: [sourceX, sourceY],
destination: webglBufferTarget,
destinationBuffer: webglBufferTarget,
byteOffset: targetByteOffset

@@ -471,3 +253,3 @@ });

// eslint-disable-next-line complexity, max-statements
export function copyToTexture(source, target, options) {
export function copyToTexture(sourceTexture, destinationTexture, options) {
const { sourceX = 0, sourceY = 0,

@@ -479,3 +261,3 @@ // attachment = GL.COLOR_ATTACHMENT0, // TODO - support gl.readBuffer

} = options || {};
const { framebuffer, deleteFramebuffer } = getFramebuffer(source);
const { framebuffer, deleteFramebuffer } = getFramebuffer(sourceTexture);
// assert(framebuffer);

@@ -496,4 +278,4 @@ const webglFramebuffer = framebuffer;

let textureTarget;
if (target instanceof WEBGLTexture) {
texture = target;
if (destinationTexture instanceof WEBGLTexture) {
texture = destinationTexture;
width = Number.isFinite(width) ? width : texture.width;

@@ -500,0 +282,0 @@ height = Number.isFinite(height) ? height : texture.height;

@@ -35,4 +35,4 @@ // luma.gl

function _copyBufferToBuffer(device, options) {
const source = options.source;
const destination = options.destination;
const source = options.sourceBuffer;
const destination = options.destinationBuffer;
// {In WebGL2 we can p}erform the copy on the GPU

@@ -60,3 +60,3 @@ // Use GL.COPY_READ_BUFFER+GL.COPY_WRITE_BUFFER avoid disturbing other targets and locking type

/** Texture to copy to/from. */
source,
sourceTexture,
/** Mip-map level of the texture to copy to/from. (Default 0) */

@@ -67,9 +67,9 @@ mipLevel = 0,

/** Width to copy */
width = options.source.width,
width = options.sourceTexture.width,
/** Height to copy */
height = options.source.height, depthOrArrayLayers = 0,
height = options.sourceTexture.height, depthOrArrayLayers = 0,
/** Defines the origin of the copy - the minimum corner of the texture sub-region to copy to/from. */
origin = [0, 0],
/** Destination buffer */
destination,
destinationBuffer,
/** Offset, in bytes, from the beginning of the buffer to the start of the image data (default 0) */

@@ -90,3 +90,3 @@ byteOffset = 0,

if (aspect !== 'all') {
throw new Error('not supported');
throw new Error('aspect not supported in WebGL');
}

@@ -98,6 +98,6 @@ // TODO - mipLevels are set when attaching texture to framebuffer

// Asynchronous read (PIXEL_PACK_BUFFER) is WebGL2 only feature
const { framebuffer, destroyFramebuffer } = getFramebuffer(source);
const { framebuffer, destroyFramebuffer } = getFramebuffer(sourceTexture);
let prevHandle;
try {
const webglBuffer = destination;
const webglBuffer = destinationBuffer;
const sourceWidth = width || framebuffer.width;

@@ -156,3 +156,3 @@ const sourceHeight = height || framebuffer.height;

/** Texture to copy to/from. */
source,
sourceTexture,
/** Mip-map level of the texture to copy to (Default 0) */

@@ -167,3 +167,3 @@ destinationMipLevel = 0,

/** Texture to copy to/from. */
destination
destinationTexture
/** Mip-map level of the texture to copy to/from. (Default 0) */

@@ -176,6 +176,6 @@ // destinationMipLevel = options.mipLevel,

} = options;
let { width = options.destination.width, height = options.destination.height
let { width = options.destinationTexture.width, height = options.destinationTexture.height
// depthOrArrayLayers = 0
} = options;
const { framebuffer, destroyFramebuffer } = getFramebuffer(source);
const { framebuffer, destroyFramebuffer } = getFramebuffer(sourceTexture);
const [sourceX, sourceY] = origin;

@@ -189,4 +189,4 @@ const [destinationX, destinationY, destinationZ] = destinationOrigin;

let textureTarget;
if (destination instanceof WEBGLTexture) {
texture = destination;
if (destinationTexture instanceof WEBGLTexture) {
texture = destinationTexture;
width = Number.isFinite(width) ? width : texture.width;

@@ -193,0 +193,0 @@ height = Number.isFinite(height) ? height : texture.height;

@@ -57,3 +57,3 @@ import type { RenderPipelineProps, RenderPipelineParameters, PrimitiveTopology, ShaderLayout, UniformValue, Binding, RenderPass, VertexArray } from '@luma.gl/core';

/** Report link status. First, check for shader compilation failures if linking fails */
_reportLinkStatus(status: 'success' | 'linking' | 'validation'): void;
_reportLinkStatus(status: 'success' | 'linking' | 'validation'): Promise<void>;
/**

@@ -60,0 +60,0 @@ * Get the shader compilation status

@@ -81,7 +81,7 @@ // luma.gl

// TODO - this is rather hacky - we could also remap the name directly in the shader layout.
const binding = this.shaderLayout.bindings.find(binding => binding.name === name) ||
this.shaderLayout.bindings.find(binding => binding.name === `${name}Uniforms`);
const binding = this.shaderLayout.bindings.find(binding_ => binding_.name === name) ||
this.shaderLayout.bindings.find(binding_ => binding_.name === `${name}Uniforms`);
if (!binding) {
const validBindings = this.shaderLayout.bindings
.map(binding => `"${binding.name}"`)
.map(binding_ => `"${binding_.name}"`)
.join(', ');

@@ -224,3 +224,3 @@ if (!options?.disableWarnings) {

/** Report link status. First, check for shader compilation failures if linking fails */
_reportLinkStatus(status) {
async _reportLinkStatus(status) {
switch (status) {

@@ -231,11 +231,24 @@ case 'success':

// First check for shader compilation failures if linking fails
if (this.vs.compilationStatus === 'error') {
this.vs.debugShader();
throw new Error(`Error during compilation of shader ${this.vs.id}`);
switch (this.vs.compilationStatus) {
case 'error':
this.vs.debugShader();
throw new Error(`Error during compilation of shader ${this.vs.id}`);
case 'pending':
this.vs.asyncCompilationStatus.then(() => this.vs.debugShader());
break;
case 'success':
break;
}
if (this.fs?.compilationStatus === 'error') {
this.fs.debugShader();
throw new Error(`Error during compilation of shader ${this.fs.id}`);
switch (this.fs?.compilationStatus) {
case 'error':
this.fs.debugShader();
throw new Error(`Error during compilation of shader ${this.fs.id}`);
case 'pending':
this.fs.asyncCompilationStatus.then(() => this.fs.debugShader());
break;
case 'success':
break;
}
throw new Error(`Error during ${status}: ${this.device.gl.getProgramInfoLog(this.handle)}`);
const linkErrorLog = this.device.gl.getProgramInfoLog(this.handle);
throw new Error(`Error during ${status}: ${linkErrorLog}`);
}

@@ -242,0 +255,0 @@ }

@@ -11,2 +11,3 @@ import { Shader, ShaderProps, CompilerMessage } from '@luma.gl/core';

destroy(): void;
get asyncCompilationStatus(): Promise<'pending' | 'success' | 'error'>;
getCompilationInfo(): Promise<readonly CompilerMessage[]>;

@@ -13,0 +14,0 @@ getCompilationInfoSync(): readonly CompilerMessage[];

@@ -36,2 +36,5 @@ // luma.gl

}
get asyncCompilationStatus() {
return this._waitForCompilationComplete().then(() => this.compilationStatus);
}
async getCompilationInfo() {

@@ -42,4 +45,4 @@ await this._waitForCompilationComplete();

getCompilationInfoSync() {
const log = this.device.gl.getShaderInfoLog(this.handle);
return log ? parseShaderCompilerLog(log) : [];
const shaderLog = this.device.gl.getShaderInfoLog(this.handle);
return shaderLog ? parseShaderCompilerLog(shaderLog) : [];
}

@@ -54,4 +57,3 @@ getTranslatedSource() {

async _compile(source) {
const addGLSLVersion = (source) => source.startsWith('#version ') ? source : `#version 300 es\n${source}`;
source = addGLSLVersion(source);
source = source.startsWith('#version ') ? source : `#version 300 es\n${source}`;
const { gl } = this.device;

@@ -58,0 +60,0 @@ gl.shaderSource(this.handle, source);

// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import { Adapter, Device, CanvasContext, log } from '@luma.gl/core';
import { Adapter, Device, log } from '@luma.gl/core';
import { WebGLDevice } from "./webgl-device.js";

@@ -46,3 +46,3 @@ import { enforceWebGL2 } from "../context/polyfills/polyfill-webgl1-extensions.js";

}
return new WebGLDevice({ gl: gl });
return new WebGLDevice({ _handle: gl });
}

@@ -53,13 +53,8 @@ async create(props = {}) {

// Load webgl and spector debug scripts from CDN if requested
if (props.debug) {
if (props.debugWebGL) {
promises.push(loadWebGLDeveloperTools());
}
if (props.debugWithSpectorJS) {
if (props.debugSpectorJS) {
promises.push(loadSpectorJS(props));
}
// Wait for page to load: if canvas is a string we need to query the DOM for the canvas element.
// We only wait when props.canvas is string to avoids setting the global page onload callback unless necessary.
if (typeof props.canvas === 'string') {
promises.push(CanvasContext.pageLoaded);
}
// Wait for all the loads to settle before creating the context.

@@ -73,3 +68,2 @@ // The Device.create() functions are async, so in contrast to the constructor, we can `await` here.

}
log.probe(LOG_LEVEL + 1, 'DOM is loaded')();
const device = new WebGLDevice(props);

@@ -76,0 +70,0 @@ // Log some debug info about the newly created context

import type { TypedArray } from '@math.gl/types';
import type { DeviceProps, DeviceInfo, CanvasContextProps, TextureFormat, Buffer, Texture, Framebuffer, VertexArray, VertexArrayProps } from '@luma.gl/core';
import type { DeviceProps, DeviceInfo, CanvasContextProps, TextureFormat, Buffer, Texture, Framebuffer, VertexArray, VertexArrayProps, BufferProps, ShaderProps, SamplerProps, TextureProps, ExternalTexture, ExternalTextureProps, FramebufferProps, RenderPipelineProps, ComputePipeline, ComputePipelineProps, RenderPassProps, ComputePass, ComputePassProps, CommandEncoderProps, TransformFeedbackProps, QuerySetProps } from '@luma.gl/core';
import { Device, CanvasContext } from '@luma.gl/core';

@@ -9,3 +9,2 @@ import type { GLExtensions } from '@luma.gl/constants';

import type { Spector } from "../context/debug/spector-types.js";
import type { BufferProps, ShaderProps, SamplerProps, TextureProps, ExternalTexture, ExternalTextureProps, FramebufferProps, RenderPipelineProps, ComputePipeline, ComputePipelineProps, RenderPassProps, ComputePass, ComputePassProps, CommandEncoderProps, TransformFeedbackProps, QuerySetProps } from '@luma.gl/core';
import { WEBGLBuffer } from "./resources/webgl-buffer.js";

@@ -12,0 +11,0 @@ import { WEBGLShader } from "./resources/webgl-shader.js";

@@ -61,5 +61,10 @@ // luma.gl

super({ ...props, id: props.id || uid('webgl-device') });
// WebGL requires a canvas to be created before creating the context
if (!props.createCanvasContext) {
throw new Error('WebGLDevice requires props.createCanvasContext to be set');
}
const canvasContextProps = props.createCanvasContext === true ? {} : props.createCanvasContext;
// If attaching to an already attached context, return the attached device
// @ts-expect-error device is attached to context
const device = props.gl?.device;
let device = canvasContextProps.canvas?.gl?.device;
if (device) {

@@ -69,18 +74,32 @@ throw new Error(`WebGL context already attached to device ${device.id}`);

// Create and instrument context
const canvas = props.gl?.canvas || props.canvas;
this.canvasContext = new WebGLCanvasContext(this, { ...props, canvas });
this.canvasContext = new WebGLCanvasContext(this, canvasContextProps);
this.lost = new Promise(resolve => {
this._resolveContextLost = resolve;
});
this.handle = createBrowserContext(this.canvasContext.canvas, {
...props,
const webglContextAttributes = { ...props.webgl };
// Copy props from CanvasContextProps
if (canvasContextProps.alphaMode === 'premultiplied') {
webglContextAttributes.premultipliedAlpha = true;
}
if (props.powerPreference !== undefined) {
webglContextAttributes.powerPreference = props.powerPreference;
}
const gl = createBrowserContext(this.canvasContext.canvas, {
onContextLost: (event) => this._resolveContextLost?.({
reason: 'destroyed',
message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
})
});
this.gl = this.handle;
if (!this.handle) {
}),
// eslint-disable-next-line no-console
onContextRestored: (event) => console.log('WebGL context restored')
}, webglContextAttributes);
if (!gl) {
throw new Error('WebGL context creation failed');
}
// @ts-expect-error device is attached to context
device = gl.device;
if (device) {
throw new Error(`WebGL context already attached to device ${device.id}`);
}
this.handle = gl;
this.gl = gl;
// Add spector debug instrumentation to context

@@ -96,4 +115,4 @@ // We need to trust spector integration to decide if spector should be initialized

this.limits = new WebGLDeviceLimits(this.gl);
this.features = new WebGLDeviceFeatures(this.gl, this._extensions, this.props.disabledFeatures);
if (this.props.initalizeFeatures) {
this.features = new WebGLDeviceFeatures(this.gl, this._extensions, this.props._disabledFeatures);
if (this.props._initializeFeatures) {
this.features.initializeFeatures();

@@ -108,4 +127,4 @@ }

// DEBUG contexts: Add luma debug instrumentation to the context, force log level to at least 1
if (props.debug) {
this.gl = makeDebugContext(this.gl, { ...props, throwOnError: true });
if (props.debugWebGL) {
this.gl = makeDebugContext(this.gl, { ...props });
this.debug = true;

@@ -138,3 +157,3 @@ log.level = Math.max(log.level, 1);

createBuffer(props) {
const newProps = this._getBufferProps(props);
const newProps = this._normalizeBufferProps(props);
return new WEBGLBuffer(this, newProps);

@@ -141,0 +160,0 @@ }

// Forked from https://github.com/BabylonJS/Spector.js/blob/master/dist/spector.d.ts
/* eslint-disable camelcase */
/* eslint-disable camelcase, no-shadow */
var LogLevel;

@@ -4,0 +4,0 @@ (function (LogLevel) {

@@ -1,8 +0,8 @@

import { Spector } from "./spector-types.js";
import type { Spector } from "./spector-types.js";
/** Spector debug initialization options */
type SpectorProps = {
/** Whether spector is enabled */
debugWithSpectorJS?: boolean;
/** Whether spector.js is enabled */
debugSpectorJS?: boolean;
/** URL to load spector script from. Typically a CDN URL */
spectorUrl?: string;
debugSpectorJSUrl?: string;
/** Canvas to monitor */

@@ -17,3 +17,3 @@ gl?: WebGL2RenderingContext;

export declare function loadSpectorJS(props: {
spectorUrl?: string;
debugSpectorJSUrl?: string;
}): Promise<void>;

@@ -20,0 +20,0 @@ export declare function initializeSpectorJS(props: SpectorProps): Spector | null;

@@ -10,7 +10,7 @@ // luma.gl

export const DEFAULT_SPECTOR_PROPS = {
debugWithSpectorJS: log.get('spector') || log.get('spectorjs'),
debugSpectorJS: log.get('debug-spectorjs'),
// https://github.com/BabylonJS/Spector.js#basic-usage
// https://forum.babylonjs.com/t/spectorcdn-is-temporarily-off/48241
// spectorUrl: 'https://spectorcdn.babylonjs.com/spector.bundle.js';
spectorUrl: 'https://cdn.jsdelivr.net/npm/spectorjs@0.9.30/dist/spector.bundle.js',
debugSpectorJSUrl: 'https://cdn.jsdelivr.net/npm/spectorjs@0.9.30/dist/spector.bundle.js',
gl: undefined

@@ -22,3 +22,3 @@ };

try {
await loadScript(props.spectorUrl || DEFAULT_SPECTOR_PROPS.spectorUrl);
await loadScript(props.debugSpectorJSUrl || DEFAULT_SPECTOR_PROPS.debugSpectorJSUrl);
}

@@ -32,3 +32,3 @@ catch (error) {

props = { ...DEFAULT_SPECTOR_PROPS, ...props };
if (!props.debugWithSpectorJS) {
if (!props.debugSpectorJS) {
return null;

@@ -38,4 +38,4 @@ }

log.probe(LOG_LEVEL, 'SPECTOR found and initialized. Start with `luma.spector.displayUI()`')();
const { Spector } = globalThis.SPECTOR;
spector = new Spector();
const { Spector: SpectorJS } = globalThis.SPECTOR;
spector = new SpectorJS();
if (globalThis.luma) {

@@ -42,0 +42,0 @@ globalThis.luma.spector = spector;

type DebugContextProps = {
debug?: boolean;
throwOnError?: boolean;
break?: string[];
debugWebGL?: boolean;
};

@@ -6,0 +4,0 @@ declare global {

@@ -32,3 +32,3 @@ // luma.gl

export function makeDebugContext(gl, props = {}) {
return props.debug ? getDebugContext(gl, props) : getRealContext(gl);
return props.debugWebGL ? getDebugContext(gl, props) : getRealContext(gl);
}

@@ -92,5 +92,3 @@ // Returns the real context from either of the real/debug contexts

debugger; // eslint-disable-line
if (props.throwOnError) {
throw new Error(message);
}
// throw new Error(message);
}

@@ -104,22 +102,9 @@ // Don't generate function string until it is needed

}
// If array of breakpoint strings supplied, check if any of them is contained in current GLEnum function
if (props.break && props.break.length > 0) {
functionString = functionString || getFunctionString(functionName, functionArgs);
const isBreakpoint = props.break.every((breakOn) => functionString.indexOf(breakOn) !== -1);
if (isBreakpoint) {
debugger; // eslint-disable-line
}
}
for (const arg of functionArgs) {
if (arg === undefined) {
functionString = functionString || getFunctionString(functionName, functionArgs);
if (props.throwOnError) {
throw new Error(`Undefined argument: ${functionString}`);
}
else {
log.error(`Undefined argument: ${functionString}`)();
debugger; // eslint-disable-line
}
debugger; // eslint-disable-line
// throw new Error(`Undefined argument: ${functionString}`);
}
}
}
/**
* ContextProps
* @param onContextLost
* @param onContextRestored
*
* BROWSER CONTEXT PARAMETERS
* @param debug Instrument context (at the expense of performance).
* @param alpha Default render target has an alpha buffer.
* @param depth Default render target has a depth buffer of at least 16 bits.
* @param stencil Default render target has a stencil buffer of at least 8 bits.
* @param antialias Boolean that indicates whether or not to perform anti-aliasing.
* @param premultipliedAlpha Boolean that indicates that the page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
* @param preserveDrawingBuffer Default render target buffers will not be automatically cleared and will preserve their values until cleared or overwritten
* @param failIfMajorPerformanceCaveat Do not create if the system performance is low.
* @param onContextRestored *
*/
type ContextProps = {
onContextLost?: (event: Event) => void;
onContextRestored?: (event: Event) => void;
alpha?: boolean;
desynchronized?: boolean;
antialias?: boolean;
depth?: boolean;
failIfMajorPerformanceCaveat?: boolean;
powerPreference?: 'default' | 'high-performance' | 'low-power';
premultipliedAlpha?: boolean;
preserveDrawingBuffer?: boolean;
/** Called when a context is lost */
onContextLost: (event: Event) => void;
/** Called when a context is restored */
onContextRestored: (event: Event) => void;
};

@@ -33,4 +17,4 @@ /**

*/
export declare function createBrowserContext(canvas: HTMLCanvasElement | OffscreenCanvas, props: ContextProps): WebGL2RenderingContext;
export declare function createBrowserContext(canvas: HTMLCanvasElement | OffscreenCanvas, props: ContextProps, webglContextAttributes: WebGLContextAttributes): WebGL2RenderingContext;
export {};
//# sourceMappingURL=create-browser-context.d.ts.map
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
const DEFAULT_CONTEXT_PROPS = {
powerPreference: 'high-performance', // After all, most apps are using WebGL for performance reasons
// eslint-disable-next-line no-console
onContextLost: () => console.error('WebGL context lost'),
// eslint-disable-next-line no-console
onContextRestored: () => console.info('WebGL context restored')
};
/**

@@ -16,33 +9,34 @@ * Create a WebGL context for a canvas

*/
export function createBrowserContext(canvas, props) {
props = { ...DEFAULT_CONTEXT_PROPS, ...props };
export function createBrowserContext(canvas, props, webglContextAttributes) {
// Try to extract any extra information about why context creation failed
let errorMessage = null;
const onCreateError = error => (errorMessage = error.statusMessage || errorMessage);
canvas.addEventListener('webglcontextcreationerror', onCreateError, false);
const errorMessage = null;
// const onCreateError = error => (errorMessage = error.statusMessage || errorMessage);
// Avoid multiple listeners?
// canvas.removeEventListener('webglcontextcreationerror', onCreateError, false);
// canvas.addEventListener('webglcontextcreationerror', onCreateError, false);
const webglProps = {
preserveDrawingBuffer: true,
// failIfMajorPerformanceCaveat: true,
...webglContextAttributes
};
// Create the desired context
let gl = null;
// props.failIfMajorPerformanceCaveat = true;
// We require webgl2 context
gl ||= canvas.getContext('webgl2', props);
// Software GPU
// props.failIfMajorPerformanceCaveat = false;
// if (!gl && props.webgl1) {
// gl = canvas.getContext('webgl', props);
// }
// TODO are we removing this listener before giving it a chance to fire?
canvas.removeEventListener('webglcontextcreationerror', onCreateError, false);
// Create a webgl2 context
gl ||= canvas.getContext('webgl2', webglProps);
// Creation failed with failIfMajorPerformanceCaveat - Try a Software GPU
if (!gl && !webglContextAttributes.failIfMajorPerformanceCaveat) {
webglProps.failIfMajorPerformanceCaveat = false;
gl = canvas.getContext('webgl', webglProps);
// @ts-expect-error
gl.luma ||= {};
// @ts-expect-error
gl.luma.softwareRenderer = true;
}
if (!gl) {
throw new Error(`Failed to create WebGL context: ${errorMessage || 'Unknown error'}`);
}
if (props.onContextLost) {
// Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
const { onContextLost } = props;
canvas.addEventListener('webglcontextlost', (event) => onContextLost(event), false);
}
if (props.onContextRestored) {
// Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
const { onContextRestored } = props;
canvas.addEventListener('webglcontextrestored', (event) => onContextRestored(event), false);
}
// Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
const { onContextLost, onContextRestored } = props;
canvas.addEventListener('webglcontextlost', (event) => onContextLost(event), false);
canvas.addEventListener('webglcontextrestored', (event) => onContextRestored(event), false);
return gl;

@@ -49,0 +43,0 @@ }

{
"name": "@luma.gl/webgl",
"version": "9.1.0-alpha.15",
"version": "9.1.0-alpha.16",
"description": "WebGL2 adapter for the luma.gl core API",

@@ -46,7 +46,7 @@ "type": "module",

"dependencies": {
"@luma.gl/constants": "9.1.0-alpha.15",
"@luma.gl/constants": "9.1.0-alpha.16",
"@math.gl/types": "4.1.0-alpha.3",
"@probe.gl/env": "^4.0.8"
},
"gitHead": "41af576ca655cb749a5567cf903f9e9242793c77"
"gitHead": "39eec40d12c826548b636c057fdb8572adfe611f"
}

@@ -34,3 +34,3 @@ // luma.gl

glParameters: GLParameters,
func: (device?: Device) => T
func: (_?: Device) => T
): T {

@@ -66,3 +66,3 @@ if (isObjectEmpty(parameters)) {

parameters: GLParameters,
func: (device?: Device) => T
func: (_?: Device) => T
): T {

@@ -96,3 +96,3 @@ if (isObjectEmpty(parameters)) {

parameters: Parameters,
func: (device?: Device) => T
func: (_?: Device) => T
): T {

@@ -99,0 +99,0 @@ if (isObjectEmpty(parameters)) {

@@ -493,3 +493,27 @@ // luma.gl

*/
export type ReadPixelsToArrayOptions = {
sourceX?: number;
sourceY?: number;
sourceFormat?: number;
sourceAttachment?: number;
target?: Uint8Array | Uint16Array | Float32Array;
// following parameters are auto deduced if not provided
sourceWidth?: number;
sourceHeight?: number;
sourceDepth?: number;
sourceType?: number;
};
export type ReadPixelsToBufferOptions = {
sourceX?: number;
sourceY?: number;
sourceFormat?: number;
target?: Buffer; // A new Buffer object is created when not provided.
targetByteOffset?: number; // byte offset in buffer object
// following parameters are auto deduced if not provided
sourceWidth?: number;
sourceHeight?: number;
sourceType?: number;
};
/**

@@ -508,14 +532,3 @@ * Copies data from a type or a Texture object into ArrayBuffer object.

source: Framebuffer | Texture,
options?: {
sourceX?: number;
sourceY?: number;
sourceFormat?: number;
sourceAttachment?: number;
target?: Uint8Array | Uint16Array | Float32Array;
// following parameters are auto deduced if not provided
sourceWidth?: number;
sourceHeight?: number;
sourceDepth?: number;
sourceType?: number;
}
options?: ReadPixelsToArrayOptions
): Uint8Array | Uint16Array | Float32Array {

@@ -581,13 +594,3 @@ const {

source: Framebuffer | Texture,
options?: {
sourceX?: number;
sourceY?: number;
sourceFormat?: number;
target?: Buffer; // A new Buffer object is created when not provided.
targetByteOffset?: number; // byte offset in buffer object
// following parameters are auto deduced if not provided
sourceWidth?: number;
sourceHeight?: number;
sourceType?: number;
}
options?: ReadPixelsToBufferOptions
): WEBGLBuffer {

@@ -626,7 +629,7 @@ const {

commandEncoder.copyTextureToBuffer({
source: source as Texture,
sourceTexture: source as Texture,
width: sourceWidth,
height: sourceHeight,
origin: [sourceX, sourceY],
destination: webglBufferTarget,
destinationBuffer: webglBufferTarget,
byteOffset: targetByteOffset

@@ -649,4 +652,4 @@ });

export function copyToTexture(
source: Framebuffer | Texture,
target: Texture | GL,
sourceTexture: Framebuffer | Texture,
destinationTexture: Texture | GL,
options?: {

@@ -681,3 +684,3 @@ sourceX?: number;

const {framebuffer, deleteFramebuffer} = getFramebuffer(source);
const {framebuffer, deleteFramebuffer} = getFramebuffer(sourceTexture);
// assert(framebuffer);

@@ -699,4 +702,4 @@ const webglFramebuffer = framebuffer;

let textureTarget: GL;
if (target instanceof WEBGLTexture) {
texture = target;
if (destinationTexture instanceof WEBGLTexture) {
texture = destinationTexture;
width = Number.isFinite(width) ? width : texture.width;

@@ -703,0 +706,0 @@ height = Number.isFinite(height) ? height : texture.height;

@@ -76,4 +76,4 @@ // luma.gl

function _copyBufferToBuffer(device: WebGLDevice, options: CopyBufferToBufferOptions): void {
const source = options.source as WEBGLBuffer;
const destination = options.destination as WEBGLBuffer;
const source = options.sourceBuffer as WEBGLBuffer;
const destination = options.destinationBuffer as WEBGLBuffer;

@@ -110,3 +110,3 @@ // {In WebGL2 we can p}erform the copy on the GPU

/** Texture to copy to/from. */
source,
sourceTexture,
/** Mip-map level of the texture to copy to/from. (Default 0) */

@@ -118,5 +118,5 @@ mipLevel = 0,

/** Width to copy */
width = options.source.width,
width = options.sourceTexture.width,
/** Height to copy */
height = options.source.height,
height = options.sourceTexture.height,
depthOrArrayLayers = 0,

@@ -127,3 +127,3 @@ /** Defines the origin of the copy - the minimum corner of the texture sub-region to copy to/from. */

/** Destination buffer */
destination,
destinationBuffer,
/** Offset, in bytes, from the beginning of the buffer to the start of the image data (default 0) */

@@ -146,3 +146,3 @@ byteOffset = 0,

if (aspect !== 'all') {
throw new Error('not supported');
throw new Error('aspect not supported in WebGL');
}

@@ -156,6 +156,6 @@

// Asynchronous read (PIXEL_PACK_BUFFER) is WebGL2 only feature
const {framebuffer, destroyFramebuffer} = getFramebuffer(source);
const {framebuffer, destroyFramebuffer} = getFramebuffer(sourceTexture);
let prevHandle: WebGLFramebuffer | null | undefined;
try {
const webglBuffer = destination as WEBGLBuffer;
const webglBuffer = destinationBuffer as WEBGLBuffer;
const sourceWidth = width || framebuffer.width;

@@ -229,3 +229,3 @@ const sourceHeight = height || framebuffer.height;

/** Texture to copy to/from. */
source,
sourceTexture,
/** Mip-map level of the texture to copy to (Default 0) */

@@ -242,3 +242,3 @@ destinationMipLevel = 0,

/** Texture to copy to/from. */
destination
destinationTexture
/** Mip-map level of the texture to copy to/from. (Default 0) */

@@ -253,8 +253,8 @@ // destinationMipLevel = options.mipLevel,

let {
width = options.destination.width,
height = options.destination.height
width = options.destinationTexture.width,
height = options.destinationTexture.height
// depthOrArrayLayers = 0
} = options;
const {framebuffer, destroyFramebuffer} = getFramebuffer(source);
const {framebuffer, destroyFramebuffer} = getFramebuffer(sourceTexture);
const [sourceX, sourceY] = origin;

@@ -273,4 +273,4 @@ const [destinationX, destinationY, destinationZ] = destinationOrigin;

let textureTarget: GL;
if (destination instanceof WEBGLTexture) {
texture = destination;
if (destinationTexture instanceof WEBGLTexture) {
texture = destinationTexture;
width = Number.isFinite(width) ? width : texture.width;

@@ -277,0 +277,0 @@ height = Number.isFinite(height) ? height : texture.height;

@@ -113,8 +113,8 @@ // luma.gl

const binding =
this.shaderLayout.bindings.find(binding => binding.name === name) ||
this.shaderLayout.bindings.find(binding => binding.name === `${name}Uniforms`);
this.shaderLayout.bindings.find(binding_ => binding_.name === name) ||
this.shaderLayout.bindings.find(binding_ => binding_.name === `${name}Uniforms`);
if (!binding) {
const validBindings = this.shaderLayout.bindings
.map(binding => `"${binding.name}"`)
.map(binding_ => `"${binding_.name}"`)
.join(', ');

@@ -321,3 +321,3 @@ if (!options?.disableWarnings) {

/** Report link status. First, check for shader compilation failures if linking fails */
_reportLinkStatus(status: 'success' | 'linking' | 'validation') {
async _reportLinkStatus(status: 'success' | 'linking' | 'validation'): Promise<void> {
switch (status) {

@@ -329,11 +329,26 @@ case 'success':

// First check for shader compilation failures if linking fails
if (this.vs.compilationStatus === 'error') {
this.vs.debugShader();
throw new Error(`Error during compilation of shader ${this.vs.id}`);
switch (this.vs.compilationStatus) {
case 'error':
this.vs.debugShader();
throw new Error(`Error during compilation of shader ${this.vs.id}`);
case 'pending':
this.vs.asyncCompilationStatus.then(() => this.vs.debugShader());
break;
case 'success':
break;
}
if (this.fs?.compilationStatus === 'error') {
this.fs.debugShader();
throw new Error(`Error during compilation of shader ${this.fs.id}`);
switch (this.fs?.compilationStatus) {
case 'error':
this.fs.debugShader();
throw new Error(`Error during compilation of shader ${this.fs.id}`);
case 'pending':
this.fs.asyncCompilationStatus.then(() => this.fs.debugShader());
break;
case 'success':
break;
}
throw new Error(`Error during ${status}: ${this.device.gl.getProgramInfoLog(this.handle)}`);
const linkErrorLog = this.device.gl.getProgramInfoLog(this.handle);
throw new Error(`Error during ${status}: ${linkErrorLog}`);
}

@@ -340,0 +355,0 @@ }

@@ -42,2 +42,6 @@ // luma.gl

get asyncCompilationStatus(): Promise<'pending' | 'success' | 'error'> {
return this._waitForCompilationComplete().then(() => this.compilationStatus);
}
override async getCompilationInfo(): Promise<readonly CompilerMessage[]> {

@@ -49,4 +53,4 @@ await this._waitForCompilationComplete();

override getCompilationInfoSync(): readonly CompilerMessage[] {
const log = this.device.gl.getShaderInfoLog(this.handle);
return log ? parseShaderCompilerLog(log) : [];
const shaderLog = this.device.gl.getShaderInfoLog(this.handle);
return shaderLog ? parseShaderCompilerLog(shaderLog) : [];
}

@@ -64,5 +68,3 @@

protected async _compile(source: string): Promise<void> {
const addGLSLVersion = (source: string) =>
source.startsWith('#version ') ? source : `#version 300 es\n${source}`;
source = addGLSLVersion(source);
source = source.startsWith('#version ') ? source : `#version 300 es\n${source}`;

@@ -69,0 +71,0 @@ const {gl} = this.device;

@@ -5,3 +5,3 @@ // luma.gl

import {Adapter, Device, DeviceProps, CanvasContext, log} from '@luma.gl/core';
import {Adapter, Device, DeviceProps, log} from '@luma.gl/core';
import {WebGLDevice} from './webgl-device';

@@ -56,3 +56,3 @@ import {enforceWebGL2} from '../context/polyfills/polyfill-webgl1-extensions';

}
return new WebGLDevice({gl: gl as WebGL2RenderingContext});
return new WebGLDevice({_handle: gl as WebGL2RenderingContext});
}

@@ -66,16 +66,10 @@

// Load webgl and spector debug scripts from CDN if requested
if (props.debug) {
if (props.debugWebGL) {
promises.push(loadWebGLDeveloperTools());
}
if (props.debugWithSpectorJS) {
if (props.debugSpectorJS) {
promises.push(loadSpectorJS(props));
}
// Wait for page to load: if canvas is a string we need to query the DOM for the canvas element.
// We only wait when props.canvas is string to avoids setting the global page onload callback unless necessary.
if (typeof props.canvas === 'string') {
promises.push(CanvasContext.pageLoaded);
}
// Wait for all the loads to settle before creating the context.

@@ -90,4 +84,2 @@ // The Device.create() functions are async, so in contrast to the constructor, we can `await` here.

log.probe(LOG_LEVEL + 1, 'DOM is loaded')();
const device = new WebGLDevice(props);

@@ -94,0 +86,0 @@

@@ -15,24 +15,3 @@ // luma.gl

VertexArray,
VertexArrayProps
} from '@luma.gl/core';
import {Device, CanvasContext, log} from '@luma.gl/core';
import type {GLExtensions} from '@luma.gl/constants';
import {WebGLStateTracker} from '../context/state-tracker/webgl-state-tracker';
import {createBrowserContext} from '../context/helpers/create-browser-context';
import {getDeviceInfo} from './device-helpers/webgl-device-info';
import {WebGLDeviceFeatures} from './device-helpers/webgl-device-features';
import {WebGLDeviceLimits} from './device-helpers/webgl-device-limits';
import {WebGLCanvasContext} from './webgl-canvas-context';
import type {Spector} from '../context/debug/spector-types';
import {initializeSpectorJS} from '../context/debug/spector';
import {makeDebugContext} from '../context/debug/webgl-developer-tools';
import {
isTextureFormatSupported,
isTextureFormatRenderable,
isTextureFormatFilterable
} from './converters/texture-formats';
import {uid} from '../utils/uid';
// WebGL classes
import type {
VertexArrayProps,
BufferProps,

@@ -59,2 +38,19 @@ ShaderProps,

} from '@luma.gl/core';
import {Device, CanvasContext, log} from '@luma.gl/core';
import type {GLExtensions} from '@luma.gl/constants';
import {WebGLStateTracker} from '../context/state-tracker/webgl-state-tracker';
import {createBrowserContext} from '../context/helpers/create-browser-context';
import {getDeviceInfo} from './device-helpers/webgl-device-info';
import {WebGLDeviceFeatures} from './device-helpers/webgl-device-features';
import {WebGLDeviceLimits} from './device-helpers/webgl-device-limits';
import {WebGLCanvasContext} from './webgl-canvas-context';
import type {Spector} from '../context/debug/spector-types';
import {initializeSpectorJS} from '../context/debug/spector';
import {makeDebugContext} from '../context/debug/webgl-developer-tools';
import {
isTextureFormatSupported,
isTextureFormatRenderable,
isTextureFormatFilterable
} from './converters/texture-formats';
import {uid} from '../utils/uid';

@@ -125,5 +121,11 @@ import {WEBGLBuffer} from './resources/webgl-buffer';

// WebGL requires a canvas to be created before creating the context
if (!props.createCanvasContext) {
throw new Error('WebGLDevice requires props.createCanvasContext to be set');
}
const canvasContextProps = props.createCanvasContext === true ? {} : props.createCanvasContext;
// If attaching to an already attached context, return the attached device
// @ts-expect-error device is attached to context
const device: WebGLDevice | undefined = props.gl?.device;
let device: WebGLDevice | undefined = canvasContextProps.canvas?.gl?.device;
if (device) {

@@ -134,4 +136,3 @@ throw new Error(`WebGL context already attached to device ${device.id}`);

// Create and instrument context
const canvas = props.gl?.canvas || props.canvas;
this.canvasContext = new WebGLCanvasContext(this, {...props, canvas});
this.canvasContext = new WebGLCanvasContext(this, canvasContextProps);

@@ -142,16 +143,38 @@ this.lost = new Promise<{reason: 'destroyed'; message: string}>(resolve => {

this.handle = createBrowserContext(this.canvasContext.canvas, {
...props,
onContextLost: (event: Event) =>
this._resolveContextLost?.({
reason: 'destroyed',
message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
})
});
this.gl = this.handle;
const webglContextAttributes: WebGLContextAttributes = {...props.webgl};
// Copy props from CanvasContextProps
if (canvasContextProps.alphaMode === 'premultiplied') {
webglContextAttributes.premultipliedAlpha = true;
}
if (props.powerPreference !== undefined) {
webglContextAttributes.powerPreference = props.powerPreference;
}
if (!this.handle) {
const gl = createBrowserContext(
this.canvasContext.canvas,
{
onContextLost: (event: Event) =>
this._resolveContextLost?.({
reason: 'destroyed',
message: 'Entered sleep mode, or too many apps or browser tabs are using the GPU.'
}),
// eslint-disable-next-line no-console
onContextRestored: (event: Event) => console.log('WebGL context restored')
},
webglContextAttributes
);
if (!gl) {
throw new Error('WebGL context creation failed');
}
// @ts-expect-error device is attached to context
device = gl.device;
if (device) {
throw new Error(`WebGL context already attached to device ${device.id}`);
}
this.handle = gl;
this.gl = gl;
// Add spector debug instrumentation to context

@@ -169,4 +192,8 @@ // We need to trust spector integration to decide if spector should be initialized

this.limits = new WebGLDeviceLimits(this.gl);
this.features = new WebGLDeviceFeatures(this.gl, this._extensions, this.props.disabledFeatures);
if (this.props.initalizeFeatures) {
this.features = new WebGLDeviceFeatures(
this.gl,
this._extensions,
this.props._disabledFeatures
);
if (this.props._initializeFeatures) {
this.features.initializeFeatures();

@@ -184,4 +211,4 @@ }

// DEBUG contexts: Add luma debug instrumentation to the context, force log level to at least 1
if (props.debug) {
this.gl = makeDebugContext(this.gl, {...props, throwOnError: true});
if (props.debugWebGL) {
this.gl = makeDebugContext(this.gl, {...props});
this.debug = true;

@@ -222,3 +249,3 @@ log.level = Math.max(log.level, 1);

createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WEBGLBuffer {
const newProps = this._getBufferProps(props);
const newProps = this._normalizeBufferProps(props);
return new WEBGLBuffer(this, newProps);

@@ -225,0 +252,0 @@ }

// Forked from https://github.com/BabylonJS/Spector.js/blob/master/dist/spector.d.ts
/* eslint-disable camelcase */
/* eslint-disable camelcase, no-shadow */

@@ -4,0 +4,0 @@ interface IEvent<T> {

@@ -8,10 +8,10 @@ // luma.gl

import {Spector} from './spector-types';
import type {Spector} from './spector-types';
/** Spector debug initialization options */
type SpectorProps = {
/** Whether spector is enabled */
debugWithSpectorJS?: boolean;
/** Whether spector.js is enabled */
debugSpectorJS?: boolean;
/** URL to load spector script from. Typically a CDN URL */
spectorUrl?: string;
debugSpectorJSUrl?: string;
/** Canvas to monitor */

@@ -33,7 +33,7 @@ gl?: WebGL2RenderingContext;

export const DEFAULT_SPECTOR_PROPS: Required<SpectorProps> = {
debugWithSpectorJS: log.get('spector') || log.get('spectorjs'),
debugSpectorJS: log.get('debug-spectorjs'),
// https://github.com/BabylonJS/Spector.js#basic-usage
// https://forum.babylonjs.com/t/spectorcdn-is-temporarily-off/48241
// spectorUrl: 'https://spectorcdn.babylonjs.com/spector.bundle.js';
spectorUrl: 'https://cdn.jsdelivr.net/npm/spectorjs@0.9.30/dist/spector.bundle.js',
debugSpectorJSUrl: 'https://cdn.jsdelivr.net/npm/spectorjs@0.9.30/dist/spector.bundle.js',
gl: undefined!

@@ -43,6 +43,6 @@ };

/** Loads spector from CDN if not already installed */
export async function loadSpectorJS(props: {spectorUrl?: string}): Promise<void> {
export async function loadSpectorJS(props: {debugSpectorJSUrl?: string}): Promise<void> {
if (!globalThis.SPECTOR) {
try {
await loadScript(props.spectorUrl || DEFAULT_SPECTOR_PROPS.spectorUrl);
await loadScript(props.debugSpectorJSUrl || DEFAULT_SPECTOR_PROPS.debugSpectorJSUrl);
} catch (error) {

@@ -56,3 +56,3 @@ log.warn(String(error));

props = {...DEFAULT_SPECTOR_PROPS, ...props};
if (!props.debugWithSpectorJS) {
if (!props.debugSpectorJS) {
return null;

@@ -63,4 +63,4 @@ }

log.probe(LOG_LEVEL, 'SPECTOR found and initialized. Start with `luma.spector.displayUI()`')();
const {Spector} = globalThis.SPECTOR as any;
spector = new Spector();
const {Spector: SpectorJS} = globalThis.SPECTOR as any;
spector = new SpectorJS();
if (globalThis.luma) {

@@ -67,0 +67,0 @@ (globalThis.luma as any).spector = spector;

@@ -14,14 +14,5 @@ // luma.gl

type DebugContextProps = {
debug?: boolean;
throwOnError?: boolean;
break?: string[];
debugWebGL?: boolean;
};
// const DEFAULT_DEBUG_CONTEXT_PROPS: Required<DebugContextProps> = {
// debug: true,
// throwOnError: false,
// break: [],
// webgl2: false,
// }
type ContextData = {

@@ -64,3 +55,3 @@ realContext?: WebGL2RenderingContext;

): WebGL2RenderingContext {
return props.debug ? getDebugContext(gl, props) : getRealContext(gl);
return props.debugWebGL ? getDebugContext(gl, props) : getRealContext(gl);
}

@@ -141,5 +132,3 @@

debugger; // eslint-disable-line
if (props.throwOnError) {
throw new Error(message);
}
// throw new Error(message);
}

@@ -159,24 +148,9 @@

// If array of breakpoint strings supplied, check if any of them is contained in current GLEnum function
if (props.break && props.break.length > 0) {
functionString = functionString || getFunctionString(functionName, functionArgs);
const isBreakpoint = props.break.every(
(breakOn: string) => functionString.indexOf(breakOn) !== -1
);
if (isBreakpoint) {
debugger; // eslint-disable-line
}
}
for (const arg of functionArgs) {
if (arg === undefined) {
functionString = functionString || getFunctionString(functionName, functionArgs);
if (props.throwOnError) {
throw new Error(`Undefined argument: ${functionString}`);
} else {
log.error(`Undefined argument: ${functionString}`)();
debugger; // eslint-disable-line
}
debugger; // eslint-disable-line
// throw new Error(`Undefined argument: ${functionString}`);
}
}
}

@@ -8,35 +8,11 @@ // luma.gl

* @param onContextLost
* @param onContextRestored
*
* BROWSER CONTEXT PARAMETERS
* @param debug Instrument context (at the expense of performance).
* @param alpha Default render target has an alpha buffer.
* @param depth Default render target has a depth buffer of at least 16 bits.
* @param stencil Default render target has a stencil buffer of at least 8 bits.
* @param antialias Boolean that indicates whether or not to perform anti-aliasing.
* @param premultipliedAlpha Boolean that indicates that the page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
* @param preserveDrawingBuffer Default render target buffers will not be automatically cleared and will preserve their values until cleared or overwritten
* @param failIfMajorPerformanceCaveat Do not create if the system performance is low.
* @param onContextRestored *
*/
type ContextProps = {
onContextLost?: (event: Event) => void;
onContextRestored?: (event: Event) => void;
alpha?: boolean; // indicates if the canvas contains an alpha buffer.
desynchronized?: boolean; // hints the user agent to reduce the latency by desynchronizing the canvas paint cycle from the event loop
antialias?: boolean; // indicates whether or not to perform anti-aliasing.
depth?: boolean; // indicates that the drawing buffer has a depth buffer of at least 16 bits.
failIfMajorPerformanceCaveat?: boolean; // indicates if a context will be created if the system performance is low or if no hardware GPU is available.
powerPreference?: 'default' | 'high-performance' | 'low-power';
premultipliedAlpha?: boolean; // page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
preserveDrawingBuffer?: boolean; // buffers will not be cleared and will preserve their values until cleared or overwritten by the author.
/** Called when a context is lost */
onContextLost: (event: Event) => void;
/** Called when a context is restored */
onContextRestored: (event: Event) => void;
};
const DEFAULT_CONTEXT_PROPS: ContextProps = {
powerPreference: 'high-performance', // After all, most apps are using WebGL for performance reasons
// eslint-disable-next-line no-console
onContextLost: () => console.error('WebGL context lost'),
// eslint-disable-next-line no-console
onContextRestored: () => console.info('WebGL context restored')
};
/**

@@ -49,30 +25,35 @@ * Create a WebGL context for a canvas

canvas: HTMLCanvasElement | OffscreenCanvas,
props: ContextProps
props: ContextProps,
webglContextAttributes: WebGLContextAttributes
): WebGL2RenderingContext {
props = {...DEFAULT_CONTEXT_PROPS, ...props};
// Try to extract any extra information about why context creation failed
let errorMessage = null;
const onCreateError = error => (errorMessage = error.statusMessage || errorMessage);
canvas.addEventListener('webglcontextcreationerror', onCreateError, false);
const errorMessage = null;
// const onCreateError = error => (errorMessage = error.statusMessage || errorMessage);
// Avoid multiple listeners?
// canvas.removeEventListener('webglcontextcreationerror', onCreateError, false);
// canvas.addEventListener('webglcontextcreationerror', onCreateError, false);
const webglProps: WebGLContextAttributes = {
preserveDrawingBuffer: true,
// failIfMajorPerformanceCaveat: true,
...webglContextAttributes
};
// Create the desired context
let gl: WebGL2RenderingContext | null = null;
// props.failIfMajorPerformanceCaveat = true;
// Create a webgl2 context
gl ||= canvas.getContext('webgl2', webglProps);
// We require webgl2 context
gl ||= canvas.getContext('webgl2', props) as WebGL2RenderingContext;
// Creation failed with failIfMajorPerformanceCaveat - Try a Software GPU
if (!gl && !webglContextAttributes.failIfMajorPerformanceCaveat) {
webglProps.failIfMajorPerformanceCaveat = false;
gl = canvas.getContext('webgl', webglProps) as WebGL2RenderingContext;
// @ts-expect-error
gl.luma ||= {};
// @ts-expect-error
gl.luma.softwareRenderer = true;
}
// Software GPU
// props.failIfMajorPerformanceCaveat = false;
// if (!gl && props.webgl1) {
// gl = canvas.getContext('webgl', props);
// }
// TODO are we removing this listener before giving it a chance to fire?
canvas.removeEventListener('webglcontextcreationerror', onCreateError, false);
if (!gl) {

@@ -82,16 +63,10 @@ throw new Error(`Failed to create WebGL context: ${errorMessage || 'Unknown error'}`);

if (props.onContextLost) {
// Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
const {onContextLost} = props;
canvas.addEventListener('webglcontextlost', (event: Event) => onContextLost(event), false);
}
if (props.onContextRestored) {
// Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
const {onContextRestored} = props;
canvas.addEventListener(
'webglcontextrestored',
(event: Event) => onContextRestored(event),
false
);
}
// Carefully extract and wrap callbacks to prevent addEventListener from rebinding them.
const {onContextLost, onContextRestored} = props;
canvas.addEventListener('webglcontextlost', (event: Event) => onContextLost(event), false);
canvas.addEventListener(
'webglcontextrestored',
(event: Event) => onContextRestored(event),
false
);

@@ -98,0 +73,0 @@ return gl;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc