From 5beadb45a86be24022d751339bbeb77501feba58 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sun, 12 Jan 2025 11:48:05 -0800 Subject: [PATCH] Move private method above public. --- src/classes/gpu.ts | 132 ++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/src/classes/gpu.ts b/src/classes/gpu.ts index ed8a9e8..a493d12 100644 --- a/src/classes/gpu.ts +++ b/src/classes/gpu.ts @@ -92,6 +92,72 @@ export class NanoPowGpu { NanoPowGpu.init() } + static async #dispatch (seed: bigint, hash: string, threshold: number, passes: number): Promise { + if (this.#device == null) throw new Error(`WebGPU device failed to load.`) + // Set up uniform buffer object + // Note: u32 size is 4, but total alignment must be multiple of 16 + const uboView = new DataView(new ArrayBuffer(48)) + for (let i = 0; i < 64; i += 8) { + const uint32 = hash.slice(i, i + 8) + uboView.setUint32(i / 2, parseInt(uint32, 16)) + } + uboView.setBigUint64(32, seed, true) + uboView.setUint32(40, threshold, true) + this.#device.queue.writeBuffer(this.#uboBuffer, 0, uboView) + + // Reset `nonce` and `found` to 0u in WORK before each calculation + this.#device.queue.writeBuffer(this.#gpuBuffer, 0, new Uint32Array([0, 0, 0])) + + // Bind UBO read and GPU write buffers + const bindGroup = this.#device.createBindGroup({ + layout: this.#bindGroupLayout, + entries: [ + { + binding: 0, + resource: { + buffer: this.#uboBuffer + }, + }, + { + binding: 1, + resource: { + buffer: this.#gpuBuffer + }, + }, + ], + }) + + // Create command encoder to issue commands to GPU and initiate computation + const commandEncoder = this.#device.createCommandEncoder() + const passEncoder = commandEncoder.beginComputePass() + + // Issue commands and end compute pass structure + passEncoder.setPipeline(this.#pipeline) + passEncoder.setBindGroup(0, bindGroup) + passEncoder.dispatchWorkgroups(passes, passes) + passEncoder.end() + + // Copy 8-byte nonce and 4-byte found flag from GPU to CPU for reading + commandEncoder.copyBufferToBuffer(this.#gpuBuffer, 0, this.#cpuBuffer, 0, 12) + + // End computation by passing array of command buffers to command queue for execution + this.#device.queue.submit([commandEncoder.finish()]) + + // Read results back to Javascript and then unmap buffer after reading + let data = null + try { + await this.#cpuBuffer.mapAsync(GPUMapMode.READ) + await this.#device.queue.onSubmittedWorkDone() + data = new DataView(this.#cpuBuffer.getMappedRange().slice(0)) + this.#cpuBuffer.unmap() + } catch (err) { + console.warn(`Error getting data from GPU. ${err}`) + return this.#dispatch(seed, hash, threshold, passes) + } + if (data == null) throw new Error(`Failed to get data from buffer.`) + return data + } + /** * Finds a nonce that satisfies the Nano proof-of-work requirements. * @@ -169,70 +235,4 @@ export class NanoPowGpu { if (found && work !== nonce) throw new Error(`Nonce found but does not match work`) return found } - - static async #dispatch (seed: bigint, hash: string, threshold: number, passes: number): Promise { - if (this.#device == null) throw new Error(`WebGPU device failed to load.`) - // Set up uniform buffer object - // Note: u32 size is 4, but total alignment must be multiple of 16 - const uboView = new DataView(new ArrayBuffer(48)) - for (let i = 0; i < 64; i += 8) { - const uint32 = hash.slice(i, i + 8) - uboView.setUint32(i / 2, parseInt(uint32, 16)) - } - uboView.setBigUint64(32, seed, true) - uboView.setUint32(40, threshold, true) - this.#device.queue.writeBuffer(this.#uboBuffer, 0, uboView) - - // Reset `nonce` and `found` to 0u in WORK before each calculation - this.#device.queue.writeBuffer(this.#gpuBuffer, 0, new Uint32Array([0, 0, 0])) - - // Bind UBO read and GPU write buffers - const bindGroup = this.#device.createBindGroup({ - layout: this.#bindGroupLayout, - entries: [ - { - binding: 0, - resource: { - buffer: this.#uboBuffer - }, - }, - { - binding: 1, - resource: { - buffer: this.#gpuBuffer - }, - }, - ], - }) - - // Create command encoder to issue commands to GPU and initiate computation - const commandEncoder = this.#device.createCommandEncoder() - const passEncoder = commandEncoder.beginComputePass() - - // Issue commands and end compute pass structure - passEncoder.setPipeline(this.#pipeline) - passEncoder.setBindGroup(0, bindGroup) - passEncoder.dispatchWorkgroups(passes, passes) - passEncoder.end() - - // Copy 8-byte nonce and 4-byte found flag from GPU to CPU for reading - commandEncoder.copyBufferToBuffer(this.#gpuBuffer, 0, this.#cpuBuffer, 0, 12) - - // End computation by passing array of command buffers to command queue for execution - this.#device.queue.submit([commandEncoder.finish()]) - - // Read results back to Javascript and then unmap buffer after reading - let data = null - try { - await this.#cpuBuffer.mapAsync(GPUMapMode.READ) - await this.#device.queue.onSubmittedWorkDone() - data = new DataView(this.#cpuBuffer.getMappedRange().slice(0)) - this.#cpuBuffer.unmap() - } catch (err) { - console.warn(`Error getting data from GPU. ${err}`) - return this.#dispatch(seed, hash, threshold, passes) - } - if (data == null) throw new Error(`Failed to get data from buffer.`) - return data - } } -- 2.34.1