feat(webgl): typings (#1436)

This commit is contained in:
Ib Green 2021-01-14 17:33:57 -08:00 committed by GitHub
parent 269e690c5c
commit a046ef5025
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 260 additions and 176 deletions

View File

@ -17,6 +17,10 @@ export class MiniAnimationLoop {
return Promise.resolve();
}
onInitialize() {}
onRender() {}
_setDisplay() {}
_getCanvas(props = {}) {

View File

@ -1,8 +1,17 @@
import Resource from '@luma.gl/webgl/classes/resource';
export default class Framebuffer extends Resource {
static readonly STATUS: number[];
readonly width: number;
readonly height: number;
readonly attachments: any[];
/**
* Support
* @param gl
* @param options.colorBufferFloat Whether floating point textures can be rendered and read
* @param options.colorBufferHalfFloat Whether half float textures can be rendered and read
*/
static isSupported(
gl: WebGLRenderingContext,
options?: {
@ -26,9 +35,9 @@ export default class Framebuffer extends Resource {
depth?: boolean;
stencil?: boolean;
check?: boolean;
readBuffer: any;
drawBuffers: any;
}): void;
readBuffer?: any;
drawBuffers?: any;
}): this;
// @ts-ignore
delete(): void;
update(options?: {

View File

@ -1,6 +1,5 @@
import GL from '@luma.gl/constants';
import {isWebGL2, assertWebGL2Context, log} from '@luma.gl/gltools';
import {getWebGL2Context, assertWebGL2Context, log} from '@luma.gl/gltools';
import Resource from './resource';
import Texture2D from './texture-2d';
import Renderbuffer from './renderbuffer';
@ -8,21 +7,17 @@ import {clear, clearBuffer} from './clear';
import {copyToDataUrl} from './copy-and-blit.js';
import {getFeatures} from '../features';
import {getKey} from '../webgl-utils';
import {assert} from '../utils';
const ERR_MULTIPLE_RENDERTARGETS = 'Multiple render targets not supported';
export default class Framebuffer extends Resource {
static isSupported(
gl,
{
static isSupported(gl, options = {}) {
const {
colorBufferFloat, // Whether floating point textures can be rendered and read
colorBufferHalfFloat // Whether half float textures can be rendered and read
} = {}
) {
} = options;
let supported = true;
if (colorBufferFloat) {
@ -65,11 +60,13 @@ export default class Framebuffer extends Resource {
}
get MAX_COLOR_ATTACHMENTS() {
return this.gl.getParameter(this.gl.MAX_COLOR_ATTACHMENTS);
const gl2 = assertWebGL2Context(this.gl);
return gl2.getParameter(gl2.MAX_COLOR_ATTACHMENTS);
}
get MAX_DRAW_BUFFERS() {
return this.gl.getParameter(this.gl.MAX_DRAW_BUFFERS);
const gl2 = assertWebGL2Context(this.gl);
return gl2.getParameter(gl2.MAX_DRAW_BUFFERS);
}
constructor(gl, opts = {}) {
@ -117,8 +114,8 @@ export default class Framebuffer extends Resource {
depth = true,
stencil = false,
check = true,
readBuffer,
drawBuffers
readBuffer = undefined,
drawBuffers = undefined
}) {
assert(width >= 0 && height >= 0, 'Width and height need to be integers');
@ -152,7 +149,9 @@ export default class Framebuffer extends Resource {
resource.delete();
}
super.delete();
return this;
}
update({
attachments = {},
readBuffer,
@ -171,13 +170,15 @@ export default class Framebuffer extends Resource {
if (drawBuffers) {
this._setDrawBuffers(drawBuffers);
}
// @ts-ignore
gl.bindFramebuffer(GL.FRAMEBUFFER, prevHandle || null);
return this;
}
// Attachment resize is expected to be a noop if size is same
resize({width, height} = {}) {
resize(options = {}) {
let {width, height} = options;
// for default framebuffer, just update the stored size
if (this.handle === null) {
assert(width === undefined && height === undefined);
@ -247,6 +248,7 @@ export default class Framebuffer extends Resource {
}
}
// @ts-ignore
this.gl.bindFramebuffer(GL.FRAMEBUFFER, prevHandle || null);
// Assign to attachments and remove any nulls to get a clean attachment map
@ -271,11 +273,14 @@ export default class Framebuffer extends Resource {
const {gl} = this;
const prevHandle = gl.bindFramebuffer(GL.FRAMEBUFFER, this.handle);
const status = gl.checkFramebufferStatus(GL.FRAMEBUFFER);
// @ts-ignore
gl.bindFramebuffer(GL.FRAMEBUFFER, prevHandle || null);
return status;
}
clear({color, depth, stencil, drawBuffers = []} = {}) {
clear(options = {}) {
const {color, depth, stencil, drawBuffers = []} = options;
// Bind framebuffer and delegate to global clear functions
const prevHandle = this.gl.bindFramebuffer(GL.FRAMEBUFFER, this.handle);
@ -287,6 +292,7 @@ export default class Framebuffer extends Resource {
clearBuffer({drawBuffer, value});
});
// @ts-ignore
this.gl.bindFramebuffer(GL.FRAMEBUFFER, prevHandle || null);
return this;
@ -357,16 +363,18 @@ export default class Framebuffer extends Resource {
// signals to the GL that it need not preserve all pixels of a specified region of the framebuffer
invalidate({attachments = [], x = 0, y = 0, width, height}) {
const {gl} = this;
assertWebGL2Context(gl);
const prevHandle = gl.bindFramebuffer(GL.READ_FRAMEBUFFER, this.handle);
const gl2 = assertWebGL2Context(this.gl);
const prevHandle = gl2.bindFramebuffer(GL.READ_FRAMEBUFFER, this.handle);
const invalidateAll = x === 0 && y === 0 && width === undefined && height === undefined;
if (invalidateAll) {
gl.invalidateFramebuffer(GL.READ_FRAMEBUFFER, attachments);
gl2.invalidateFramebuffer(GL.READ_FRAMEBUFFER, attachments);
} else {
gl.invalidateFramebuffer(GL.READ_FRAMEBUFFER, attachments, x, y, width, height);
// TODO - why does type checking fail on this line
// @ts-ignore
gl2.invalidateFramebuffer(GL.READ_FRAMEBUFFER, attachments, x, y, width, height);
}
gl.bindFramebuffer(GL.READ_FRAMEBUFFER, prevHandle);
// @ts-ignore
gl2.bindFramebuffer(GL.READ_FRAMEBUFFER, prevHandle);
return this;
}
@ -380,6 +388,7 @@ export default class Framebuffer extends Resource {
this.gl.bindFramebuffer(GL.FRAMEBUFFER, null);
}
if (keys && value > 1000) {
// @ts-ignore
value = getKey(this.gl, value);
}
return value;
@ -388,6 +397,7 @@ export default class Framebuffer extends Resource {
getAttachmentParameters(
attachment = GL.COLOR_ATTACHMENT0,
keys,
// @ts-ignore
parameters = this.constructor.ATTACHMENT_PARAMETERS || []
) {
const values = {};
@ -427,7 +437,7 @@ export default class Framebuffer extends Resource {
return this;
}
message = message || `Framebuffer ${this.id}`;
const image = copyToDataUrl(this, {maxHeight: 100});
const image = copyToDataUrl(this, {targetMaxHeight: 100});
log.image({logLevel, message, image}, message)();
return this;
}
@ -551,7 +561,8 @@ export default class Framebuffer extends Resource {
switch (texture.target) {
case GL.TEXTURE_2D_ARRAY:
case GL.TEXTURE_3D:
gl.framebufferTextureLayer(GL.FRAMEBUFFER, attachment, texture.target, level, layer);
const gl2 = assertWebGL2Context(gl);
gl2.framebufferTextureLayer(GL.FRAMEBUFFER, attachment, texture.target, level, layer);
break;
case GL.TEXTURE_CUBE_MAP:
@ -574,9 +585,9 @@ export default class Framebuffer extends Resource {
// Expects framebuffer to be bound
_setReadBuffer(readBuffer) {
const {gl} = this;
if (isWebGL2(gl)) {
gl.readBuffer(readBuffer);
const gl2 = getWebGL2Context(this.gl);
if (gl2) {
gl2.readBuffer(readBuffer);
} else {
// Setting to color attachment 0 is a noop, so allow it in WebGL1
assert(
@ -590,10 +601,12 @@ export default class Framebuffer extends Resource {
// Expects framebuffer to be bound
_setDrawBuffers(drawBuffers) {
const {gl} = this;
if (isWebGL2(gl)) {
gl.drawBuffers(drawBuffers);
const gl2 = assertWebGL2Context(gl);
if (gl2) {
gl2.drawBuffers(drawBuffers);
} else {
const ext = gl.getExtension('WEBGL.draw_buffers');
// TODO - is this not handled by polyfills?
const ext = gl.getExtension('WEBGL_draw_buffers');
if (ext) {
ext.drawBuffersWEBGL(drawBuffers);
} else {
@ -663,6 +676,7 @@ function mapIndexToCubeMapFace(layer) {
// Get a string describing the framebuffer error if installed
function _getFrameBufferStatus(status) {
// Use error mapping if installed
// @ts-ignore
const STATUS = Framebuffer.STATUS || {};
return STATUS[status] || `Framebuffer error ${status}`;
}

View File

@ -75,7 +75,7 @@ export default class Program extends Resource {
if (varyings && varyings.length > 0) {
assertWebGL2Context(this.gl);
this.varyings = varyings;
this.gl.transformFeedbackVaryings(this.handle, varyings, bufferMode);
this.gl2.transformFeedbackVaryings(this.handle, varyings, bufferMode);
}
this._compileAndLink();
@ -172,13 +172,13 @@ export default class Program extends Resource {
withParameters(this.gl, parameters, () => {
// TODO - Use polyfilled WebGL2RenderingContext instead of ANGLE extension
if (isIndexed && isInstanced) {
this.gl.drawElementsInstanced(drawMode, vertexCount, indexType, offset, instanceCount);
this.gl2.drawElementsInstanced(drawMode, vertexCount, indexType, offset, instanceCount);
} else if (isIndexed && isWebGL2(this.gl) && !isNaN(start) && !isNaN(end)) {
this.gl.drawRangeElements(drawMode, start, end, vertexCount, indexType, offset);
this.gl2.drawRangeElements(drawMode, start, end, vertexCount, indexType, offset);
} else if (isIndexed) {
this.gl.drawElements(drawMode, vertexCount, indexType, offset);
} else if (isInstanced) {
this.gl.drawArraysInstanced(drawMode, offset, vertexCount, instanceCount);
this.gl2.drawArraysInstanced(drawMode, offset, vertexCount, instanceCount);
} else {
this.gl.drawArrays(drawMode, offset, vertexCount);
}
@ -289,9 +289,11 @@ export default class Program extends Resource {
const type = this.gl.getShaderParameter(this.handle, GL.SHADER_TYPE);
switch (type) {
case GL.VERTEX_SHADER:
// @ts-ignore
opts.vs = new VertexShader({handle: shaderHandle});
break;
case GL.FRAGMENT_SHADER:
// @ts-ignore
opts.fs = new FragmentShader({handle: shaderHandle});
break;
default:
@ -330,6 +332,7 @@ export default class Program extends Resource {
log.timeEnd(LOG_PROGRAM_PERF_PRIORITY, `linkProgram for ${this._getName()}`)();
// Avoid checking program linking error in production
// @ts-ignore
if (gl.debug || log.level > 0) {
const linked = gl.getProgramParameter(this.handle, gl.LINK_STATUS);
if (!linked) {
@ -352,13 +355,13 @@ export default class Program extends Resource {
this._uniformCount = this._getParameter(GL.ACTIVE_UNIFORMS);
for (let i = 0; i < this._uniformCount; i++) {
const info = this.gl.getActiveUniform(this.handle, i);
const {name, isArray} = parseUniformName(info.name);
const {name} = parseUniformName(info.name);
let location = gl.getUniformLocation(this.handle, name);
this._uniformSetters[name] = getUniformSetter(gl, location, info, isArray);
this._uniformSetters[name] = getUniformSetter(gl, location, info);
if (info.size > 1) {
for (let l = 0; l < info.size; l++) {
location = gl.getUniformLocation(this.handle, `${name}[${l}]`);
this._uniformSetters[`${name}[${l}]`] = getUniformSetter(gl, location, info, isArray);
this._uniformSetters[`${name}[${l}]`] = getUniformSetter(gl, location, info);
}
}
}
@ -371,23 +374,23 @@ export default class Program extends Resource {
// https://
// developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/getActiveUniforms
getActiveUniforms(uniformIndices, pname) {
return this.gl.getActiveUniforms(this.handle, uniformIndices, pname);
return this.gl2.getActiveUniforms(this.handle, uniformIndices, pname);
}
// Retrieves the index of a uniform block
getUniformBlockIndex(blockName) {
return this.gl.getUniformBlockIndex(this.handle, blockName);
return this.gl2.getUniformBlockIndex(this.handle, blockName);
}
// Retrieves information about an active uniform block (`blockIndex`)
// https://
// developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/getActiveUniformBlockParameter
getActiveUniformBlockParameter(blockIndex, pname) {
return this.gl.getActiveUniformBlockParameter(this.handle, blockIndex, pname);
return this.gl2.getActiveUniformBlockParameter(this.handle, blockIndex, pname);
}
// Binds a uniform block (`blockIndex`) to a specific binding point (`blockBinding`)
uniformBlockBinding(blockIndex, blockBinding) {
this.gl.uniformBlockBinding(this.handle, blockIndex, blockBinding);
this.gl2.uniformBlockBinding(this.handle, blockIndex, blockBinding);
}
}

View File

@ -80,7 +80,7 @@ export default class Query extends Resource {
}
this.target = target;
this.gl.beginQuery(this.target, this.handle);
this.gl2.beginQuery(this.target, this.handle);
return this;
}
@ -93,7 +93,7 @@ export default class Query extends Resource {
}
if (this.target) {
this.gl.endQuery(this.target);
this.gl2.endQuery(this.target);
this.target = null;
this._queryPending = true;
}
@ -106,7 +106,7 @@ export default class Query extends Resource {
return false;
}
const resultAvailable = this.gl.getQueryParameter(this.handle, GL_QUERY_RESULT_AVAILABLE);
const resultAvailable = this.gl2.getQueryParameter(this.handle, GL_QUERY_RESULT_AVAILABLE);
if (resultAvailable) {
this._queryPending = false;
}
@ -115,12 +115,12 @@ export default class Query extends Resource {
// Timing query is disjoint, i.e. results are invalid
isTimerDisjoint() {
return this.gl.getParameter(GL_GPU_DISJOINT_EXT);
return this.gl2.getParameter(GL_GPU_DISJOINT_EXT);
}
// Returns query result.
getResult() {
return this.gl.getQueryParameter(this.handle, GL_QUERY_RESULT);
return this.gl2.getQueryParameter(this.handle, GL_QUERY_RESULT);
}
// Returns the query result, converted to milliseconds to match JavaScript conventions.
@ -156,10 +156,10 @@ export default class Query extends Resource {
}
_createHandle() {
return Query.isSupported(this.gl) ? this.gl.createQuery() : null;
return Query.isSupported(this.gl) ? this.gl2.createQuery() : null;
}
_deleteHandle() {
this.gl.deleteQuery(this.handle);
this.gl2.deleteQuery(this.handle);
}
}

View File

@ -2,7 +2,7 @@ import GL from '@luma.gl/constants';
// Define local extension strings to optimize minification
// const SRGB = 'EXT_sRGB';
// const EXT_FLOAT_WEBGL1 = 'WEBGL.color_buffer_float';
// const EXT_FLOAT_WEBGL1 = 'WEBGL_color_buffer_float';
const EXT_FLOAT_WEBGL2 = 'EXT_color_buffer_float';
// const EXT_HALF_FLOAT_WEBGL1 = 'EXT_color_buffer_half_float';
@ -57,7 +57,7 @@ export default {
[GL.RGBA16F]: {gl2: EXT_FLOAT_WEBGL2, bpp: 8},
[GL.R32F]: {gl2: EXT_FLOAT_WEBGL2, bpp: 4},
[GL.RG32F]: {gl2: EXT_FLOAT_WEBGL2, bpp: 8},
// TODO - can't get WEBGL.color_buffer_float to work on renderbuffers
// TODO - can't get WEBGL_color_buffer_float to work on renderbuffers
[GL.RGBA32F]: {gl2: EXT_FLOAT_WEBGL2, bpp: 16},
// [GL.RGBA32F]: {gl2: EXT_FLOAT_WEBGL2, gl1: EXT_FLOAT_WEBGL1},
[GL.R11F_G11F_B10F]: {gl2: EXT_FLOAT_WEBGL2, bpp: 4}

View File

@ -1,10 +1,14 @@
export default class Resource {
readonly id: string;
readonly gl: WebGLRenderingContext;
readonly gl2: WebGL2RenderingContext;
readonly _handle: any;
get handle(): any;
constructor(gl: WebGLRenderingContext, opts?: {});
toString(): string;
get handle(): any;
delete({deleteChildren}?: {deleteChildren?: boolean}): this;
delete({ deleteChildren }?: { deleteChildren?: boolean }): this;
bind(funcOrHandle?: any): any;
unbind(): void;
/**

View File

@ -16,6 +16,8 @@ export default class Resource {
const {id, userData = {}} = opts;
this.gl = gl;
// @ts-ignore
this.gl2 = gl;
// this.ext = polyfillContext(gl);
this.id = id || uid(this.constructor.name);
this.userData = userData;
@ -58,6 +60,7 @@ export default class Resource {
delete({deleteChildren = false} = {}) {
// Delete this object, and get refs to any children
// @ts-ignore
const children = this._handle && this._deleteHandle(this._handle);
if (this._handle) {
this._removeStats();
@ -65,10 +68,10 @@ export default class Resource {
this._handle = null;
// Optionally, recursively delete the children
// @ts-ignore
if (children && deleteChildren) {
children.filter(Boolean).forEach(child => {
child.delete();
});
// @ts-ignore
children.filter(Boolean).forEach(child => child.delete());
}
return this;
@ -111,6 +114,7 @@ export default class Resource {
pname = getKeyValue(this.gl, pname);
assert(pname);
// @ts-ignore
const parameters = this.constructor.PARAMETERS || {};
// Use parameter definitions to handle unsupported parameters
@ -139,10 +143,11 @@ export default class Resource {
// Many resources support a getParameter call -
// getParameters will get all parameters - slow but useful for debugging
// eslint-disable-next-line complexity
getParameters(opts = {}) {
const {parameters, keys} = {};
getParameters(options = {}) {
const {parameters, keys} = options;
// Get parameter definitions for this Resource
// @ts-ignore
const PARAMETERS = this.constructor.PARAMETERS || {};
const isWebgl2 = isWebGL2(this.gl);
@ -164,7 +169,7 @@ export default class Resource {
if (parameterAvailable) {
const key = keys ? getKey(this.gl, pname) : pname;
values[key] = this.getParameter(pname, opts);
values[key] = this.getParameter(pname, options);
if (keys && parameter.type === 'GLenum') {
values[key] = getKey(this.gl, values[key]);
}
@ -179,7 +184,7 @@ export default class Resource {
*
* @todo - cache parameter to avoid issuing WebGL calls?
*
* @param {GLenum} pname - parameter (GL constant, value or key)
* @param {string} pname - parameter (GL constant, value or key)
* @param {GLint|GLfloat|GLenum} value
* @return {Resource} returns self to enable chaining
*/
@ -187,6 +192,7 @@ export default class Resource {
pname = getKeyValue(this.gl, pname);
assert(pname);
// @ts-ignore
const parameters = this.constructor.PARAMETERS || {};
const parameter = parameters[pname];
@ -242,7 +248,7 @@ export default class Resource {
throw new Error(ERR_RESOURCE_METHOD_UNDEFINED);
}
_bindHandle() {
_bindHandle(handle) {
throw new Error(ERR_RESOURCE_METHOD_UNDEFINED);
}
@ -250,15 +256,11 @@ export default class Resource {
throw new Error(ERR_RESOURCE_METHOD_UNDEFINED);
}
/** @returns {number} */
_getParameter(pname, opts) {
throw new Error(ERR_RESOURCE_METHOD_UNDEFINED);
}
/**
* @param {GLenum} pname
* @param {GLint|GLfloat|GLenum} param
* @return {Sampler} returns self to enable chaining
*/
_setParameter(pname, value) {
throw new Error(ERR_RESOURCE_METHOD_UNDEFINED);
}

View File

@ -69,10 +69,10 @@ export class Shader extends Resource {
// Debug method - Returns translated source if available
getTranslatedSource() {
const extension = this.gl.getExtension('WEBGL.debug_shaders');
const extension = this.gl.getExtension('WEBGL_debug_shaders');
return extension
? extension.getTranslatedShaderSource(this.handle)
: 'No translated source available. WEBGL.debug_shaders not implemented';
: 'No translated source available. WEBGL_debug_shaders not implemented';
}
// PRIVATE METHODS

View File

@ -3,6 +3,7 @@ export default class TextureCube extends Texture {
static readonly FACES: number[];
constructor(gl: WebGLRenderingContext, props?: {});
initialize(props?: {}): void;
subImage(options: {face: any; data: any; x?: number; y?: number; mipmapLevel?: number}): any;
setCubeMapImageData(options: {

View File

@ -22,13 +22,13 @@ export default class TextureCube extends Texture {
Object.seal(this);
}
/* eslint-disable max-len, max-statements */
initialize(props = {}) {
const {mipmaps = true, parameters = {}} = props;
// Store props for accessors
this.opts = props;
// @ts-ignore
this.setCubeMapImageData(props).then(() => {
this.loaded = true;
@ -40,9 +40,11 @@ export default class TextureCube extends Texture {
this.setParameters(parameters);
});
return this;
}
subImage({face, data, x = 0, y = 0, mipmapLevel = 0}) {
// @ts-ignore TODO - is this a bug?
return this._subImage({target: face, data, x, y, mipmapLevel});
}

View File

@ -1,4 +1,6 @@
import GL from '@luma.gl/constants';
import {isWebGL2, assertWebGL2Context, withParameters, log} from '@luma.gl/gltools';
import {global} from 'probe.gl/env';
import Resource from './resource';
import Buffer from './buffer';
@ -9,9 +11,6 @@ import {
isFormatSupported,
isLinearFilteringSupported
} from './texture-formats';
import {isWebGL2, assertWebGL2Context, withParameters, log} from '@luma.gl/gltools';
import {global} from 'probe.gl/env';
import {uid, isPowerOfTwo, assert} from '../utils';
// Supported min filters for NPOT texture.
@ -22,7 +21,8 @@ const NPOT_MIN_FILTERS = [GL.LINEAR, GL.NEAREST];
const WebGLBuffer = global.WebGLBuffer || function WebGLBuffer() {};
export default class Texture extends Resource {
static isSupported(gl, {format, linearFiltering} = {}) {
static isSupported(gl, opts = {}) {
const {format, linearFiltering} = opts;
let supported = true;
if (format) {
supported = supported && isFormatSupported(gl, format);
@ -90,6 +90,7 @@ export default class Texture extends Resource {
return this;
}
const isVideo = typeof HTMLVideoElement !== 'undefined' && data instanceof HTMLVideoElement;
// @ts-ignore
if (isVideo && data.readyState < HTMLVideoElement.HAVE_METADATA) {
data.addEventListener('loadedmetadata', () => this.initialize(props));
return this;
@ -180,6 +181,7 @@ export default class Texture extends Resource {
this._video = {
video: data,
parameters,
// @ts-ignore
lastTime: data.readyState >= HTMLVideoElement.HAVE_CURRENT_DATA ? data.currentTime : -1
};
}
@ -190,6 +192,7 @@ export default class Texture extends Resource {
update() {
if (this._video) {
const {video, parameters, lastTime} = this._video;
// @ts-ignore
if (lastTime === video.currentTime || video.readyState < HTMLVideoElement.HAVE_CURRENT_DATA) {
return;
}
@ -303,6 +306,8 @@ export default class Texture extends Resource {
let dataType = null;
({data, dataType} = this._getDataType({data, compressed}));
let gl2;
withParameters(this.gl, parameters, () => {
switch (dataType) {
case 'null':
@ -311,6 +316,7 @@ export default class Texture extends Resource {
case 'typed-array':
// Looks like this assert is not necessary, as offset is ignored under WebGL1
// assert((offset === 0 || isWebGL2(gl)), 'offset supported in WebGL2 only');
// @ts-ignore
gl.texImage2D(
target,
level,
@ -321,15 +327,16 @@ export default class Texture extends Resource {
dataFormat,
type,
data,
// @ts-ignore
offset
);
break;
case 'buffer':
// WebGL2 enables creating textures directly from a WebGL buffer
assertWebGL2Context(gl);
gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, data.handle || data);
gl.texImage2D(target, level, format, width, height, border, dataFormat, type, offset);
gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
gl2 = assertWebGL2Context(gl);
gl2.bindBuffer(GL.PIXEL_UNPACK_BUFFER, data.handle || data);
gl2.texImage2D(target, level, format, width, height, border, dataFormat, type, offset);
gl2.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
break;
case 'browser-object':
if (isWebGL2(gl)) {
@ -457,17 +464,20 @@ export default class Texture extends Resource {
} else if (data === null) {
this.gl.texSubImage2D(target, level, x, y, width, height, dataFormat, type, null);
} else if (ArrayBuffer.isView(data)) {
// const gl2 = assertWebGL2Context(this.gl);
// @ts-ignore last offset parameter is ignored under WebGL1
this.gl.texSubImage2D(target, level, x, y, width, height, dataFormat, type, data, offset);
} else if (data instanceof WebGLBuffer) {
// WebGL2 allows us to create texture directly from a WebGL buffer
assertWebGL2Context(this.gl);
const gl2 = assertWebGL2Context(this.gl);
// This texImage2D signature uses currently bound GL.PIXEL_UNPACK_BUFFER
this.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, data);
this.gl.texSubImage2D(target, level, x, y, width, height, dataFormat, type, offset);
this.gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
gl2.bindBuffer(GL.PIXEL_UNPACK_BUFFER, data);
gl2.texSubImage2D(target, level, x, y, width, height, dataFormat, type, offset);
gl2.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
} else if (isWebGL2(this.gl)) {
// Assume data is a browser supported object (ImageData, Canvas, ...)
this.gl.texSubImage2D(target, level, x, y, width, height, dataFormat, type, data);
const gl2 = assertWebGL2Context(this.gl);
gl2.texSubImage2D(target, level, x, y, width, height, dataFormat, type, data);
} else {
this.gl.texSubImage2D(target, level, x, y, dataFormat, type, data);
}

View File

@ -82,15 +82,19 @@ export default class TransformFeedback extends Resource {
}
begin(primitiveMode = GL.POINTS) {
// @ts-ignore
this.gl.bindTransformFeedback(GL.TRANSFORM_FEEDBACK, this.handle);
this._bindBuffers();
// @ts-ignore
this.gl.beginTransformFeedback(primitiveMode);
return this;
}
end() {
// @ts-ignore
this.gl.endTransformFeedback();
this._unbindBuffers();
// @ts-ignore
this.gl.bindTransformFeedback(GL.TRANSFORM_FEEDBACK, null);
return this;
}
@ -150,8 +154,10 @@ export default class TransformFeedback extends Resource {
_bindBuffer(index, buffer, byteOffset = 0, byteSize) {
const handle = buffer && buffer.handle;
if (!handle || byteSize === undefined) {
// @ts-ignore
this.gl.bindBufferBase(GL.TRANSFORM_FEEDBACK_BUFFER, index, handle);
} else {
// @ts-ignore
this.gl.bindBufferRange(GL.TRANSFORM_FEEDBACK_BUFFER, index, handle, byteOffset, byteSize);
}
return this;
@ -160,14 +166,17 @@ export default class TransformFeedback extends Resource {
// RESOURCE METHODS
_createHandle() {
// @ts-ignore
return this.gl.createTransformFeedback();
}
_deleteHandle() {
// @ts-ignore
this.gl.deleteTransformFeedback(this.handle);
}
_bindHandle(handle) {
// @ts-ignore
this.gl.bindTransformFeedback(GL.TRANSFORM_FEEDBACK, this.handle);
}
}

View File

@ -1,3 +1,5 @@
// @ts-nocheck
/* eslint-disable camelcase */
import {decomposeCompositeGLType} from '../webgl-utils/attribute-utils';
import {assert} from '../utils';

View File

@ -12,5 +12,8 @@ export default class VertexArrayObject extends Resource {
setElementBuffer(elementBuffer?: any, opts?: {}): this;
setBuffer(location: any, buffer: any, accessor: any): this;
enable(location: any, enable?: boolean): this;
getConstantBuffer(elementCount: any, value: any, accessor: any): any;
getConstantBuffer(elementCount: any, value: any): any;
// PRIVATE
_normalizeConstantArrayValue(arrayValue);
}

View File

@ -32,8 +32,11 @@ export default class VertexArrayObject extends Resource {
static getMaxAttributes(gl) {
// TODO - should be cached per context
// @ts-ignore
VertexArrayObject.MAX_ATTRIBUTES =
// @ts-ignore
VertexArrayObject.MAX_ATTRIBUTES || gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
// @ts-ignore
return VertexArrayObject.MAX_ATTRIBUTES;
}
@ -67,6 +70,9 @@ export default class VertexArrayObject extends Resource {
this.bufferValue = null;
this.isDefaultArray = opts.isDefaultArray || false;
/** @type {WebGL2RenderingContext} */
this.gl2 = gl;
this.initialize(opts);
Object.seal(this);
@ -77,6 +83,7 @@ export default class VertexArrayObject extends Resource {
if (this.buffer) {
this.buffer.delete();
}
return this;
}
get MAX_ATTRIBUTES() {
@ -114,7 +121,7 @@ export default class VertexArrayObject extends Resource {
const {size, type, stride, offset, normalized, integer, divisor} = accessor;
const {gl} = this;
const {gl, gl2} = this;
location = Number(location);
this.bind(() => {
@ -124,13 +131,13 @@ export default class VertexArrayObject extends Resource {
// WebGL2 supports *integer* data formats, i.e. GPU will see integer values
if (integer) {
assert(isWebGL2(gl));
gl.vertexAttribIPointer(location, size, type, stride, offset);
gl2.vertexAttribIPointer(location, size, type, stride, offset);
} else {
// Attaches ARRAY_BUFFER with specified buffer format to location
gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
}
gl.enableVertexAttribArray(location);
gl.vertexAttribDivisor(location, divisor || 0);
gl2.vertexAttribDivisor(location, divisor || 0);
// NOTE We don't unbind buffer here, typically another buffer will be bound just after
});
@ -165,10 +172,10 @@ export default class VertexArrayObject extends Resource {
// NOTE: Desktop OpenGL cannot disable attribute 0.
// https://stackoverflow.com/questions/20305231/webgl-warning-attribute-0-is-disabled-
// this-has-significant-performance-penalt
getConstantBuffer(elementCount, value, accessor) {
getConstantBuffer(elementCount, value) {
// Create buffer only when needed, and reuse it (avoids inflating buffer creation statistics)
const constantValue = this._normalizeConstantArrayValue(value, accessor);
const constantValue = this._normalizeConstantArrayValue(value);
const byteLength = constantValue.byteLength * elementCount;
const length = constantValue.length * elementCount;
@ -197,7 +204,7 @@ export default class VertexArrayObject extends Resource {
// TODO - convert Arrays based on known type? (read type from accessor, don't assume Float32Array)
// TODO - handle single values for size 1 attributes?
_normalizeConstantArrayValue(arrayValue, accessor) {
_normalizeConstantArrayValue(arrayValue) {
if (Array.isArray(arrayValue)) {
return new Float32Array(arrayValue);
}
@ -278,17 +285,21 @@ export default class VertexArrayObject extends Resource {
// RESOURCE IMPLEMENTATION
_createHandle() {
return this.gl.createVertexArray();
/** @type {WebGL2RenderingContext} */
// @ts-ignore
const gl2 = this.gl;
return gl2.createVertexArray();
}
_deleteHandle(handle) {
this.gl.deleteVertexArray(handle);
this.gl2.deleteVertexArray(handle);
// @ts-ignore
return [this.elements];
// return [this.elements, ...this.buffers];
}
_bindHandle(handle) {
this.gl.bindVertexArray(handle);
this.gl2.bindVertexArray(handle);
}
// Generic getter for information about a vertex attribute at a given position

View File

@ -1,7 +1,9 @@
import ProgramConfiguration from './program-configuration';
import VertexArrayObject from './vertex-array-object';
import ProgramConfiguration from "./program-configuration";
import VertexArrayObject from "./vertex-array-object";
import Accessor from './accessor';
export default class VertexArray {
readonly attributes: object;
constructor(gl: WebGLRenderingContext, opts?: {});
delete(): void;
initialize(props?: {}): this;
@ -20,6 +22,6 @@ export default class VertexArray {
// FOR TESTING
readonly configuration: ProgramConfiguration | null;
readonly accessors: {size?: number}[];
readonly accessors: Accessor[];
readonly vertexArrayObject: VertexArrayObject;
}

View File

@ -184,7 +184,7 @@ export default class VertexArray {
);
if (location >= 0) {
arrayValue = this.vertexArrayObject._normalizeConstantArrayValue(arrayValue, accessor);
arrayValue = this.vertexArrayObject._normalizeConstantArrayValue(arrayValue);
this.values[location] = arrayValue;
this.accessors[location] = accessor;
@ -265,11 +265,16 @@ export default class VertexArray {
// Resolve locations and accessors
_resolveLocationAndAccessor(locationOrName, value, valueAccessor, appAccessor) {
const INVALID_RESULT = {
location: -1,
accessor: null
};
const {location, name} = this._getAttributeIndex(locationOrName);
if (!Number.isFinite(location) || location < 0) {
this.unused[locationOrName] = value;
log.once(3, () => `unused value ${locationOrName} in ${this.id}`)();
return this;
return INVALID_RESULT;
}
const accessInfo = this._getAttributeInfo(name || location);
@ -277,10 +282,7 @@ export default class VertexArray {
// Attribute location wasn't directly found.
// Likely due to multi-location attributes (e.g. matrix)
if (!accessInfo) {
return {
location: -1,
accessor: null
};
return INVALID_RESULT;
}
// Resolve the partial accessors into a final accessor

View File

@ -78,7 +78,9 @@ function getDebugTableRow(vertexArray, attribute, accessor, header) {
marker = changed ? '*' : '';
value = data;
// @ts-ignore
bytes = buffer.byteLength;
// @ts-ignore
verts = bytes / data.BYTES_PER_ELEMENT / size;
let format;

View File

@ -85,9 +85,9 @@ function addLineNumbers(string, start = 1, delim = ': ') {
const lines = string.split(/\r?\n/);
const maxDigits = String(lines.length + start - 1).length;
return lines.map((line, i) => {
const lineNumber = i + start;
const digits = String(lineNumber).length;
const prefix = padLeft(String(lineNumber), maxDigits - digits);
const lineNumber = String(i + start);
const digits = lineNumber.length;
const prefix = padLeft(lineNumber, maxDigits - digits);
return prefix + delim + line;
});
}

View File

@ -1,3 +1,74 @@
// luma.gl Base WebGL wrapper library
// Provides simple class/function wrappers around the low level webgl objects
// These classes are intentionally close to the WebGL API
// but make it easier to use.
// Higher level abstractions can be built on these classes
// Initialize any global state
export {lumaStats} from './init';
// UTILS
export {requestAnimationFrame, cancelAnimationFrame} from './webgl-utils/request-animation-frame';
// WebGL Functions
export {cloneTextureFrom} from './webgl-utils/texture-utils';
export {getKeyValue, getKey} from './webgl-utils/constants-to-keys';
export {getContextInfo, getGLContextInfo, getContextLimits} from './features/limits';
export {FEATURES} from './features/webgl-features-table';
export {hasFeature, hasFeatures, getFeatures} from './features/features';
export {default as canCompileGLGSExtension} from './features/check-glsl-extension';
// WebGL Helper Classes
export {default as Accessor} from './classes/accessor';
// WebGL1 classes
export {default as Buffer} from './classes/buffer';
export {Shader, VertexShader, FragmentShader} from './classes/shader';
export {default as Program} from './classes/program';
export {default as Framebuffer} from './classes/framebuffer';
export {default as Renderbuffer} from './classes/renderbuffer';
export {default as Texture2D} from './classes/texture-2d';
export {default as TextureCube} from './classes/texture-cube';
export {clear, clearBuffer} from './classes/clear';
// Copy and Blit
export {
readPixelsToArray,
readPixelsToBuffer,
copyToDataUrl,
copyToImage,
copyToTexture,
blit
} from './classes/copy-and-blit';
// WebGL2 classes & Extensions
export {default as Query} from './classes/query';
export {default as Texture3D} from './classes/texture-3d';
export {default as TransformFeedback} from './classes/transform-feedback';
export {default as VertexArrayObject} from './classes/vertex-array-object';
export {default as VertexArray} from './classes/vertex-array';
export {default as UniformBufferLayout} from './classes/uniform-buffer-layout';
// experimental WebGL exports
export {setPathPrefix, loadFile, loadImage} from './utils/load-file';
// PARSE SHADER SOURCE
export {default as getShaderName} from './glsl-utils/get-shader-name';
export {default as getShaderVersion} from './glsl-utils/get-shader-version';
// UTILS
export {log} from '@luma.gl/gltools';
export {default as assert} from './utils/assert';
export {uid, isObjectEmpty} from './utils/utils';
// INTERNAL
export {parseUniformName, getUniformSetter} from './classes/uniforms';
export {getDebugTableForUniforms} from './debug/debug-uniforms';
export {getDebugTableForVertexArray} from './debug/debug-vertex-array';
export {getDebugTableForProgramConfiguration} from './debug/debug-program-configuration';
/*
declare module "@luma.gl/webgl/classes/texture-formats" {
export const TEXTURE_FORMATS: {
@ -423,74 +494,3 @@ declare module "@luma.gl/webgl/utils/array-utils-flat" {
declare module "@luma.gl/webgl/classes/vertex-array-object" {
}
*/
// luma.gl Base WebGL wrapper library
// Provides simple class/function wrappers around the low level webgl objects
// These classes are intentionally close to the WebGL API
// but make it easier to use.
// Higher level abstractions can be built on these classes
// Initialize any global state
export {lumaStats} from './init';
// UTILS
export {requestAnimationFrame, cancelAnimationFrame} from './webgl-utils/request-animation-frame';
// WebGL Functions
export {cloneTextureFrom} from './webgl-utils/texture-utils';
export {getKeyValue, getKey} from './webgl-utils/constants-to-keys';
export {getContextInfo, getGLContextInfo, getContextLimits} from './features/limits';
export {FEATURES} from './features/webgl-features-table';
export {hasFeature, hasFeatures, getFeatures} from './features/features';
export {default as canCompileGLGSExtension} from './features/check-glsl-extension';
// WebGL Helper Classes
export {default as Accessor} from './classes/accessor';
// WebGL1 classes
export {default as Buffer} from './classes/buffer';
export {Shader, VertexShader, FragmentShader} from './classes/shader';
export {default as Program} from './classes/program';
export {default as Framebuffer} from './classes/framebuffer';
export {default as Renderbuffer} from './classes/renderbuffer';
export {default as Texture2D} from './classes/texture-2d';
export {default as TextureCube} from './classes/texture-cube';
export {clear, clearBuffer} from './classes/clear';
// Copy and Blit
export {
readPixelsToArray,
readPixelsToBuffer,
copyToDataUrl,
copyToImage,
copyToTexture,
blit
} from './classes/copy-and-blit';
// WebGL2 classes & Extensions
export {default as Query} from './classes/query';
export {default as Texture3D} from './classes/texture-3d';
export {default as TransformFeedback} from './classes/transform-feedback';
export {default as VertexArrayObject} from './classes/vertex-array-object';
export {default as VertexArray} from './classes/vertex-array';
export {default as UniformBufferLayout} from './classes/uniform-buffer-layout';
// experimental WebGL exports
export {setPathPrefix, loadFile, loadImage} from './utils/load-file';
// PARSE SHADER SOURCE
export {default as getShaderName} from './glsl-utils/get-shader-name';
export {default as getShaderVersion} from './glsl-utils/get-shader-version';
// UTILS
export {log} from '@luma.gl/gltools';
export {default as assert} from './utils/assert';
export {uid, isObjectEmpty} from './utils/utils';
// INTERNAL
export {parseUniformName, getUniformSetter} from './classes/uniforms';
export {getDebugTableForUniforms} from './debug/debug-uniforms';
export {getDebugTableForVertexArray} from './debug/debug-vertex-array';
export {getDebugTableForProgramConfiguration} from './debug/debug-program-configuration';

3
modules/webgl/src/types.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
export type AccessorObject = {
size?: number;
};

View File

@ -9,6 +9,7 @@
"allowSyntheticDefaultImports": true,
"noEmit": true,
"baseUrl": ".",
"rootDir": ".",
"paths": {
"@luma.gl/constants/*": ["modules/constants/src/*"],
"@luma.gl/constants/test/*": ["modules/constants/test/*"],