gridviz 3.0.36 → 3.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  'use strict'
3
3
 
4
4
  import { Style } from '../core/Style.js'
5
- import { makeWebGLCanvas } from '../utils/webGLUtils.js'
5
+ import { getSharedWebGLCanvas } from '../utils/webGLUtils.js'
6
6
  import { WebGLSquareColoringAdvanced } from '../utils/WebGLSquareColoringAdvanced.js'
7
7
 
8
8
  /**
@@ -80,11 +80,11 @@ export class SquareColorWebGLStyle extends Style {
80
80
  //get view scale
81
81
  const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
82
82
 
83
- //create canvas and webgl renderer
83
+ //the shared webgl canvas (one context for all styles, instead of a new one per draw)
84
84
  //for opacity control, see: https://webglfundamentals.org/webgl/lessons/webgl-and-alpha.html
85
- const cvWGL = makeWebGLCanvas(
86
- geoCanvas.w + '',
87
- geoCanvas.h + '',
85
+ const cvWGL = getSharedWebGLCanvas(
86
+ geoCanvas.w,
87
+ geoCanvas.h,
88
88
  this.opacity != undefined ? { premultipliedAlpha: false } : undefined
89
89
  )
90
90
  if (!cvWGL) {
@@ -1,28 +1,10 @@
1
1
  //@ts-check
2
2
  'use strict'
3
3
 
4
- import { initShaderProgram, createShader } from './webGLUtils.js'
4
+ import { getProgram, getBuffer } from './webGLUtils.js'
5
5
  import { color } from 'd3-color'
6
6
 
7
- /**
8
- * Everything to easily draw colored squares with webGL.
9
- * All the same size, but different fill color.
10
- */
11
- export class WebGLSquareColoring {
12
- /**
13
- *
14
- * @param {WebGLRenderingContext} gl
15
- */
16
- constructor(gl, sizePix) {
17
- this.gl = gl
18
- this.sizePix = sizePix || 10.0
19
-
20
- this.program = initShaderProgram(
21
- gl,
22
- createShader(
23
- gl,
24
- gl.VERTEX_SHADER,
25
- `
7
+ const VERTEX_SHADER = `
26
8
  attribute vec2 pos;
27
9
  uniform float sizePix;
28
10
  uniform mat3 mat;
@@ -34,11 +16,8 @@ export class WebGLSquareColoring {
34
16
  vColor = color;
35
17
  }
36
18
  `
37
- ),
38
- createShader(
39
- gl,
40
- gl.FRAGMENT_SHADER,
41
- `
19
+
20
+ const FRAGMENT_SHADER = `
42
21
  precision mediump float;
43
22
  varying vec4 vColor;
44
23
  void main(void) {
@@ -46,8 +25,22 @@ export class WebGLSquareColoring {
46
25
  vColor_[3] = 255.0 * vColor_[3];
47
26
  gl_FragColor = vColor_;
48
27
  }`
49
- )
50
- )
28
+
29
+ /**
30
+ * Everything to easily draw colored squares with webGL.
31
+ * All the same size, but different fill color.
32
+ */
33
+ export class WebGLSquareColoring {
34
+ /**
35
+ *
36
+ * @param {WebGLRenderingContext} gl
37
+ */
38
+ constructor(gl, sizePix) {
39
+ this.gl = gl
40
+ this.sizePix = sizePix || 10.0
41
+
42
+ // compiled once per context, then reused
43
+ this.program = getProgram(gl, VERTEX_SHADER, FRAGMENT_SHADER)
51
44
  gl.useProgram(this.program)
52
45
 
53
46
  //buffer data
@@ -79,9 +72,11 @@ export class WebGLSquareColoring {
79
72
  draw(transfoMat) {
80
73
  const gl = this.gl
81
74
 
82
- //vertice data
83
- gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
84
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.verticesBuffer), gl.STATIC_DRAW)
75
+ gl.useProgram(this.program)
76
+
77
+ //vertice data, in a buffer reused from draw to draw
78
+ gl.bindBuffer(gl.ARRAY_BUFFER, getBuffer(gl, 'WebGLSquareColoring.pos'))
79
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.verticesBuffer), gl.DYNAMIC_DRAW)
85
80
  const position = gl.getAttribLocation(this.program, 'pos')
86
81
  gl.vertexAttribPointer(
87
82
  position,
@@ -94,8 +89,8 @@ export class WebGLSquareColoring {
94
89
  gl.enableVertexAttribArray(position)
95
90
 
96
91
  //color data
97
- gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
98
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.colorsBuffer), gl.STATIC_DRAW)
92
+ gl.bindBuffer(gl.ARRAY_BUFFER, getBuffer(gl, 'WebGLSquareColoring.color'))
93
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.colorsBuffer), gl.DYNAMIC_DRAW)
99
94
  var color = gl.getAttribLocation(this.program, 'color')
