gridviz icon indicating copy to clipboard operation
gridviz copied to clipboard

Performance

Open joewdavies opened this issue 11 months ago • 2 comments
trafficstars

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 avatar Nov 23 '24 11:11 joewdavies