@luma.gl/gltools
Version:
WebGL2 API Polyfills for WebGL1 WebGLRenderingContext
516 lines (455 loc) • 17.3 kB
JavaScript
// Tables describing WebGL parameters
import GL from '@luma.gl/constants';
import {isWebGL2} from '../utils/webgl-checks';
// DEFAULT SETTINGS - FOR FAST CACHE INITIALIZATION AND CONTEXT RESETS
/* eslint-disable no-shadow */
export const GL_PARAMETER_DEFAULTS = {
[]: false,
[]: new Float32Array([0, 0, 0, 0]),
[]: GL.FUNC_ADD,
[]: GL.FUNC_ADD,
[]: GL.ONE,
[]: GL.ZERO,
[]: GL.ONE,
[]: GL.ZERO,
[]: new Float32Array([0, 0, 0, 0]), // TBD
[]: [true, true, true, true],
[]: false,
[]: GL.BACK,
[]: false,
[]: 1,
[]: GL.LESS,
[]: new Float32Array([0, 1]), // TBD
[]: true,
[]: true,
// FRAMEBUFFER_BINDING and DRAW_FRAMEBUFFER_BINDING(WebGL2) refer same state.
[]: null,
[]: GL.CCW,
[]: GL.DONT_CARE,
[]: 1,
[]: false,
[]: 0,
[]: 0,
[]: 1.0,
[]: false,
[]: false,
// Note: Dynamic value. If scissor test enabled we expect users to set correct scissor box
[]: new Int32Array([0, 0, 1024, 1024]),
[]: false,
[]: 0,
[]: 0xffffffff,
[]: 0xffffffff,
[]: GL.ALWAYS,
[]: 0,
[]: 0xffffffff,
[]: GL.ALWAYS,
[]: 0,
[]: 0xffffffff,
[]: GL.KEEP,
[]: GL.KEEP,
[]: GL.KEEP,
[]: GL.KEEP,
[]: GL.KEEP,
[]: GL.KEEP,
// Dynamic value: We use [0, 0, 1024, 1024] as default, but usually this is updated in each frame.
[]: [0, 0, 1024, 1024],
// WEBGL1 PIXEL PACK/UNPACK MODES
[]: 4,
[]: 4,
[]: false,
[]: false,
[]: GL.BROWSER_DEFAULT_WEBGL,
// WEBGL2 / EXTENSIONS
// gl1: 'OES_standard_derivatives'
[]: GL.DONT_CARE,
[]: null,
[]: false,
[]: 0,
[]: 0,
[]: 0,
[]: 0,
[]: 0,
[]: 0,
[]: 0,
[]: 0
};
// SETTER TABLES - ENABLES SETTING ANY PARAMETER WITH A COMMON API
const enable = (gl, value, key) => (value ? gl.enable(key) : gl.disable(key));
const hint = (gl, value, key) => gl.hint(key, value);
const pixelStorei = (gl, value, key) => gl.pixelStorei(key, value);
const drawFramebuffer = (gl, value) => {
const target = isWebGL2(gl) ? GL.DRAW_FRAMEBUFFER : GL.FRAMEBUFFER;
return gl.bindFramebuffer(target, value);
};
const readFramebuffer = (gl, value) => {
return gl.bindFramebuffer(GL.READ_FRAMEBUFFER, value);
};
// Utility
function isArray(array) {
return Array.isArray(array) || ArrayBuffer.isView(array);
}
// Map from WebGL parameter names to corresponding WebGL setter functions
// WegGL constants are read by parameter names, but set by function names
// NOTE: When value type is a string, it will be handled by 'GL_COMPOSITE_PARAMETER_SETTERS'
export const GL_PARAMETER_SETTERS = {
[]: enable,
[]: (gl, value) => gl.blendColor(...value),
[]: 'blendEquation',
[]: 'blendEquation',
[]: 'blendFunc',
[]: 'blendFunc',
[]: 'blendFunc',
[]: 'blendFunc',
[]: (gl, value) => gl.clearColor(...value),
[]: (gl, value) => gl.colorMask(...value),
[]: enable,
[]: (gl, value) => gl.cullFace(value),
[]: enable,
[]: (gl, value) => gl.clearDepth(value),
[]: (gl, value) => gl.depthFunc(value),
[]: (gl, value) => gl.depthRange(...value),
[]: (gl, value) => gl.depthMask(value),
[]: enable,
[]: hint,
// NOTE: FRAMEBUFFER_BINDING and DRAW_FRAMEBUFFER_BINDING(WebGL2) refer same state.
[]: drawFramebuffer,
[]: (gl, value) => gl.frontFace(value),
[]: hint,
[]: (gl, value) => gl.lineWidth(value),
[]: enable,
[]: 'polygonOffset',
[]: 'polygonOffset',
[]: enable,
[]: 'sampleCoverage',
[]: 'sampleCoverage',
[]: enable,
[]: (gl, value) => gl.scissor(...value),
[]: enable,
[]: (gl, value) => gl.clearStencil(value),
[]: (gl, value) => gl.stencilMaskSeparate(GL.FRONT, value),
[]: (gl, value) => gl.stencilMaskSeparate(GL.BACK, value),
[]: 'stencilFuncFront',
[]: 'stencilFuncFront',
[]: 'stencilFuncFront',
[]: 'stencilFuncBack',
[]: 'stencilFuncBack',
[]: 'stencilFuncBack',
[]: 'stencilOpFront',
[]: 'stencilOpFront',
[]: 'stencilOpFront',
[]: 'stencilOpBack',
[]: 'stencilOpBack',
[]: 'stencilOpBack',
[]: (gl, value) => gl.viewport(...value),
// WEBGL1 PIXEL PACK/UNPACK MODES
[]: pixelStorei,
[]: pixelStorei,
[]: pixelStorei,
[]: pixelStorei,
[]: pixelStorei,
// WEBGL2 PIXEL PACK/UNPACK MODES
// RASTERIZER_DISCARD ...
[]: pixelStorei,
[]: pixelStorei,
[]: pixelStorei,
[]: readFramebuffer,
[]: pixelStorei,
[]: pixelStorei,
[]: pixelStorei,
[]: pixelStorei,
[]: pixelStorei,
// Function-style setters
framebuffer: (gl, framebuffer) => {
// accepts 1) a WebGLFramebuffer 2) null (default framebuffer), or 3) luma.gl Framebuffer class
// framebuffer is null when restoring to default framebuffer, otherwise use the WebGL handle.
const handle = framebuffer && 'handle' in framebuffer ? framebuffer.handle : framebuffer;
return gl.bindFramebuffer(GL.FRAMEBUFFER, handle);
},
blend: (gl, value) => (value ? gl.enable(GL.BLEND) : gl.disable(GL.BLEND)),
blendColor: (gl, value) => gl.blendColor(...value),
blendEquation: (gl, args) => {
args = isArray(args) ? args : [args, args];
gl.blendEquationSeparate(...args);
},
blendFunc: (gl, args) => {
args = isArray(args) && args.length === 2 ? [...args, ...args] : args;
gl.blendFuncSeparate(...args);
},
clearColor: (gl, value) => gl.clearColor(...value),
clearDepth: (gl, value) => gl.clearDepth(value),
clearStencil: (gl, value) => gl.clearStencil(value),
colorMask: (gl, value) => gl.colorMask(...value),
cull: (gl, value) => (value ? gl.enable(GL.CULL_FACE) : gl.disable(GL.CULL_FACE)),
cullFace: (gl, value) => gl.cullFace(value),
depthTest: (gl, value) => (value ? gl.enable(GL.DEPTH_TEST) : gl.disable(GL.DEPTH_TEST)),
depthFunc: (gl, value) => gl.depthFunc(value),
depthMask: (gl, value) => gl.depthMask(value),
depthRange: (gl, value) => gl.depthRange(...value),
dither: (gl, value) => (value ? gl.enable(GL.DITHER) : gl.disable(GL.DITHER)),
derivativeHint: (gl, value) => {
// gl1: 'OES_standard_derivatives'
gl.hint(GL.FRAGMENT_SHADER_DERIVATIVE_HINT, value);
},
frontFace: (gl, value) => gl.frontFace(value),
mipmapHint: (gl, value) => gl.hint(GL.GENERATE_MIPMAP_HINT, value),
lineWidth: (gl, value) => gl.lineWidth(value),
polygonOffsetFill: (gl, value) =>
value ? gl.enable(GL.POLYGON_OFFSET_FILL) : gl.disable(GL.POLYGON_OFFSET_FILL),
polygonOffset: (gl, value) => gl.polygonOffset(...value),
sampleCoverage: (gl, value) => gl.sampleCoverage(...value),
scissorTest: (gl, value) => (value ? gl.enable(GL.SCISSOR_TEST) : gl.disable(GL.SCISSOR_TEST)),
scissor: (gl, value) => gl.scissor(...value),
stencilTest: (gl, value) => (value ? gl.enable(GL.STENCIL_TEST) : gl.disable(GL.STENCIL_TEST)),
stencilMask: (gl, value) => {
value = isArray(value) ? value : [value, value];
const [mask, backMask] = value;
gl.stencilMaskSeparate(GL.FRONT, mask);
gl.stencilMaskSeparate(GL.BACK, backMask);
},
stencilFunc: (gl, args) => {
args = isArray(args) && args.length === 3 ? [...args, ...args] : args;
const [func, ref, mask, backFunc, backRef, backMask] = args;
gl.stencilFuncSeparate(GL.FRONT, func, ref, mask);
gl.stencilFuncSeparate(GL.BACK, backFunc, backRef, backMask);
},
stencilOp: (gl, args) => {
args = isArray(args) && args.length === 3 ? [...args, ...args] : args;
const [sfail, dpfail, dppass, backSfail, backDpfail, backDppass] = args;
gl.stencilOpSeparate(GL.FRONT, sfail, dpfail, dppass);
gl.stencilOpSeparate(GL.BACK, backSfail, backDpfail, backDppass);
},
viewport: (gl, value) => gl.viewport(...value)
};
function getValue(glEnum, values, cache) {
return values[glEnum] !== undefined ? values[glEnum] : cache[glEnum];
}
// COMPOSITE_WEBGL_PARAMETER_
export const GL_COMPOSITE_PARAMETER_SETTERS = {
blendEquation: (gl, values, cache) =>
gl.blendEquationSeparate(
getValue(GL.BLEND_EQUATION_RGB, values, cache),
getValue(GL.BLEND_EQUATION_ALPHA, values, cache)
),
blendFunc: (gl, values, cache) =>
gl.blendFuncSeparate(
getValue(GL.BLEND_SRC_RGB, values, cache),
getValue(GL.BLEND_DST_RGB, values, cache),
getValue(GL.BLEND_SRC_ALPHA, values, cache),
getValue(GL.BLEND_DST_ALPHA, values, cache)
),
polygonOffset: (gl, values, cache) =>
gl.polygonOffset(
getValue(GL.POLYGON_OFFSET_FACTOR, values, cache),
getValue(GL.POLYGON_OFFSET_UNITS, values, cache)
),
sampleCoverage: (gl, values, cache) =>
gl.sampleCoverage(
getValue(GL.SAMPLE_COVERAGE_VALUE, values, cache),
getValue(GL.SAMPLE_COVERAGE_INVERT, values, cache)
),
stencilFuncFront: (gl, values, cache) =>
gl.stencilFuncSeparate(
GL.FRONT,
getValue(GL.STENCIL_FUNC, values, cache),
getValue(GL.STENCIL_REF, values, cache),
getValue(GL.STENCIL_VALUE_MASK, values, cache)
),
stencilFuncBack: (gl, values, cache) =>
gl.stencilFuncSeparate(
GL.BACK,
getValue(GL.STENCIL_BACK_FUNC, values, cache),
getValue(GL.STENCIL_BACK_REF, values, cache),
getValue(GL.STENCIL_BACK_VALUE_MASK, values, cache)
),
stencilOpFront: (gl, values, cache) =>
gl.stencilOpSeparate(
GL.FRONT,
getValue(GL.STENCIL_FAIL, values, cache),
getValue(GL.STENCIL_PASS_DEPTH_FAIL, values, cache),
getValue(GL.STENCIL_PASS_DEPTH_PASS, values, cache)
),
stencilOpBack: (gl, values, cache) =>
gl.stencilOpSeparate(
GL.BACK,
getValue(GL.STENCIL_BACK_FAIL, values, cache),
getValue(GL.STENCIL_BACK_PASS_DEPTH_FAIL, values, cache),
getValue(GL.STENCIL_BACK_PASS_DEPTH_PASS, values, cache)
)
};
// Setter functions intercepted for cache updates
export const GL_HOOKED_SETTERS = {
// GENERIC SETTERS
enable: (update, capability) =>
update({
[]: true
}),
disable: (update, capability) =>
update({
[]: false
}),
pixelStorei: (update, pname, value) =>
update({
[]: value
}),
hint: (update, pname, hint) =>
update({
[]: hint
}),
// SPECIFIC SETTERS
bindFramebuffer: (update, target, framebuffer) => {
switch (target) {
case GL.FRAMEBUFFER:
return update({
[]: framebuffer,
[]: framebuffer
});
case GL.DRAW_FRAMEBUFFER:
return update({[GL.DRAW_FRAMEBUFFER_BINDING]: framebuffer});
case GL.READ_FRAMEBUFFER:
return update({[GL.READ_FRAMEBUFFER_BINDING]: framebuffer});
default:
return null;
}
},
blendColor: (update, r, g, b, a) =>
update({
[]: new Float32Array([r, g, b, a])
}),
blendEquation: (update, mode) =>
update({
[]: mode,
[]: mode
}),
blendEquationSeparate: (update, modeRGB, modeAlpha) =>
update({
[]: modeRGB,
[]: modeAlpha
}),
blendFunc: (update, src, dst) =>
update({
[]: src,
[]: dst,
[]: src,
[]: dst
}),
blendFuncSeparate: (update, srcRGB, dstRGB, srcAlpha, dstAlpha) =>
update({
[]: srcRGB,
[]: dstRGB,
[]: srcAlpha,
[]: dstAlpha
}),
clearColor: (update, r, g, b, a) =>
update({
[]: new Float32Array([r, g, b, a])
}),
clearDepth: (update, depth) =>
update({
[]: depth
}),
clearStencil: (update, s) =>
update({
[]: s
}),
colorMask: (update, r, g, b, a) =>
update({
[]: [r, g, b, a]
}),
cullFace: (update, mode) =>
update({
[]: mode
}),
depthFunc: (update, func) =>
update({
[]: func
}),
depthRange: (update, zNear, zFar) =>
update({
[]: new Float32Array([zNear, zFar])
}),
depthMask: (update, mask) =>
update({
[]: mask
}),
frontFace: (update, face) =>
update({
[]: face
}),
lineWidth: (update, width) =>
update({
[]: width
}),
polygonOffset: (update, factor, units) =>
update({
[]: factor,
[]: units
}),
sampleCoverage: (update, value, invert) =>
update({
[]: value,
[]: invert
}),
scissor: (update, x, y, width, height) =>
update({
[]: new Int32Array([x, y, width, height])
}),
stencilMask: (update, mask) =>
update({
[]: mask,
[]: mask
}),
stencilMaskSeparate: (update, face, mask) =>
update({
[]: mask
}),
stencilFunc: (update, func, ref, mask) =>
update({
[]: func,
[]: ref,
[]: mask,
[]: func,
[]: ref,
[]: mask
}),
stencilFuncSeparate: (update, face, func, ref, mask) =>
update({
[]: func,
[]: ref,
[]: mask
}),
stencilOp: (update, fail, zfail, zpass) =>
update({
[]: fail,
[]: zfail,
[]: zpass,
[]: fail,
[]: zfail,
[]: zpass
}),
stencilOpSeparate: (update, face, fail, zfail, zpass) =>
update({
[]: fail,
[]: zfail,
[]: zpass
}),
viewport: (update, x, y, width, height) =>
update({
[]: [x, y, width, height]
})
};
// GETTER TABLE - FOR READING OUT AN ENTIRE CONTEXT
const isEnabled = (gl, key) => gl.isEnabled(key);
// Exceptions for any keys that cannot be queried by gl.getParameters
export const GL_PARAMETER_GETTERS = {
[]: isEnabled,
[]: isEnabled,
[]: isEnabled,
[]: isEnabled,
[]: isEnabled,
[]: isEnabled,
[]: isEnabled,
[]: isEnabled,
[]: isEnabled,
// WebGL 2
[]: isEnabled
};