Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active September 5, 2025 00:32
Show Gist options
  • Save greggman/26a9f74135087973dbb3a1a825287ee9 to your computer and use it in GitHub Desktop.
Save greggman/26a9f74135087973dbb3a1a825287ee9 to your computer and use it in GitHub Desktop.
WebGPU: Depth Texture Copy with mips
:root {
color-scheme: light dark;
}
/*bug-in-github-api-content-can-not-be-empty*/
const code = `
struct VSOut {
@builtin(position) pos: vec4f,
@location(0) uv: vec2f,
};
@vertex fn vs(
@builtin(vertex_index) VertexIndex : u32
) -> VSOut {
let pos = array(
vec2f(-1, 3),
vec2f( 3, -1),
vec2f(-1, -1),
);
var vout: VSOut;
let xy = pos[VertexIndex];
vout.pos = vec4(xy, 0.0, 1.0);
vout.uv = xy * vec2f(0.5, -0.5) + vec2f(0.5);
return vout;
}
@group(0) @binding(0) var tex: texture_depth_2d;
@group(0) @binding(1) var smp: sampler;
@fragment fn fs(vin: VSOut) -> @location(0) vec4f {
return vec4f(textureSampleLevel(tex, smp, vin.uv, 0), 0, 0, 1);
}
`;
async function test(powerPreference) {
log('---[', powerPreference, ']----');
const adapter = await navigator.gpu?.requestAdapter({
powerPreference,
});
const device = await adapter?.requestDevice();
if (!device) {
alert('need webgpu');
return;
}
log(device.adapterInfo?.vendor, device.adapterInfo?.architecture)
device.addEventListener('uncapturederror', e => console.error(e.error.message));
const canvas = document.createElement("canvas");
document.body.append(canvas);
const context = canvas.getContext('webgpu');
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
alphaMode: 'opaque',
});
const size = 32
const depthSrcTex = device.createTexture({
format: 'depth16unorm',
size: [size, size],
mipLevelCount: 3,
usage: GPUTextureUsage.COPY_DST |
GPUTextureUsage.COPY_SRC |
GPUTextureUsage.TEXTURE_BINDING,
});
const data = new Uint16Array(32 * 32);
for (let y = 0; y < size; ++y) {
for (let x = 0; x < size; ++x) {
const dx = size / 2 - x;
const dy = size / 2 - y;
const d = Math.sqrt(dx * dx + dy * dy);
const off = y * size + x;
data[off] = d / (size * 0.71) * 0x10000;
}
}
device.queue.writeTexture({ texture: depthSrcTex }, data, { bytesPerRow: size * 2 }, [size, size]);
const depthDstTex = device.createTexture({
format: 'depth16unorm',
mipLevelCount: 3,
size: [size, size],
usage: GPUTextureUsage.COPY_DST |
GPUTextureUsage.TEXTURE_BINDING,
});
const bgl = device.createBindGroupLayout({
entries: [
{
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
sampler: {
type: "non-filtering",
},
},
{
binding: 0,
visibility: GPUShaderStage.FRAGMENT,
texture: {
sampleType: "depth",
viewDimension: "2d",
multisampled: false,
},
},
],
});
const layout = device.createPipelineLayout({
bindGroupLayouts: [bgl],
});
const module = device.createShaderModule({code});
const pipeline = device.createRenderPipeline({
layout,
vertex: {
module,
entryPoint: 'vs',
},
fragment: {
module,
entryPoint: 'fs',
targets: [{format: presentationFormat}],
}
});
const sampler = device.createSampler();
const encoder = device.createCommandEncoder();
for (let mipLevel = 0; mipLevel < depthSrcTex.mipLevelCount; ++mipLevel) {
encoder.copyTextureToTexture(
{ texture: depthSrcTex, mipLevel },
{ texture: depthDstTex, mipLevel },
[size >> mipLevel, size >> mipLevel],
);
}
const pass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
clearColor: [0, 0, 0, 0],
loadOp: 'clear',
storeOp: 'store',
}]
});
[depthSrcTex, depthDstTex].forEach((tex, i) => {
const bg = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: tex },
{ binding: 1, resource: sampler },
],
});
pass.setPipeline(pipeline);
pass.setBindGroup(0, bg);
pass.setViewport(i * 150, 0, 150, 150, 0, 1);
pass.draw(3);
});
pass.end();
device.queue.submit([encoder.finish()]);
}
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = args.join(' ');
document.body.appendChild(elem);
}
await test('high-performance');
await test('low-power');
{"name":"WebGPU: Depth Texture Copy with mips","settings":{},"filenames":["index.html","index.css","index.js"]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment