UNPKG

s2maps-gpu

Version:

S2 Maps GPU - An open source, high-performance, and GPU-accelerated map engine for rendering large-scale, interactive maps.

187 lines (186 loc) 7.97 kB
import Workflow from './workflow.js'; // WEBGL1 import frag1 from '../shaders/glyphFilter1.fragment.glsl'; import vert1 from '../shaders/glyphFilter1.vertex.glsl'; // WEBGL2 import frag2 from '../shaders/glyphFilter2.fragment.glsl'; import vert2 from '../shaders/glyphFilter2.vertex.glsl'; /** Glyph Filter Feature is a standalone glyph filter compute storage unit that can be processed by the GPU */ export default class GlyphFilterWorkflow extends Workflow { label = 'glyphFilter'; quadTexture; resultTexture; quadFramebuffer; resultFramebuffer; indexOffset = 0; mode = 1; /** @param context - The WebGL(1|2) context */ constructor(context) { // get gl from context const { type } = context; // inject Program super(context); // build shaders if (type === 1) this.buildShaders(vert1, frag1, { aStep: 0, aST: 1, aXY: 2, aOffset: 3, aPad: 4, aWH: 5, aIndex: 6, aID: 7, }); else this.buildShaders(vert2, frag2); // finish building the textures this.#buildTextures(); } /** Build a texture that holds the glyph data */ #buildTextures() { const { gl, devicePixelRatio } = this.context; this.use(); // setup the devicePixelRatio this.setDevicePixelRatio(devicePixelRatio); // TEXTURES const quadTexture = gl.createTexture(); if (quadTexture === null) throw new Error('Failed to create GlyphFilter:quadTexture'); this.quadTexture = quadTexture; const resultTexture = gl.createTexture(); if (resultTexture === null) throw new Error('Failed to create GlyphFilter:resultTexture'); this.resultTexture = resultTexture; // result texture gl.bindTexture(gl.TEXTURE_2D, resultTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.canvas.width, gl.canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); // set filter system gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); // quad texture using floats gl.bindTexture(gl.TEXTURE_2D, quadTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 4_096, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); // set filter system gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); // FRAMEBUFFERS // QUAD FRAMEBUFFER const quadFramebuffer = gl.createFramebuffer(); if (quadFramebuffer === null) throw new Error('Failed to create GlyphFilter:quadFramebuffer'); this.quadFramebuffer = quadFramebuffer; gl.bindFramebuffer(gl.FRAMEBUFFER, quadFramebuffer); // attach quadTexture to quadFramebuffer gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, quadTexture, 0); // RESULT FRAMEBUFFER const resultFramebuffer = gl.createFramebuffer(); if (resultFramebuffer === null) throw new Error('Failed to create GlyphFilter:resultFramebuffer'); this.resultFramebuffer = resultFramebuffer; gl.bindFramebuffer(gl.FRAMEBUFFER, resultFramebuffer); // attach quadTexture to resultFramebuffer gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, resultTexture, 0); // we are finished, so go back to our main buffer gl.bindFramebuffer(gl.FRAMEBUFFER, null); } /** Delete the glyph filter workflow */ delete() { const { gl, quadTexture, resultTexture, quadFramebuffer, resultFramebuffer } = this; // delete textures gl.deleteTexture(quadTexture); gl.deleteTexture(resultTexture); // delete framebuffers gl.deleteFramebuffer(quadFramebuffer); gl.deleteFramebuffer(resultFramebuffer); // cleanup super.delete(); } /** Resize the glyph filter workflow */ resize() { const { gl, resultTexture } = this; // bind the resultTexture gl.bindTexture(gl.TEXTURE_2D, resultTexture); // update the texture's aspect gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.canvas.width, gl.canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); } /** * Set the draw mode * @param mode - the draw mode */ setMode(mode) { this.mode = mode; super.setMode(mode); } /** Bind the resultant computed texture to a uniform texture that can be accessed by the glyph shaders */ bindResultTexture() { const { gl } = this; gl.bindTexture(gl.TEXTURE_2D, this.resultTexture); } /** Bind the quad framebuffer */ bindQuadFrameBuffer() { const { context, gl } = this; gl.bindFramebuffer(gl.FRAMEBUFFER, this.quadFramebuffer); context.clearColorBuffer(); context.disableBlend(); gl.viewport(0, 0, 4_096, 2); gl.bindTexture(gl.TEXTURE_2D, this.resultTexture); // clear indexOffset this.indexOffset = 0; } /** Bind the result framebuffer */ bindResultFramebuffer() { const { context, gl } = this; gl.bindFramebuffer(gl.FRAMEBUFFER, this.resultFramebuffer); context.defaultBlend(); context.resetViewport(); context.clearColorBuffer(); gl.bindTexture(gl.TEXTURE_2D, this.quadTexture); // clear indexOffset this.indexOffset = 0; } /** * Draw the glyph filter features to the compute texture * @param feature - feature to draw * @param _interactive - whether or not the feature is interactive */ draw(feature, _interactive = false) { const { gl, context, mode, uniforms, indexOffset } = this; const { type } = context; // set current indexOffset gl.uniform1f(uniforms.uIndexOffset, indexOffset); // grab variables const { featureCode, filterCount, filterOffset, source, size } = feature; const { glyphFilterBuffer, glyphFilterIDBuffer, filterVAO } = source; // set feature code if (type === 1) { gl.uniform1f(uniforms.uSize, size ?? 1); } else { this.setFeatureCode(featureCode); } // use vao gl.bindVertexArray(filterVAO); // apply the appropriate offset gl.bindBuffer(gl.ARRAY_BUFFER, glyphFilterBuffer); gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 44, filterOffset * 44); // s, t gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 44, 8 + filterOffset * 44); // x, y gl.vertexAttribPointer(3, 2, gl.FLOAT, false, 44, 16 + filterOffset * 44); // offsetX, offsetY gl.vertexAttribPointer(4, 2, gl.FLOAT, false, 44, 24 + filterOffset * 44); // paddingX, paddingY gl.vertexAttribPointer(5, 2, gl.FLOAT, false, 44, 32 + filterOffset * 44); // width, height gl.vertexAttribPointer(6, 1, gl.FLOAT, false, 44, 40 + filterOffset * 44); // index gl.bindBuffer(gl.ARRAY_BUFFER, glyphFilterIDBuffer); gl.vertexAttribPointer(7, 4, gl.UNSIGNED_BYTE, true, 4, filterOffset * 4); // draw based upon mode if (mode === 1) gl.drawArraysInstanced(gl.POINTS, 0, 2, filterCount); else gl.drawArraysInstanced(gl.POINTS, 0, 1, filterCount); // increment offset this.indexOffset += filterCount; } }