{
const { device, context } = await gpu.init(1024, 1024)
const DISPATCH_SIZE = [Math.ceil(1024 / 16), Math.ceil(1024 / 16), 1]
const texInput = gpu.canvasTexture(device, sample.canvas, { usage: gpu.Usage.StorageTexture })
const texOutput = device.createTexture({
size: [1024, 1024],
format: 'rgba8unorm',
usage:
GPUTextureUsage.STORAGE_BINDING |
GPUTextureUsage.COPY_SRC |
GPUTextureUsage.TEXTURE_BINDING
})
const shaderModule = device.createShaderModule({
code: `
@group(0) @binding(0) var texInput: texture_2d<f32>;
@group(0) @binding(1) var texOutput: texture_storage_2d<rgba8unorm, write>;
const tileSize: vec2u = vec2u(16, 16);
const blurRadius: i32 = 10;
const count: f32 = pow(f32(blurRadius) * 2 + 1, 2);
@compute
@workgroup_size(16, 16, 1)
fn cs(@builtin(global_invocation_id) global_id: vec3u) {
let texSize: vec2u = textureDimensions(texInput).xy;
let tileNW: vec2u = (global_id.xy / tileSize) * tileSize;
let localPos: vec2u = global_id.xy % tileSize;
let texPos: vec2u = tileNW + localPos;
if (texPos.x < texSize.x && texPos.y < texSize.y) {
var colorSum: vec4f = vec4f(0.0, 0.0, 0.0, 0.0);
for (var y = -blurRadius; y <= blurRadius; y++) {
for (var x = -blurRadius; x <= blurRadius; x++) {
let samplePos = vec2i(texPos) + vec2i(x, y);
if (
samplePos.x >= 0 && samplePos.x < i32(texSize.x) &&
samplePos.y >= 0 && samplePos.y < i32(texSize.y)
) {
colorSum = colorSum + textureLoad(texInput, samplePos, 0);
}
}
}
let color: vec4f = colorSum / count;
textureStore(texOutput, texPos, color);
}
}
`
})
const bindGroupLayout = device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.COMPUTE,
texture: { format: 'rgba8unorm' }
},
{
binding: 1,
visibility: GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT,
storageTexture: { format: 'rgba8unorm' }
}
]
})
const pipelineLayout = device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout]
})
const pipeline = device.createComputePipeline({
layout: pipelineLayout,
compute: {
module: shaderModule,
entryPoint: 'cs'
}
})
const bindGroup0 = device.createBindGroup({
layout: bindGroupLayout,
entries: [
{ binding: 0, resource: texInput.createView() },
{ binding: 1, resource: texOutput.createView() }
]
})
const bindGroup1 = device.createBindGroup({
layout: bindGroupLayout,
entries: [
{ binding: 0, resource: texOutput.createView() },
{ binding: 1, resource: texInput.createView() },
]
})
function render() {
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginComputePass();
passEncoder.setPipeline(pipeline);
for (let i = 0; i < 3; i++) {
passEncoder.setBindGroup(0, bindGroup0);
passEncoder.dispatchWorkgroups(...DISPATCH_SIZE);
passEncoder.setBindGroup(0, bindGroup1);
passEncoder.dispatchWorkgroups(...DISPATCH_SIZE);
}
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
}
render()
const debug = new TextureRenderer(device, texOutput)
debug.render()
return debug.canvas
}