static #gpuBuffer: GPUBuffer
static #cpuBuffer: GPUBuffer
static #bindGroupLayout: GPUBindGroupLayout
- static #pipeline: GPUComputePipeline
+ static #searchPipeline: GPUComputePipeline
+ static #validatePipeline: GPUComputePipeline
// Initialize WebGPU
static async init (): Promise<void> {
}
]
})
+ const shaderModule = this.#device.createShaderModule({
+ code: NanoPowGpuComputeShader
+ })
+ // Create pipeline to connect compute shader to binding layout
+ this.#searchPipeline = this.#device.createComputePipeline({
+ layout: this.#device.createPipelineLayout({
+ bindGroupLayouts: [this.#bindGroupLayout]
+ }),
+ compute: {
+ entryPoint: 'search',
+ module: shaderModule
+ }
+ })
// Create pipeline to connect compute shader to binding layout
- this.#pipeline = this.#device.createComputePipeline({
+ this.#validatePipeline = this.#device.createComputePipeline({
layout: this.#device.createPipelineLayout({
bindGroupLayouts: [this.#bindGroupLayout]
}),
compute: {
- entryPoint: 'main',
- module: this.#device.createShaderModule({
- code: NanoPowGpuComputeShader
- })
+ entryPoint: 'validate',
+ module: shaderModule
}
})
}
console.table(averages)
}
- static async #dispatch (seed: bigint, hash: string, threshold: number, passes: number): Promise<DataView> {
+ static async #dispatch (pipeline: GPUComputePipeline, seed: bigint, hash: string, threshold: number, passes: number): Promise<DataView> {
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 passEncoder = commandEncoder.beginComputePass()
// Issue commands and end compute pass structure
- passEncoder.setPipeline(this.#pipeline)
+ passEncoder.setPipeline(pipeline)
passEncoder.setBindGroup(0, bindGroup)
passEncoder.dispatchWorkgroups(passes, passes)
passEncoder.end()
this.#cpuBuffer.unmap()
} catch (err) {
console.warn(`Error getting data from GPU. ${err}`)
- return this.#dispatch(seed, hash, threshold, passes)
+ return this.#dispatch(pipeline, seed, hash, threshold, passes)
}
if (data == null) throw new Error(`Failed to get data from buffer.`)
return data
start = performance.now()
const random = Math.floor(Math.random() * 0xffffffff)
const seed = (BigInt(random) << 32n) | BigInt(random)
- const data = await this.#dispatch(seed, hash, threshold, effort * 0x100)
+ const data = await this.#dispatch(this.#searchPipeline, seed, hash, threshold, effort * 0x100)
nonce = data.getBigUint64(0, true)
this.#busy = !data.getUint32(8)
times.push(performance.now() - start)
if (this.#device == null) throw new Error(`WebGPU device failed to load.`)
const seed = BigInt(`0x${work}`)
- const data = await this.#dispatch(seed, hash, threshold, 1)
+ const data = await this.#dispatch(this.#validatePipeline, seed, hash, threshold, 1)
const nonce = data.getBigUint64(0, true).toString(16).padStart(16, '0')
const found = !!data.getUint32(8)
this.#busy = false
*/
const BLAKE2B_IV32_1: u32 = 0x6A09E667u;
+/**
+* Search compute function
+* Calls main with a workgroup size of 64 which has been tested as optimal
+*/
+@compute @workgroup_size(64)
+fn search(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ main(global_id);
+}
+
+/**
+* Validate compute function
+* Calls main with a workgroup size of 1 so that only one value is tested
+*/
+@compute @workgroup_size(1)
+fn validate(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ main(global_id);
+}
+
/**
* Main compute function
* A random u32 provided by the UBO is copied to form a pair. Each component of
* this 8-byte value is then XOR'd with a different dimensional index from
* the thread identifier.
*/
-@compute @workgroup_size(64)
-fn main(@builtin(global_invocation_id) id: vec3<u32>) {
+fn main(id: vec3<u32>) {
if (atomicLoad(&work.found) != 0u) { return; }
let threshold: u32 = ubo.threshold;