|
| 1 | +import { |
| 2 | + PipelineLayout, |
| 3 | + PipelineLayoutProps, |
| 4 | + StorageBufferBindingLayout, |
| 5 | + StorageTextureBindingLayout |
| 6 | +} from '@luma.gl/core'; |
| 7 | +import {WebGPUDevice} from '../webgpu-device'; |
| 8 | + |
| 9 | +const VISIBILITY_ALL = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE; |
| 10 | + |
| 11 | +export class WebGPUPipelineLayout extends PipelineLayout { |
| 12 | + device: WebGPUDevice; |
| 13 | + handle: GPUPipelineLayout; |
| 14 | + |
| 15 | + constructor(device: WebGPUDevice, props: PipelineLayoutProps) { |
| 16 | + super(device, props); |
| 17 | + |
| 18 | + this.device = device; |
| 19 | + |
| 20 | + const bindGroupEntries = this.mapShaderLayoutToBindGroupEntries(); |
| 21 | + |
| 22 | + this.handle = this.device.handle.createPipelineLayout({ |
| 23 | + label: props?.id ?? 'unnamed-pipeline-layout', |
| 24 | + bindGroupLayouts: [ |
| 25 | + // TODO (kaapp): We can cache these to re-use them across |
| 26 | + // layers, particularly if using a separate group for injected |
| 27 | + // bindings (e.g. project/lighting) |
| 28 | + this.device.handle.createBindGroupLayout({ |
| 29 | + label: 'bind-group-layout', |
| 30 | + entries: bindGroupEntries |
| 31 | + }) |
| 32 | + ] |
| 33 | + }); |
| 34 | + } |
| 35 | + |
| 36 | + override destroy(): void { |
| 37 | + // WebGPUPipelineLayout has no destroy method. |
| 38 | + // @ts-expect-error |
| 39 | + this.handle = null; |
| 40 | + } |
| 41 | + |
| 42 | + protected mapShaderLayoutToBindGroupEntries(): GPUBindGroupLayoutEntry[] { |
| 43 | + // Set up the pipeline layout |
| 44 | + // TODO (kaapp): This only supports the first group, but so does the rest of the code |
| 45 | + const bindGroupEntries: GPUBindGroupLayoutEntry[] = []; |
| 46 | + |
| 47 | + for (let i = 0; i < this.props.shaderLayout.bindings.length; i++) { |
| 48 | + const binding = this.props.shaderLayout.bindings[i]; |
| 49 | + const bindingTypeInfo: Omit<GPUBindGroupLayoutEntry, 'binding' | 'visibility'> = {}; |
| 50 | + |
| 51 | + switch (binding.type) { |
| 52 | + case 'uniform': { |
| 53 | + bindingTypeInfo.buffer = { |
| 54 | + type: 'uniform', |
| 55 | + hasDynamicOffset: binding.hasDynamicOffset, |
| 56 | + minBindingSize: binding.minBindingSize |
| 57 | + }; |
| 58 | + break; |
| 59 | + } |
| 60 | + |
| 61 | + case 'read-only-storage': { |
| 62 | + bindingTypeInfo.buffer = { |
| 63 | + type: 'read-only-storage', |
| 64 | + hasDynamicOffset: binding.hasDynamicOffset, |
| 65 | + minBindingSize: binding.minBindingSize |
| 66 | + }; |
| 67 | + break; |
| 68 | + } |
| 69 | + |
| 70 | + case 'sampler': { |
| 71 | + bindingTypeInfo.sampler = { |
| 72 | + type: binding.samplerType |
| 73 | + }; |
| 74 | + break; |
| 75 | + } |
| 76 | + |
| 77 | + case 'storage': { |
| 78 | + if (isStorageTextureBindingLayout(binding)) { |
| 79 | + bindingTypeInfo.storageTexture = { |
| 80 | + // TODO (kaapp): Not all formats in the binding layout are supported |
| 81 | + // by WebGPU, but at least it will provide a clear error for now. |
| 82 | + format: binding.format as GPUTextureFormat, |
| 83 | + access: binding.access, |
| 84 | + viewDimension: binding.viewDimension |
| 85 | + }; |
| 86 | + } else { |
| 87 | + bindingTypeInfo.buffer = { |
| 88 | + type: 'storage', |
| 89 | + hasDynamicOffset: binding.hasDynamicOffset, |
| 90 | + minBindingSize: binding.minBindingSize |
| 91 | + }; |
| 92 | + } |
| 93 | + break; |
| 94 | + } |
| 95 | + |
| 96 | + case 'texture': { |
| 97 | + bindingTypeInfo.texture = { |
| 98 | + multisampled: binding.multisampled, |
| 99 | + sampleType: binding.sampleType, |
| 100 | + viewDimension: binding.viewDimension |
| 101 | + }; |
| 102 | + break; |
| 103 | + } |
| 104 | + |
| 105 | + default: { |
| 106 | + console.warn('unhandled binding type when creating pipeline descriptor'); |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + bindGroupEntries.push({ |
| 111 | + binding: binding.location, |
| 112 | + visibility: binding.visibility || VISIBILITY_ALL, |
| 113 | + ...bindingTypeInfo |
| 114 | + }); |
| 115 | + } |
| 116 | + |
| 117 | + return bindGroupEntries; |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +const isStorageTextureBindingLayout = ( |
| 122 | + maybe: StorageBufferBindingLayout | StorageTextureBindingLayout |
| 123 | +): maybe is StorageTextureBindingLayout => { |
| 124 | + return (maybe as StorageTextureBindingLayout).format !== undefined; |
| 125 | +}; |
0 commit comments