100
95
  gl.vertexAttribPointer(color, 4, gl.FLOAT, false, 0, 0)
101
96
  gl.enableVertexAttribArray(color)
@@ -109,6 +104,7 @@ export class WebGLSquareColoring {
109
104
  // Enable the depth test
110
105
  //gl.enable(gl.DEPTH_TEST);
111
106
  // Clear the color buffer bit
107
+ gl.clearColor(0, 0, 0, 0)
112
108
  gl.clear(gl.COLOR_BUFFER_BIT)
113
109
  // Set the view port
114
110
  //gl.viewport(0, 0, cg.w, cg.h);
@@ -1,9 +1,24 @@
1
1
  //@ts-check
2
2
  'use strict'
3
3
 
4
- import { initShaderProgram, createShader } from './webGLUtils.js'
4
+ import { getProgram, getBuffer } from './webGLUtils.js'
5
5
  import { color } from 'd3-color'
6
6
 
7
+ const VERTEX_SHADER = `
8
+ attribute vec2 pos;
9
+ uniform float sizePix;
10
+ uniform mat3 mat;
11
+
12
+ attribute float t;
13
+ varying float vt;
14
+
15
+ void main() {
16
+ gl_Position = vec4(mat * vec3(pos, 1.0), 1.0);
17
+ gl_PointSize = sizePix;
18
+ vt = t;
19
+ }
20
+ `
21
+
7
22
  /**
8
23
  * Everything to easily draw colored squares with webGL.
9
24
  * All the same size, but different fill color.
@@ -31,26 +46,6 @@ export class WebGLSquareColoringAdvanced {
31
46
  //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
32
47
  //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
33
48
 
34
- /** @type {WebGLShader} */
35
- const vShader = createShader(
36
- gl,
37
- gl.VERTEX_SHADER,
38
- `
39
- attribute vec2 pos;
40
- uniform float sizePix;
41
- uniform mat3 mat;
42
-
43
- attribute float t;
44
- varying float vt;
45
-
46
- void main() {
47
- gl_Position = vec4(mat * vec3(pos, 1.0), 1.0);
48
- gl_PointSize = sizePix;
49
- vt = t;
50
- }
51
- `
52
- )
53
-
54
49
  //prepare fragment shader code
55
50
  //declare the uniform and other variables
56
51
  const fshBuff = []
@@ -157,11 +152,9 @@ export class WebGLSquareColoringAdvanced {
157
152
  //let fshString = fshBuff.join("\n")
158
153
  //console.log(fshString)
159
154
 
160
- /** @type {WebGLShader} */
161
- const fShader = createShader(gl, gl.FRAGMENT_SHADER, fshString)
162
-
155
+ // compiled once per context and fragment source (colours, stretching), then reused
163
156
  /** @type {WebGLProgram} */
164
- this.program = initShaderProgram(gl, vShader, fShader)
157
+ this.program = getProgram(gl, VERTEX_SHADER, fshString)
165
158
  gl.useProgram(this.program)
166
159
 
167
160
  //set uniforms
@@ -191,9 +184,11 @@ export class WebGLSquareColoringAdvanced {
191
184
  const gl = this.gl
192
185
  const program = this.program
193
186
 
194
- //vertice data
195
- gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
196
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesBuffer), gl.STATIC_DRAW)
187
+ gl.useProgram(program)
188
+
189
+ //vertice data, in a buffer reused from draw to draw
190
+ gl.bindBuffer(gl.ARRAY_BUFFER, getBuffer(gl, 'WebGLSquareColoringAdvanced.pos'))
191
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesBuffer), gl.DYNAMIC_DRAW)
197
192
  const position = gl.getAttribLocation(program, 'pos')
198
193
  gl.vertexAttribPointer(
199
194
  position,
@@ -206,8 +201,8 @@ export class WebGLSquareColoringAdvanced {
206
201
  gl.enableVertexAttribArray(position)
207
202
 
208
203
  //t data
209
- gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
210
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tBuffer), gl.STATIC_DRAW)
204
+ gl.bindBuffer(gl.ARRAY_BUFFER, getBuffer(gl, 'WebGLSquareColoringAdvanced.t'))
205
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tBuffer), gl.DYNAMIC_DRAW)
211
206
  const t = gl.getAttribLocation(program, 't')
212
207
  gl.vertexAttribPointer(t, 1, gl.FLOAT, false, 0, 0)
213
208
  gl.enableVertexAttribArray(t)
