From: Chris Duncan Date: Wed, 18 Dec 2024 16:42:15 +0000 (-0800) Subject: Get query version working by storing and reading the entire canvas as before, probabl... X-Git-Url: https://zoso.dev/?a=commitdiff_plain;h=11ec999ecd1a1fdb287a02321117ded7db19fc6b;p=libnemo.git Get query version working by storing and reading the entire canvas as before, probably an issue with xy coordinates being used to fetch work bytes. Anyway, it's loads faster now, but loading up all 16 workers is a no-go, so that needs work. Also would be nice to reduce the canvas viewport and associated renderbuffer to a single pixel since that's all we need. --- diff --git a/src/lib/workers/pow.ts b/src/lib/workers/pow.ts index 3a6e359..d3c34c1 100644 --- a/src/lib/workers/pow.ts +++ b/src/lib/workers/pow.ts @@ -24,6 +24,7 @@ export class Pow { */ // Vertex Shader static #vsSource = `#version 300 es +#pragma vscode_glsllint_stage: vert precision highp float; layout (location=0) in vec4 position; layout (location=1) in vec2 uv; @@ -37,6 +38,7 @@ void main() { // Fragment shader static #fsSource = `#version 300 es +#pragma vscode_glsllint_stage: frag precision highp float; precision highp int; @@ -189,20 +191,20 @@ void main() { // Pixel data is multipled by threshold test result (0 or 1) // First 4 bytes insignificant, only calculate digest of second 4 bytes - fragColor = mix( - fragColor, - vec4( + if ((BLAKE2B_IV32_1 ^ v[1] ^ v[17]) > threshold) { + fragColor = vec4( float(x_index + 1u)/255., // +1 to distinguish from 0 (unsuccessful) pixels float(y_index + 1u)/255., // Same as previous float(x_pos)/255., // Return the 2 custom bytes used in work value float(y_pos)/255. // Second custom byte - ), - float((BLAKE2B_IV32_1 ^ v[1] ^ v[17]) > threshold) - ); + ); + } else { + discard; + } }` /** Used to set canvas size. Must be a multiple of 256. */ - static #WORKLOAD: number = 256 * Math.max(1, Math.floor(navigator.hardwareConcurrency / 2)) + static #WORKLOAD: number = 256 * Math.max(1, Math.floor(navigator.hardwareConcurrency)) static #hexify (arr: number[] | Uint8Array): string { let out = '' @@ -223,6 +225,9 @@ void main() { static #fragmentShader: WebGLShader | null static #positionBuffer: WebGLBuffer | null static #uvBuffer: WebGLBuffer | null + static #frameBuffer: WebGLFramebuffer | null + static #renderBuffer: WebGLRenderbuffer | null + static #query: WebGLQuery | null static #pixels: Uint8Array // Vertex Positions, 2 triangles static #positions = new Float32Array([ @@ -280,12 +285,28 @@ void main() { this.#gl.vertexAttribPointer(1, 2, this.#gl.FLOAT, false, 0, 0) this.#gl.enableVertexAttribArray(1) + this.#frameBuffer = this.#gl.createFramebuffer() + this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, this.#frameBuffer) + this.#renderBuffer = this.#gl.createRenderbuffer() + this.#gl.bindRenderbuffer(this.#gl.RENDERBUFFER, this.#renderBuffer) + this.#gl.renderbufferStorage(this.#gl.RENDERBUFFER, this.#gl.RGBA8, this.#gl.drawingBufferWidth, this.#gl.drawingBufferHeight) + this.#gl.framebufferRenderbuffer(this.#gl.FRAMEBUFFER, this.#gl.COLOR_ATTACHMENT0, this.#gl.RENDERBUFFER, this.#renderBuffer) + if (this.#gl.checkFramebufferStatus(this.#gl.FRAMEBUFFER) !== this.#gl.FRAMEBUFFER_COMPLETE) { + throw new Error('Framebuffer is not complete') + } + // this.#gl.viewport(0, 0, 1, 1) + // this.#pixels = new Uint8Array(4) + this.#pixels = new Uint8Array(this.#gl.drawingBufferWidth * this.#gl.drawingBufferHeight * 4) + this.#query = this.#gl.createQuery() + if (this.#query == null) { + throw new Error('Failed to create query') + } + this.#work0Location = this.#gl.getUniformLocation(this.#program, 'u_work0') this.#work1Location = this.#gl.getUniformLocation(this.#program, 'u_work1') this.#blockHashLocation = this.#gl.getUniformLocation(this.#program, "blockHash") this.#thresholdLocation = this.#gl.getUniformLocation(this.#program, "threshold") this.#workloadLocation = this.#gl.getUniformLocation(this.#program, "workload") - this.#pixels = new Uint8Array(this.#gl.drawingBufferWidth * this.#gl.drawingBufferHeight * 4) } static #calculate (hashHex: string, callback: (nonce: string | PromiseLike) => any, threshold: number): void { @@ -309,9 +330,11 @@ void main() { const draw = (): void => { n++ if (Pow.#gl == null) throw new Error('WebGL 2 is required') + if (Pow.#query == null) throw new Error('WebGL 2 is required to run queries') performance.mark('start') crypto.getRandomValues(work0) crypto.getRandomValues(work1) + Pow.#gl.clear(Pow.#gl.COLOR_BUFFER_BIT) Pow.#gl.uniform4uiv(Pow.#work0Location, work0) Pow.#gl.uniform4uiv(Pow.#work1Location, work1) @@ -319,8 +342,35 @@ void main() { Pow.#gl.uniform1ui(Pow.#thresholdLocation, threshold) Pow.#gl.uniform1f(Pow.#workloadLocation, Pow.#WORKLOAD - 1) - Pow.#gl.clear(Pow.#gl.COLOR_BUFFER_BIT) + Pow.#gl.beginQuery(Pow.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE, Pow.#query) Pow.#gl.drawArrays(Pow.#gl.TRIANGLES, 0, 6) + Pow.#gl.endQuery(Pow.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE) + + requestAnimationFrame(checkQueryResult) + } + + function checkQueryResult () { + if (Pow.#gl == null) throw new Error('WebGL 2 is required to check query results') + if (Pow.#query == null) throw new Error('Query not found') + if (Pow.#gl.getQueryParameter(Pow.#query, Pow.#gl.QUERY_RESULT_AVAILABLE)) { + const anySamplesPassed = Pow.#gl.getQueryParameter(Pow.#query, Pow.#gl.QUERY_RESULT) + if (anySamplesPassed) { + // A valid nonce was found + readBackResult() + } else { + performance.mark('end') + frameTimes.push(performance.measure('draw', 'start', 'end').duration) + performance.clearMarks() + // No valid nonce found, start the next draw call + requestAnimationFrame(draw) + } + } else { + // Query result not yet available, check again in the next frame + requestAnimationFrame(checkQueryResult) + } + } + function readBackResult () { + if (Pow.#gl == null) throw new Error('WebGL 2 is required to check read results') Pow.#gl.readPixels(0, 0, Pow.#gl.drawingBufferWidth, Pow.#gl.drawingBufferHeight, Pow.#gl.RGBA, Pow.#gl.UNSIGNED_BYTE, Pow.#pixels) // Check the pixels for any success for (let i = 0; i < Pow.#pixels.length; i += 4) { @@ -330,7 +380,7 @@ void main() { performance.clearMarks() console.log(`average frame time: ${(frameTimes.reduce((a, b) => a + b)) / frameTimes.length} ms`) console.log(`frames calculated: ${n}`) - const hex = this.#hexify(work1) + this.#hexify([ + const hex = Pow.#hexify(work1) + Pow.#hexify([ Pow.#pixels[i + 2], Pow.#pixels[i + 3], work0[2] ^ (Pow.#pixels[i] - 1), @@ -341,15 +391,8 @@ void main() { return } } - performance.mark('end') - frameTimes.push(performance.measure('draw', 'start', 'end').duration) - performance.clearMarks() - // Nothing found yet, try again - self.requestAnimationFrame(draw) } - - // Begin generation - self.requestAnimationFrame(draw) + draw() } }