]> zoso.dev Git - libnemo.git/commitdiff
Speed up initial PoW calculation by caching the canvas and context when worker is...
authorChris Duncan <chris@zoso.dev>
Thu, 12 Dec 2024 06:14:39 +0000 (22:14 -0800)
committerChris Duncan <chris@zoso.dev>
Thu, 12 Dec 2024 06:14:39 +0000 (22:14 -0800)
src/lib/workers/pow.ts

index e5534bd2402f199b992aa2cc5f0d47b3267dda24..ce89a60d7b4d3287b48eb35ee16e290ae3fd4636 100644 (file)
@@ -29,8 +29,9 @@ export class Pow {
        // Both width and height must be multiple of 256, (one byte)
        // but do not need to be the same,
        // matching GPU capabilities is the aim
-       static webglWidth = 256 * 8
-       static webglHeight = 256 * 8
+       static webglWidth = 256 * 4
+       static webglHeight = 256 * 4
+       static gl = new OffscreenCanvas(this.webglWidth, this.webglHeight).getContext('webgl2')
 
        static hexify (arr: number[] | Uint8Array): string {
                let out = ''
@@ -49,15 +50,10 @@ export class Pow {
                        reverseHex += hashHex.slice(i - 2, i)
                }
 
-               const canvas = new OffscreenCanvas(this.webglWidth, this.webglHeight)
-               const gl = canvas.getContext('webgl2')
-
-               if (!gl)
+               if (this.gl == null)
                        throw new Error('webgl2_required')
 
-               gl.clearColor(0, 0, 0, 1)
-
-               console.log(`color cleared: ${performance.now()}`)
+               this.gl.clearColor(0, 0, 0, 1)
 
                // Vertext Shader
                const vsSource = `#version 300 es
@@ -186,8 +182,8 @@ export class Pow {
 
                void main() {
                        int i;
-                       uint uv_x = uint(uv_pos.x * ${canvas.width - 1}.);
-                       uint uv_y = uint(uv_pos.y * ${canvas.height - 1}.);
+                       uint uv_x = uint(uv_pos.x * ${this.webglWidth - 1}.);
+                       uint uv_y = uint(uv_pos.y * ${this.webglHeight - 1}.);
                        uint x_pos = uv_x % 256u;
                        uint y_pos = uv_y % 256u;
                        uint x_index = (uv_x - x_pos) / 256u;
@@ -235,87 +231,100 @@ export class Pow {
                        }
                }`
 
-               const vertexShader = gl.createShader(gl.VERTEX_SHADER)
+               console.log(`shaders start: ${performance.now()}`)
+               const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER)
                if (vertexShader == null)
                        throw 'error creating vertex shader'
-               gl.shaderSource(vertexShader, vsSource)
-               gl.compileShader(vertexShader)
+               this.gl.shaderSource(vertexShader, vsSource)
+               this.gl.compileShader(vertexShader)
 
-               if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS))
-                       throw gl.getShaderInfoLog(vertexShader)
+               if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS))
+                       throw this.gl.getShaderInfoLog(vertexShader)
 
-               const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
+               const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER)
                if (fragmentShader == null)
                        throw 'error creating fragment shader'
-               gl.shaderSource(fragmentShader, fsSource)
-               gl.compileShader(fragmentShader)
+               this.gl.shaderSource(fragmentShader, fsSource)
+               this.gl.compileShader(fragmentShader)
 
-               if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS))
-                       throw gl.getShaderInfoLog(fragmentShader)
+               if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS))
+                       throw this.gl.getShaderInfoLog(fragmentShader)
 
-               const program = gl.createProgram()
+               const program = this.gl.createProgram()
                if (program == null)
                        throw 'error creating program'
-               gl.attachShader(program, vertexShader)
-               gl.attachShader(program, fragmentShader)
-               gl.linkProgram(program)
+               this.gl.attachShader(program, vertexShader)
+               this.gl.attachShader(program, fragmentShader)
+               this.gl.linkProgram(program)
 
-               if (!gl.getProgramParameter(program, gl.LINK_STATUS))
-                       throw gl.getProgramInfoLog(program)
+               if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS))
+                       throw this.gl.getProgramInfoLog(program)
 
-               gl.useProgram(program)
+               this.gl.useProgram(program)
 
                // Construct simple 2D geometry
