Last active
September 5, 2025 00:32
-
-
Save greggman/26a9f74135087973dbb3a1a825287ee9 to your computer and use it in GitHub Desktop.
WebGPU: Depth Texture Copy with mips
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:root { | |
color-scheme: light dark; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*bug-in-github-api-content-can-not-be-empty*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"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