Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Enable webgl1 fallback #2656

Merged
merged 8 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- _...Add new stuff here..._

### 🐞 Bug fixes
- Fix regression - Add webgl1 fallback to accomondate users without webgl2 support
- _...Add new stuff here..._

## 3.0.1
Expand Down
45 changes: 39 additions & 6 deletions build/generate-shaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,51 @@ console.log('Generating shaders');
* It will also create a simple package.json file to allow importing this package in webpack
*/

const files = globSync('./src/shaders/*.glsl');
for (const file of files) {
const vertex = globSync('./src/shaders/*.vertex.glsl');
for (const file of vertex) {
const code = fs.readFileSync(file, 'utf8');
const content = glslToTs(code);
const content = glslToTs(code, 'vertex');
const fileName = path.join('.', 'src', 'shaders', `${file.split(path.sep).splice(-1)}.g.ts`);
fs.writeFileSync(fileName, content);
}
console.log(`Finished converting ${files.length} glsl files`);

function glslToTs(code: string): string {
console.log(`Finished converting ${vertex.length} vertex shaders`);

const fragment = globSync('./src/shaders/*.fragment.glsl');
for (const file of fragment) {
const code = fs.readFileSync(file, 'utf8');
const content = glslToTs(code, 'fragment');
const fileName = path.join('.', 'src', 'shaders', `${file.split(path.sep).splice(-1)}.g.ts`);
fs.writeFileSync(fileName, content);
}

console.log(`Finished converting ${fragment.length} fragment shaders`);

function glslToTs(code: string, type: 'fragment'|'vertex'): string {
code = code
.trim(); // strip whitespace at the start/end

// WebGL1 Compat -- Start

if (type === 'fragment') {
code = code
.replace(/\bin\s/g, 'varying ') // For fragment shaders, replace "in " with "varying "
.replace('out highp vec4 fragColor;', '');
}

if (type === 'vertex') {
code = code
.replace(/\bin\s/g, 'attribute ') // For vertex shaders, replace "in " with "attribute "
.replace(/\bout\s/g, 'varying '); // For vertex shaders, replace "out " with "varying "
}

code = code
.replace(/fragColor/g, 'gl_FragColor')
.replace(/texture\(/g, 'texture2D(');

// WebGL1 Compat -- End

code = code
.trim() // strip whitespace at the start/end
.replace(/\s*\/\/[^\n]*\n/g, '\n') // strip double-slash comments
.replace(/\n+/g, '\n') // collapse multi line breaks
.replace(/\n\s+/g, '\n') // strip indentation
Expand Down
42 changes: 32 additions & 10 deletions src/gl/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
StructArrayMember
} from '../util/struct_array';
import type {Color} from '@maplibre/maplibre-gl-style-spec';
import {isWebGL2} from './webgl2';

type ClearArgs = {
color?: Color;
Expand All @@ -23,7 +24,7 @@ type ClearArgs = {
};

class Context {
gl: WebGL2RenderingContext;
gl: WebGLRenderingContext | WebGL2RenderingContext;

currentNumAttributes: number;
maxTextureSize: number;
Expand Down Expand Up @@ -67,7 +68,7 @@ class Context {
RGBA16F?: GLenum;
RGB16F?: GLenum;

constructor(gl: WebGL2RenderingContext) {
constructor(gl: WebGLRenderingContext | WebGL2RenderingContext) {
this.gl = gl;
this.clearColor = new ClearColor(this);
this.clearDepth = new ClearDepth(this);
Expand Down Expand Up @@ -101,18 +102,35 @@ class Context {
this.pixelStoreUnpackPremultiplyAlpha = new PixelStoreUnpackPremultiplyAlpha(this);
this.pixelStoreUnpackFlipY = new PixelStoreUnpackFlipY(this);

this.extTextureFilterAnisotropic = gl.getExtension('EXT_texture_filter_anisotropic');
this.extTextureFilterAnisotropic = (
gl.getExtension('EXT_texture_filter_anisotropic') ||
gl.getExtension('MOZ_EXT_texture_filter_anisotropic') ||
gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic')
);

if (this.extTextureFilterAnisotropic) {
this.extTextureFilterAnisotropicMax = gl.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
}

this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
this.HALF_FLOAT = gl.HALF_FLOAT;

gl.getExtension('EXT_color_buffer_half_float');
gl.getExtension('EXT_color_buffer_float');
this.RGBA16F = gl.RGBA16F;
this.RGB16F = gl.RGB16F;
const extSRGB = gl.getExtension('EXT_sRGB');
if (!extSRGB) {
console.warn('EXT_sRGB extension not supported');
}

if (isWebGL2(gl)) {
this.HALF_FLOAT = gl.HALF_FLOAT;
const extColorBufferHalfFloat = gl.getExtension('EXT_color_buffer_half_float');
this.RGBA16F = gl.RGBA16F ?? extColorBufferHalfFloat?.RGBA16F_EXT;
this.RGB16F = gl.RGB16F ?? extColorBufferHalfFloat?.RGB16F_EXT;
gl.getExtension('EXT_color_buffer_float');
} else {
gl.getExtension('EXT_color_buffer_half_float');
gl.getExtension('OES_texture_half_float_linear');
const extTextureHalfFloat = gl.getExtension('OES_texture_half_float');
this.HALF_FLOAT = extTextureHalfFloat?.HALF_FLOAT_OES;
}
}

setDefault() {
Expand Down Expand Up @@ -285,11 +303,15 @@ class Context {
}

createVertexArray(): WebGLVertexArrayObject | undefined {
return this.gl.createVertexArray();
if (isWebGL2(this.gl))
return this.gl.createVertexArray();
return this.gl.getExtension('OES_vertex_array_object')?.createVertexArrayOES();
}

deleteVertexArray(x: WebGLVertexArrayObject | undefined) {
return this.gl.deleteVertexArray(x);
if (isWebGL2(this.gl))
return this.gl.deleteVertexArray(x);
return this.gl.getExtension('OES_vertex_array_object')?.deleteVertexArrayOES(x);
}

unbindVAO() {
Expand Down
28 changes: 14 additions & 14 deletions src/gl/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
type BlendFuncConstant = typeof WebGL2RenderingContext.ZERO | typeof WebGL2RenderingContext.ONE | typeof WebGL2RenderingContext.SRC_COLOR | typeof WebGL2RenderingContext.ONE_MINUS_SRC_COLOR | typeof WebGL2RenderingContext.DST_COLOR | typeof WebGL2RenderingContext.ONE_MINUS_DST_COLOR | typeof WebGL2RenderingContext.SRC_ALPHA | typeof WebGL2RenderingContext.ONE_MINUS_SRC_ALPHA | typeof WebGL2RenderingContext.DST_ALPHA | typeof WebGL2RenderingContext.ONE_MINUS_DST_ALPHA | typeof WebGL2RenderingContext.CONSTANT_COLOR | typeof WebGL2RenderingContext.ONE_MINUS_CONSTANT_COLOR | typeof WebGL2RenderingContext.CONSTANT_ALPHA | typeof WebGL2RenderingContext.ONE_MINUS_CONSTANT_ALPHA | typeof WebGL2RenderingContext.BLEND_COLOR;
type BlendFuncConstant = WebGLRenderingContextBase['ZERO'] | WebGLRenderingContextBase['ONE'] | WebGLRenderingContextBase['SRC_COLOR'] | WebGLRenderingContextBase['ONE_MINUS_SRC_COLOR'] | WebGLRenderingContextBase['DST_COLOR'] | WebGLRenderingContextBase['ONE_MINUS_DST_COLOR'] | WebGLRenderingContextBase['SRC_ALPHA'] | WebGLRenderingContextBase['ONE_MINUS_SRC_ALPHA'] | WebGLRenderingContextBase['DST_ALPHA'] | WebGLRenderingContextBase['ONE_MINUS_DST_ALPHA'] | WebGLRenderingContextBase['CONSTANT_COLOR'] | WebGLRenderingContextBase['ONE_MINUS_CONSTANT_COLOR'] | WebGLRenderingContextBase['CONSTANT_ALPHA'] | WebGLRenderingContextBase['ONE_MINUS_CONSTANT_ALPHA'] | WebGLRenderingContextBase['BLEND_COLOR'];

export type BlendFuncType = [BlendFuncConstant, BlendFuncConstant];

export type BlendEquationType = typeof WebGL2RenderingContext.FUNC_ADD | typeof WebGL2RenderingContext.FUNC_SUBTRACT | typeof WebGL2RenderingContext.FUNC_REVERSE_SUBTRACT;
export type BlendEquationType = WebGLRenderingContextBase['FUNC_ADD'] | WebGLRenderingContextBase['FUNC_SUBTRACT'] | WebGLRenderingContextBase['FUNC_REVERSE_SUBTRACT'];

export type ColorMaskType = [boolean, boolean, boolean, boolean];

export type CompareFuncType = typeof WebGL2RenderingContext.NEVER | typeof WebGL2RenderingContext.LESS | typeof WebGL2RenderingContext.EQUAL | typeof WebGL2RenderingContext.LEQUAL | typeof WebGL2RenderingContext.GREATER | typeof WebGL2RenderingContext.NOTEQUAL | typeof WebGL2RenderingContext.GEQUAL | typeof WebGL2RenderingContext.ALWAYS;
export type CompareFuncType = WebGLRenderingContextBase['NEVER'] | WebGLRenderingContextBase['LESS'] | WebGLRenderingContextBase['EQUAL'] | WebGLRenderingContextBase['LEQUAL'] | WebGLRenderingContextBase['GREATER'] | WebGLRenderingContextBase['NOTEQUAL'] | WebGLRenderingContextBase['GEQUAL'] | WebGLRenderingContextBase['ALWAYS'];

export type DepthMaskType = boolean;

Expand All @@ -20,7 +20,7 @@ export type StencilFuncType = {
mask: number;
};

export type StencilOpConstant = typeof WebGL2RenderingContext.KEEP | typeof WebGL2RenderingContext.ZERO | typeof WebGL2RenderingContext.REPLACE | typeof WebGL2RenderingContext.INCR | typeof WebGL2RenderingContext.INCR_WRAP | typeof WebGL2RenderingContext.DECR | typeof WebGL2RenderingContext.DECR_WRAP | typeof WebGL2RenderingContext.INVERT;
export type StencilOpConstant = WebGLRenderingContextBase['KEEP'] | WebGLRenderingContextBase['ZERO'] | WebGLRenderingContextBase['REPLACE'] | WebGLRenderingContextBase['INCR'] | WebGLRenderingContextBase['INCR_WRAP'] | WebGLRenderingContextBase['DECR'] | WebGLRenderingContextBase['DECR_WRAP'] | WebGLRenderingContextBase['INVERT'];

export type StencilOpType = [StencilOpConstant, StencilOpConstant, StencilOpConstant];

Expand All @@ -29,31 +29,31 @@ export type TextureUnitType = number;
export type ViewportType = [number, number, number, number];

export type StencilTestGL = {
func: typeof WebGL2RenderingContext.NEVER;
func: WebGLRenderingContextBase['NEVER'];
mask: 0;
} | {
func: typeof WebGL2RenderingContext.LESS;
func: WebGLRenderingContextBase['LESS'];
mask: number;
} | {
func: typeof WebGL2RenderingContext.EQUAL;
func: WebGLRenderingContextBase['EQUAL'];
mask: number;
} | {
func: typeof WebGL2RenderingContext.LEQUAL;
func: WebGLRenderingContextBase['LEQUAL'];
mask: number;
} | {
func: typeof WebGL2RenderingContext.GREATER;
func: WebGLRenderingContextBase['GREATER'];
mask: number;
} | {
func: typeof WebGL2RenderingContext.NOTEQUAL;
func: WebGLRenderingContextBase['NOTEQUAL'];
mask: number;
} | {
func: typeof WebGL2RenderingContext.GEQUAL;
func: WebGLRenderingContextBase['GEQUAL'];
mask: number;
} | {
func: typeof WebGL2RenderingContext.ALWAYS;
func: WebGLRenderingContextBase['ALWAYS'];
mask: 0;
};

export type CullFaceModeType = typeof WebGL2RenderingContext.FRONT | typeof WebGL2RenderingContext.BACK | typeof WebGL2RenderingContext.FRONT_AND_BACK;
export type CullFaceModeType = WebGLRenderingContextBase['FRONT'] | WebGLRenderingContextBase['BACK'] | WebGLRenderingContextBase['FRONT_AND_BACK'];

export type FrontFaceType = typeof WebGL2RenderingContext.CW | typeof WebGL2RenderingContext.CCW;
export type FrontFaceType = WebGLRenderingContextBase['CW'] | WebGLRenderingContextBase['CCW'];
12 changes: 10 additions & 2 deletions src/gl/value.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Color} from '@maplibre/maplibre-gl-style-spec';
import {isWebGL2} from './webgl2';

import type Context from './context';
import type {
Expand Down Expand Up @@ -26,7 +27,7 @@ export interface IValue<T> {
}

class BaseValue<T> implements IValue<T> {
gl: WebGL2RenderingContext;
gl: WebGLRenderingContext|WebGL2RenderingContext;
current: T;
default: T;
dirty: boolean;
Expand Down Expand Up @@ -424,7 +425,13 @@ export class BindVertexArray extends BaseValue<WebGLVertexArrayObject | null> {
set(v: WebGLVertexArrayObject | null) {
if (v === this.current && !this.dirty) return;
const gl = this.gl;
gl.bindVertexArray(v);

if (isWebGL2(gl)) {
gl.bindVertexArray(v);
} else {
gl.getExtension('OES_vertex_array_object')?.bindVertexArrayOES(v);
}

this.current = v;
this.dirty = false;
}
Expand Down Expand Up @@ -494,6 +501,7 @@ export class ColorAttachment extends FramebufferAttachment<WebGLTexture> {
// attachment point, but thus far MBGL only uses textures for color
const gl = this.gl;
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, v, 0);

this.current = v;
this.dirty = false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/gl/vertex_buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class VertexBuffer {
gl.bufferSubData(gl.ARRAY_BUFFER, 0, array.arrayBuffer);
}

enableAttributes(gl: WebGL2RenderingContext, program: Program<any>) {
enableAttributes(gl: WebGLRenderingContext|WebGL2RenderingContext, program: Program<any>) {
for (let j = 0; j < this.attributes.length; j++) {
const member = this.attributes[j];
const attribIndex: number | void = program.attributes[member.name];
Expand All @@ -83,7 +83,7 @@ class VertexBuffer {
* @param program The active WebGL program
* @param vertexOffset Index of the starting vertex of the segment
*/
setVertexAttribPointers(gl: WebGL2RenderingContext, program: Program<any>, vertexOffset?: number | null) {
setVertexAttribPointers(gl: WebGLRenderingContext|WebGL2RenderingContext, program: Program<any>, vertexOffset?: number | null) {
for (let j = 0; j < this.attributes.length; j++) {
const member = this.attributes[j];
const attribIndex: number | void = program.attributes[member.name];
Expand Down
12 changes: 12 additions & 0 deletions src/gl/webgl2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const cache = new WeakMap();
export function isWebGL2(
gl: WebGLRenderingContext | WebGL2RenderingContext
): gl is WebGL2RenderingContext {
if (cache.has(gl)) {
return cache.get(gl);
} else {
const value = gl.getParameter(gl.VERSION).startsWith('WebGL 2.0');
cache.set(gl, value);
return value;
}
}
1 change: 1 addition & 0 deletions src/render/draw_heatmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ function bindTextureToFramebuffer(context: Context, painter: Painter, texture: W
const gl = context.gl;
// Use the higher precision half-float texture where available (producing much smoother looking heatmaps);
// Otherwise, fall back to a low precision texture

const numType = context.HALF_FLOAT ?? gl.UNSIGNED_BYTE;
const internalFormat = context.RGBA16F ?? gl.RGBA;

Expand Down
2 changes: 1 addition & 1 deletion src/render/painter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class Painter {
// every time the camera-matrix changes the terrain-facilitators will be redrawn.
terrainFacilitator: {dirty: boolean; matrix: mat4; renderTime: number};

constructor(gl: WebGL2RenderingContext, transform: Transform) {
constructor(gl: WebGLRenderingContext | WebGL2RenderingContext, transform: Transform) {
this.context = new Context(gl);
this.transform = transform;
this._tileTextures = {};
Expand Down
5 changes: 1 addition & 4 deletions src/render/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {terrainPreludeUniforms, TerrainPreludeUniformsType} from './program/terr
import type {TerrainData} from '../render/terrain';
import Terrain from '../render/terrain';

export type DrawMode = typeof WebGL2RenderingContext.LINES | typeof WebGL2RenderingContext.TRIANGLES | typeof WebGL2RenderingContext.LINE_STRIP;
export type DrawMode = WebGLRenderingContextBase['LINES'] | WebGLRenderingContextBase['TRIANGLES'] | WebGL2RenderingContext['LINE_STRIP'];

function getTokenizedAttributesAndUniforms(array: Array<string>): Array<string> {
const result = [];
Expand Down Expand Up @@ -75,8 +75,6 @@ class Program<Us extends UniformBindings> {
defines.push('#define TERRAIN3D;');
}

defines.unshift('#version 300 es');

const fragmentSource = defines.concat(shaders.prelude.fragmentSource, source.fragmentSource).join('\n');
const vertexSource = defines.concat(shaders.prelude.vertexSource, source.vertexSource).join('\n');

Expand Down Expand Up @@ -111,7 +109,6 @@ class Program<Us extends UniformBindings> {
}

gl.linkProgram(this.program);

gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);

Expand Down
6 changes: 3 additions & 3 deletions src/render/texture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import type Context from '../gl/context';
import type {RGBAImage, AlphaImage} from '../util/image';
import {isImageBitmap} from '../util/util';

export type TextureFormat = typeof WebGL2RenderingContext.RGBA | typeof WebGL2RenderingContext.ALPHA;
export type TextureFilter = typeof WebGL2RenderingContext.LINEAR | typeof WebGL2RenderingContext.LINEAR_MIPMAP_NEAREST | typeof WebGL2RenderingContext.NEAREST;
export type TextureWrap = typeof WebGL2RenderingContext.REPEAT | typeof WebGL2RenderingContext.CLAMP_TO_EDGE | typeof WebGL2RenderingContext.MIRRORED_REPEAT;
export type TextureFormat = WebGLRenderingContextBase['RGBA'] | WebGLRenderingContextBase['ALPHA'];
export type TextureFilter = WebGLRenderingContextBase['LINEAR'] | WebGLRenderingContextBase['LINEAR_MIPMAP_NEAREST'] | WebGLRenderingContextBase['NEAREST'];
export type TextureWrap = WebGLRenderingContextBase['REPEAT'] | WebGLRenderingContextBase['CLAMP_TO_EDGE'] | WebGLRenderingContextBase['MIRRORED_REPEAT'];

type EmptyImage = {
width: number;
Expand Down
2 changes: 1 addition & 1 deletion src/render/uniform_binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type UniformValues<Us extends {}> = $ObjMap<Us, <V>(u: Uniform<V>) => V>;
export type UniformLocations = {[_: string]: WebGLUniformLocation};

abstract class Uniform<T> {
gl: WebGL2RenderingContext;
gl: WebGLRenderingContext|WebGL2RenderingContext;
location: WebGLUniformLocation;
current: T;

Expand Down
10 changes: 5 additions & 5 deletions src/shaders/shaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export default shaders;
function compile(fragmentSource, vertexSource) {
const re = /#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g;

const staticAttributes = vertexSource.match(/(attribute|in) ([\w]+) ([\w]+)/g);
const staticAttributes = vertexSource.match(/attribute ([\w]+) ([\w]+)/g);
const fragmentUniforms = fragmentSource.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g);
const vertexUniforms = vertexSource.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g);
const staticUniforms = vertexUniforms ? vertexUniforms.concat(fragmentUniforms) : fragmentUniforms;
Expand All @@ -109,7 +109,7 @@ function compile(fragmentSource, vertexSource) {
if (operation === 'define') {
return `
#ifndef HAS_UNIFORM_u_${name}
in ${precision} ${type} ${name};
varying ${precision} ${type} ${name};
#else
uniform ${precision} ${type} u_${name};
#endif
Expand All @@ -132,8 +132,8 @@ uniform ${precision} ${type} u_${name};
return `
#ifndef HAS_UNIFORM_u_${name}
uniform lowp float u_${name}_t;
in ${precision} ${attrType} a_${name};
out ${precision} ${type} ${name};
attribute ${precision} ${attrType} a_${name};
varying ${precision} ${type} ${name};
#else
uniform ${precision} ${type} u_${name};
#endif
Expand Down Expand Up @@ -163,7 +163,7 @@ uniform ${precision} ${type} u_${name};
return `
#ifndef HAS_UNIFORM_u_${name}
uniform lowp float u_${name}_t;
in ${precision} ${attrType} a_${name};
attribute ${precision} ${attrType} a_${name};
#else
uniform ${precision} ${type} u_${name};
#endif
Expand Down
Loading