-               const triangleArray = gl.createVertexArray()
-               gl.bindVertexArray(triangleArray)
+               const triangleArray = this.gl.createVertexArray()
+               this.gl.bindVertexArray(triangleArray)
 
                // Vertex Positions, 2 triangles
                const positions = new Float32Array([
                        -1, -1, 0, -1, 1, 0, 1, 1, 0,
                        1, -1, 0, 1, 1, 0, -1, -1, 0
                ])
-               const positionBuffer = gl.createBuffer()
-               gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
-               gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)
-               gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0)
-               gl.enableVertexAttribArray(0)
+               const positionBuffer = this.gl.createBuffer()
+               this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer)
+               this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW)
+               this.gl.vertexAttribPointer(0, 3, this.gl.FLOAT, false, 0, 0)
+               this.gl.enableVertexAttribArray(0)
 
                // Texture Positions
                const uvPosArray = new Float32Array([
                        1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1
                ])
-               const uvBuffer = gl.createBuffer()
-               gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer)
-               gl.bufferData(gl.ARRAY_BUFFER, uvPosArray, gl.STATIC_DRAW)
-               gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0)
-               gl.enableVertexAttribArray(1)
+               const uvBuffer = this.gl.createBuffer()
+               this.gl.bindBuffer(this.gl.ARRAY_BUFFER, uvBuffer)
+               this.gl.bufferData(this.gl.ARRAY_BUFFER, uvPosArray, this.gl.STATIC_DRAW)
+               this.gl.vertexAttribPointer(1, 2, this.gl.FLOAT, false, 0, 0)
+               this.gl.enableVertexAttribArray(1)
 
-               const work0Location = gl.getUniformLocation(program, 'u_work0')
-               const work1Location = gl.getUniformLocation(program, 'u_work1')
+               const work0Location = this.gl.getUniformLocation(program, 'u_work0')
+               const work1Location = this.gl.getUniformLocation(program, 'u_work1')
 
                // Draw output until success or progressCallback says to stop
                const work0 = new Uint8Array(4)
                const work1 = new Uint8Array(4)
+               console.log(`shaders end: ${performance.now()}`)
 
                const draw = (): void => {
+                       if (this.gl == null) throw new Error('webgl2_required')
+                       const start = performance.now()
+                       console.log(`crypto start: ${performance.now()}`)
                        crypto.getRandomValues(work0)
                        crypto.getRandomValues(work1)
+                       console.log(`crypto end: ${performance.now()}`)
 
-                       gl.uniform4uiv(work0Location, work0)
-                       gl.uniform4uiv(work1Location, work1)
+                       console.log(`uiv start: ${performance.now()}`)
+                       this.gl.uniform4uiv(work0Location, work0)
+                       this.gl.uniform4uiv(work1Location, work1)
+                       console.log(`uiv end: ${performance.now()}`)
 
-                       gl.clear(gl.COLOR_BUFFER_BIT)
-                       gl.drawArrays(gl.TRIANGLES, 0, 6)
-                       const pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4)
+                       console.log(`clear start: ${performance.now()}`)
+                       this.gl.clear(this.gl.COLOR_BUFFER_BIT)
+                       console.log(`clear end: ${performance.now()}`)
+                       console.log(`draw start: ${performance.now()}`)
+                       this.gl.drawArrays(this.gl.TRIANGLES, 0, 6)
+                       console.log(`draw end: ${performance.now()}`)
+                       const pixels = new Uint8Array(this.gl.drawingBufferWidth * this.gl.drawingBufferHeight * 4)
 
                        console.log(`start readPixels: ${performance.now()}`)
-                       gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
+                       this.gl.readPixels(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixels)
                        console.log(`end readPixels: ${performance.now()}`)
 
                        // Check the pixels for any success
-                       for (let i = 0; i < pixels.length; i += 4) {
+                       for (let i = pixels.length - 4; i >= 0; i -= 4) {
                                if (pixels[i] !== 0) {
                                        console.log(`found: ${performance.now()}`)
+                                       console.log(`frame time: ${performance.now() - start}`)
                                        // Return the work value with the custom bits
                                        typeof callback === 'function' &&
                                                callback(this.hexify(work1) + this.hexify([
@@ -327,11 +336,11 @@ export class Pow {
                                        return
                                }
                        }
+                       console.log(`frame time: ${performance.now() - start}`)
                        // Nothing found yet, try again
                        self.requestAnimationFrame(draw)
                }
 
-               console.log(`starting first draw: ${performance.now()}`)
                // Begin generation
                self.requestAnimationFrame(draw)
        }