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

Performance #143

Open
joewdavies opened this issue Nov 23, 2024 · 2 comments
Open

Performance #143

joewdavies opened this issue Nov 23, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@joewdavies
Copy link
Collaborator

Opening a separate, broader issue for this as it is not necessarily related to the main thread.

The performance of 2D canvas is quite limited in most situations. Especially on mobile devices (open the lego style demo for example on your mobile and then compare it with the WebGL demo. The difference is huge.)

This got me thinking: would it make sense to port our 2D canvas logic to WebGL?

Here is a mock-up snippet:

class CanvasToWebGL {
  constructor(gl) {
    this.gl = gl;
    this.color = [1.0, 0.0, 0.0, 1.0]; // Default red color
    this.initShaders();
  }

  initShaders() {
    const vertexShaderSource = `
      attribute vec2 a_position;
      uniform vec2 u_resolution;
      void main() {
        vec2 zeroToOne = a_position / u_resolution;
        vec2 zeroToTwo = zeroToOne * 2.0;
        vec2 clipSpace = zeroToTwo - 1.0;
        gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
      }`;

    const fragmentShaderSource = `
      precision mediump float;
      uniform vec4 u_color;
      void main() {
        gl_FragColor = u_color;
      }`;

    // Compile shaders and link program
    const vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexShaderSource);
    const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentShaderSource);
    this.program = this.createProgram(vertexShader, fragmentShader);

    // Look up attribute/uniform locations
    this.positionLocation = this.gl.getAttribLocation(this.program, "a_position");
    this.resolutionLocation = this.gl.getUniformLocation(this.program, "u_resolution");
    this.colorLocation = this.gl.getUniformLocation(this.program, "u_color");

    // Create buffer for rectangle vertices
    this.positionBuffer = this.gl.createBuffer();
  }

  createShader(type, source) {
    const shader = this.gl.createShader(type);
    this.gl.shaderSource(shader, source);
    this.gl.compileShader(shader);
    if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
      console.error(this.gl.getShaderInfoLog(shader));
      this.gl.deleteShader(shader);
      return null;
    }
    return shader;
  }

  createProgram(vertexShader, fragmentShader) {
    const program = this.gl.createProgram();
    this.gl.attachShader(program, vertexShader);
    this.gl.attachShader(program, fragmentShader);
    this.gl.linkProgram(program);
    if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
      console.error(this.gl.getProgramInfoLog(program));
      this.gl.deleteProgram(program);
      return null;
    }
    return program;
  }

  setFillColor(r, g, b, a = 1.0) {
    this.color = [r, g, b, a];
  }

  fillRect(x, y, width, height) {
    const gl = this.gl;

    // Set up rectangle vertices
    const x1 = x, y1 = y;
    const x2 = x + width, y2 = y + height;
    const vertices = new Float32Array([
      x1, y1, x2, y1, x1, y2,
      x1, y2, x2, y1, x2, y2,
    ]);

    // Bind buffer and upload data
    gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    // Use program and set uniforms
    gl.useProgram(this.program);
    gl.uniform2f(this.resolutionLocation, gl.canvas.width, gl.canvas.height);
    gl.uniform4fv(this.colorLocation, this.color);

    // Enable attribute and set up pointer
    gl.enableVertexAttribArray(this.positionLocation);
    gl.vertexAttribPointer(this.positionLocation, 2, gl.FLOAT, false, 0, 0);

    // Draw rectangle
    gl.drawArrays(gl.TRIANGLES, 0, 6);
  }
}

// Usage:
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");
const webGLCanvas = new CanvasToWebGL(gl);

webGLCanvas.setFillColor(1, 0, 0); // Red color
webGLCanvas.fillRect(10, 10, 100, 50);
@joewdavies joewdavies added the enhancement New feature or request label Nov 23, 2024
@joewdavies
Copy link
Collaborator Author

This might also fix the issues that mobile users face when panning/zooming #98 (comment)

@joewdavies
Copy link
Collaborator Author

Fetching grid data with a web worker?

On the topic of performance but regarding fetching grid data rather than rendering it:

A web worker could be employed to fetch gridded data around the current map extent/zoom level. It will essentially fill the cache accordingly, therefore in some cases reducing the time between a pan or zoom event and a redraw.

Regarding the usage of web workers:

Simply create a worker.js file:

// worker.js
self.onmessage = function (event) {
  // Do some heavy computation
  const result = event.data * 2; // Example task: doubling the input
  self.postMessage(result); // Send the result back to the main thread
};

then use it like so:

import MyWorker from './worker.worker.js';

const worker = new MyWorker(); // Create a new instance of the Web Worker

worker.onmessage = function (event) {
  console.log('Worker result:', event.data); // Handle result from worker
};

worker.postMessage(5); // Send data to the worker

and add this to the webpack config:

    module: {
        rules: [
            {
                test: /\.worker\.js$/,
                use: { loader: 'worker-loader' },
            },
            // other rules
        ],
    },

Considerations:

  • Limit how much data is preloaded and avoid overloading the system.
  • Cache should be managed intelligently ( using a Least Recently Used (LRU) Cache )
  • If necessary, ensure a worker is terminated after completing its task to free up resources worker.terminate()
  • It's important to ensure that loading data behind the scenes doesn't introduce noticeable delays when the user interacts with the map.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant