*/
// 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;
// Fragment shader
static #fsSource = `#version 300 es
+#pragma vscode_glsllint_stage: frag
precision highp float;
precision highp int;
// 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 = ''
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([
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<string>) => any, threshold: number): void {
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)
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) {
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),
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()
}
}