playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
96 lines (93 loc) • 3.39 kB
JavaScript
import { array } from '../../../core/array-utils.js';
import { DebugHelper, Debug } from '../../../core/debug.js';
import { TRACEID_COMPUTEPIPELINE_ALLOC } from '../../../core/constants.js';
import { hash32Fnv1a } from '../../../core/hash.js';
import { WebgpuDebug } from './webgpu-debug.js';
import { WebgpuPipeline } from './webgpu-pipeline.js';
/**
* @import { WebgpuShader } from './webgpu-shader.js'
*/ let _pipelineId = 0;
class CacheEntry {
constructor(){
/**
* Compute pipeline
*
* @type {GPUComputePipeline|null}
* @private
*/ this.pipeline = null;
/**
* The full array of hashes used to lookup the pipeline, used in case of hash collision.
*
* @type {Uint32Array|null}
*/ this.hashes = null;
}
}
class WebgpuComputePipeline extends WebgpuPipeline {
get(shader, bindGroupFormat) {
// unique hash for the pipeline
const lookupHashes = this.lookupHashes;
lookupHashes[0] = shader.impl.computeKey;
lookupHashes[1] = bindGroupFormat.impl.key;
const hash = hash32Fnv1a(lookupHashes);
// Check cache
let cacheEntries = this.cache.get(hash);
if (cacheEntries) {
// Handle hash collisions by checking actual values
for(let i = 0; i < cacheEntries.length; i++){
const entry = cacheEntries[i];
if (array.equals(entry.hashes, lookupHashes)) {
return entry.pipeline;
}
}
}
// Cache miss - create new pipeline
const pipelineLayout = this.getPipelineLayout([
bindGroupFormat.impl
]);
const cacheEntry = new CacheEntry();
cacheEntry.hashes = new Uint32Array(lookupHashes);
cacheEntry.pipeline = this.create(shader, pipelineLayout);
// Add to cache
if (cacheEntries) {
cacheEntries.push(cacheEntry);
} else {
cacheEntries = [
cacheEntry
];
}
this.cache.set(hash, cacheEntries);
return cacheEntry.pipeline;
}
create(shader, pipelineLayout) {
const wgpu = this.device.wgpu;
/** @type {WebgpuShader} */ const webgpuShader = shader.impl;
/** @type {GPUComputePipelineDescriptor} */ const desc = {
compute: {
module: webgpuShader.getComputeShaderModule(),
entryPoint: webgpuShader.computeEntryPoint
},
// uniform / texture binding layout
layout: pipelineLayout
};
WebgpuDebug.validate(this.device);
_pipelineId++;
DebugHelper.setLabel(desc, `ComputePipelineDescr-${_pipelineId}`);
const pipeline = wgpu.createComputePipeline(desc);
DebugHelper.setLabel(pipeline, `ComputePipeline-${_pipelineId}`);
Debug.trace(TRACEID_COMPUTEPIPELINE_ALLOC, `Alloc: Id ${_pipelineId}`, desc);
WebgpuDebug.end(this.device, 'ComputePipeline creation', {
computePipeline: this,
desc: desc,
shader
});
return pipeline;
}
constructor(...args){
super(...args), this.lookupHashes = new Uint32Array(2), /**
* The cache of compute pipelines
*
* @type {Map<number, CacheEntry[]>}
*/ this.cache = new Map();
}
}
export { WebgpuComputePipeline };