@@ -218,6 +213,7 @@ export class WebGLSquareColoringAdvanced {
218
213
  // Enable the depth test
219
214
  //gl.enable(gl.DEPTH_TEST);
220
215
  // Clear the color buffer bit
216
+ gl.clearColor(0, 0, 0, 0)
221
217
  gl.clear(gl.COLOR_BUFFER_BIT)
222
218
  // Set the view port
223
219
  //gl.viewport(0, 0, cg.w, cg.h);
@@ -20,6 +20,111 @@ export function makeWebGLCanvas(width, height, opts = {}) {
20
20
  return { canvas: canvas, gl: gl, width: width, height: height }
21
21
  }
22
22
 
23
+ /**
24
+ * The shared, off-page WebGL canvases, one per set of context options.
25
+ * Styles draw into one of these and copy the result onto the map's canvas, one style at a
26
+ * time, so they never need a context each. Browsers keep only ~16 live WebGL contexts and
27
+ * drop the oldest beyond that, so one context per style instance (or worse, per draw)
28
+ * eventually blanks other WebGL content on the page.
29
+ * @type {Map<string, {canvas:HTMLCanvasElement, gl:WebGLRenderingContext, width:number, height:number}>} */
30
+ const sharedCanvases = new Map()
31
+
32
+ /**
33
+ * Returns the shared WebGL canvas for these context options, resized to width x height.
34
+ * Resizing keeps the context (and its compiled programs and buffers); only its drawing
35
+ * buffer is reallocated. A lost context is replaced.
36
+ *
37
+ * @param {number} width
38
+ * @param {number} height
39
+ * @param {object} [opts] WebGL context attributes, e.g. { premultipliedAlpha: false }
40
+ * @returns {{canvas:HTMLCanvasElement, gl:WebGLRenderingContext, width:number, height:number }}
41
+ */
42
+ export function getSharedWebGLCanvas(width, height, opts = undefined) {
43
+ const key = JSON.stringify(opts || {})
44
+ let cv = sharedCanvases.get(key)
45
+ if (!cv || cv.gl.isContextLost()) {
46
+ cv = makeWebGLCanvas(width + '', height + '', opts)
47
+ cv.width = +width
48
+ cv.height = +height
49
+ sharedCanvases.set(key, cv)
50
+ }
51
+ if (cv.width != width || cv.height != height) {
52
+ cv.canvas.width = width
53
+ cv.canvas.height = height
54
+ cv.width = +width
55
+ cv.height = +height
56
+ }
57
+ cv.gl.viewport(0, 0, cv.width, cv.height)
58
+ return cv
59
+ }
60
+
61
+ /** @type {WeakMap<WebGLRenderingContext, {programs: Map<string, WebGLProgram>, buffers: Map<string, WebGLBuffer>, owners: Map<string, any>}>} */
62
+ const glResources = new WeakMap()
63
+ function resourcesOf(gl) {
64
+ let r = glResources.get(gl)
65
+ if (!r) {
66
+ r = { programs: new Map(), buffers: new Map(), owners: new Map() }
67
+ glResources.set(gl, r)
68
+ }
69
+ return r
70
+ }
71
+
72
+ /**
73
+ * A shader program for these sources, compiled once per context and reused.
74
+ *
75
+ * @param {WebGLRenderingContext} gl
76
+ * @param {string} vertexSource
77
+ * @param {string} fragmentSource
78
+ * @returns {WebGLProgram}
79
+ */
80
+ export function getProgram(gl, vertexSource, fragmentSource) {
81
+ const programs = resourcesOf(gl).programs
82
+ const key = vertexSource + '\u0000' + fragmentSource
83
+ let program = programs.get(key)
84
+ if (!program) {
85
+ program = initShaderProgram(
86
+ gl,
87
+ createShader(gl, gl.VERTEX_SHADER, vertexSource),
88
+ createShader(gl, gl.FRAGMENT_SHADER, fragmentSource)
89
+ )
90
+ programs.set(key, program)
91
+ }
92
+ return program
93
+ }
94
+
95
+ /**
96
+ * A named buffer of a context, created once and reused: refill it with bufferData instead
97
+ * of creating a new buffer per draw (buffers are never garbage collected while their
98
+ * context lives).
99
+ *
100
+ * @param {WebGLRenderingContext} gl
101
+ * @param {string} name
102
+ * @returns {WebGLBuffer}
103
+ */
104
+ export function getBuffer(gl, name) {
105
+ const buffers = resourcesOf(gl).buffers
106
+ let buffer = buffers.get(name)
107
+ if (!buffer) {
108
+ buffer = gl.createBuffer()
109
+ buffers.set(name, buffer)
110
+ }
111
+ return buffer
112
+ }
113
+
114
+ /**
115
+ * Who last filled a named buffer, so a style can tell whether the data it uploaded is
116
+ * still there or another style has overwritten it since.
117
+ * @param {WebGLRenderingContext} gl
118
+ * @param {string} name
119
+ * @param {any} [owner] sets the owner when given
120
+ * @returns {any} the owner
121
+ */
122
+ export function bufferOwner(gl, name, owner = undefined) {
123
+ const owners = resourcesOf(gl).owners
124
+ if (owner !== undefined) owners.set(name, owner)
125
+ return owners.get(name)
126
+ }
127
+
23
128
  /**
24
129
  * Initialize a shader program, so WebGL knows how to draw our data
25
130
  *