WebGPU: Compute Shader Mipmap Generation from external texture (v0)
CSS
:root {
color-scheme: light dark;
}
canvas {
border: 1px solid gray;
margin: 5px;
max-width: 128px;
}
JavaScript
import RollingAverage from 'https://webgpufundamentals.org/webgpu/resources/js/rolling-average.js';
import TimingHelper from 'https://webgpufundamentals.org/webgpu/resources/js/timing-helper.js';
async function main() {
const adapter = await navigator.gpu.requestAdapter();
const hasTiming = adapter.features.has('timestamp-query');
const requiredFeatures = [
...(hasTiming ? ['timestamp-query', 'subgroups'] : ['subgroups']),
];
const device = await adapter.requestDevice({ requiredFeatures });
device.addEventListener('uncapturederror', e => console.error(e.error.message));
const tests = [
makeCanvas(4096, 4096),
makeCanvas(4095, 4093),
makeSmallCanvas(7),
];
for (const canvas of tests) {
await test(device, canvas);
}
}
const hsl = (h, s, l) => `hsl(${h * 360 | 0}, ${s * 100}%, ${l * 100 | 0}%)`;
function makeSmallCanvas(w) {
const canvas = new OffscreenCanvas(w, 1);
const ctx = canvas.getContext('2d');
for (let x = 0; x < w; ++x) {
ctx.fillStyle = hsl(x / w, 1, 0.5);
ctx.fillRect(x, 0, 1, 1);
}
return canvas;
}
function makeCanvas(w, h) {
const canvas = new OffscreenCanvas(w, h);
const ctx = canvas.getContext('2d');
const hue = (w + h) * 0.1434;
ctx.fillStyle = hsl(hue, 1, 0.75);
ctx.fillRect(0, 0, w, h);
ctx.lineWidth = 32;
ctx.stokeStyle = hsl(hue + 0.33, 1, 0.25);
ctx.strokeRect(0, 0, w, h);
ctx.font = `bold ${Math.min(w, h) * 0.9 | 0}px monospace`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = hsl(hue + 0.5, 1, 0.25);;
ctx.fillText('F', w / 2, h / 2);
return canvas;
}
async function test(device, canvas) {
const computePassTimingHelper = new TimingHelper(device);
const { width, height } = canvas;
const texture = device.createTexture({
format: 'rgba8unorm',
size: [width, height],
usage: GPUTextureUsage.STORAGE_BINDING |
GPUTextureUsage.TEXTURE_BINDING |
...