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 |
     ...