Created
May 16, 2024 06:12
-
-
Save greggman/39cc8a3cd26e0d88983cef891970804e to your computer and use it in GitHub Desktop.
WebGPU test mip-selection with offset
This file contains 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; | |
} | |
canvas { | |
border: 1px solid #888; | |
width: 64px; | |
height: 64px; | |
image-rendering: pixelated; | |
margin: 3px; | |
} |
This file contains 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 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
/* | |
m.mmmmmmm | |
m.mmmmmmm | |
m.mmmmmmm | |
m.mmmmmmm | |
m.mmmmmmm | |
m.mmmmmmm | |
m.mmmmmmm | |
m.mmmmmmm | |
r.rr | |
r.rr | |
r.rr | |
r.rr | |
g. | |
g. | |
b | |
*/ | |
async function main() { | |
const adapter = await navigator.gpu?.requestAdapter(); | |
const device = await adapter?.requestDevice(); | |
const preferredFormat = navigator.gpu.getPreferredCanvasFormat(); | |
for (let dd = 1; dd <= 8; dd += dd) { | |
const canvas = document.createElement('canvas'); | |
canvas.width = 8; | |
canvas.height = 8; | |
document.body.appendChild(canvas); | |
const context = canvas.getContext('webgpu'); | |
context.configure({ | |
format: preferredFormat, | |
device, | |
}); | |
const options = { | |
ddx: dd, | |
ddy: dd, | |
}; | |
const module = device.createShaderModule({code: ` | |
struct InOut { | |
@builtin(position) pos: vec4f, | |
@location(0) uv: vec2f, | |
}; | |
@vertex fn vs(@builtin(vertex_index) vertex_index : u32) -> InOut { | |
let positions = array( | |
vec2f(-1, -1), vec2f(-1, 1), | |
vec2f( 1, -1), vec2f( 1, 1), | |
); | |
let pos = positions[vertex_index]; | |
return InOut( | |
vec4f(pos, 0, 1), | |
(pos * 0.5 + 0.5) * vec2f(${options.ddx}, ${options.ddy}), | |
); | |
} | |
@group(0) @binding(0) var T : texture_2d<f32>; | |
@group(0) @binding(1) var S : sampler; | |
@fragment fn fs(v: InOut) -> @location(0) vec4f { | |
return textureSample(T, S, v.uv, vec2i(1, 0)); | |
} | |
`, | |
}); | |
const pipeline = device.createRenderPipeline({ | |
layout: 'auto', | |
vertex: { module }, | |
fragment: { | |
module, | |
targets: [ | |
{format: preferredFormat}, | |
], | |
}, | |
primitive: { | |
topology: 'triangle-strip', | |
} | |
}); | |
const sampler = device.createSampler({ | |
addressModeU: 'repeat', | |
addressModeV: 'repeat', | |
minFilter: 'linear', | |
magFilter: 'linear', | |
mipmapFilter: 'linear', | |
minFilter: 'nearest', | |
magFilter: 'nearest', | |
mipmapFilter: 'nearest', | |
}); | |
const texture = device.createTexture({ | |
size: [8, 8], | |
mipLevelCount: 4, | |
format: 'rgba8unorm', | |
usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING, | |
}); | |
const aToS = a => a.map(v => v.toString()).join(','); | |
const r = [1, 0, 0, 1]; | |
const y = [1, 1, 0, 1]; | |
const g = [0, 1, 0, 1]; | |
const c = [0, 1, 1, 1]; | |
const b = [0, 0, 1, 1]; | |
const m = [1, 0, 1, 1]; | |
const w = [1, 1, 1, 1]; | |
const _ = [0.5, 0.5, 0.5, 1]; | |
function makeTextureData({width, height}, mipLevel) { | |
const p = 2 ** mipLevel; | |
const w = Math.max(1, Math.floor(width / p)); | |
const h = Math.max(1, Math.floor(height / p)); | |
return new Uint8Array(w * h * 4).map((_, i) => { | |
const p = i / 4 | 0; | |
const x = p % w; | |
const y = p / w | 0; | |
const ch = i % 4; | |
const o = mipLevel * 10; | |
return [o + x, o + y, 0, 100 + mipLevel][ch]; | |
}); | |
} | |
const lerp = (a, b, t) => a + (b - a) * t; | |
function makeSolidTextureData({width, height}, mipLevel, color) { | |
const p = 2 ** mipLevel; | |
const w = Math.max(1, Math.floor(width / p)); | |
const h = Math.max(1, Math.floor(height / p)); | |
const d = new Uint8Array(w * h * 4).map((_, i) => { | |
const p = i / 4 | 0; | |
const x = p % w; | |
const y = p / w | 0; | |
const ch = i % 4; | |
return x === 1 ? lerp(128, 255, y / h) : color[ch] * 255; | |
}); | |
return d; | |
} | |
const colors = { r, y, g, c, b, m, w, _ }; | |
const valueToColor = Object.fromEntries(Object.entries(colors).map(([name, value]) => [aToS(value), name])); | |
[m, r, g, b].forEach((c, mipLevel) => { | |
const w = Math.max(1, texture.width >> mipLevel); | |
device.queue.writeTexture( | |
{ texture, mipLevel }, | |
makeSolidTextureData(texture, mipLevel, c), | |
{ bytesPerRow: w * 4 }, | |
[w, w], | |
); | |
}); | |
/* | |
+---+---+ | |
| | | | |
+---+---+ | |
| | | | |
+---+---+ | |
*/ | |
const bindGroup = device.createBindGroup({ | |
layout: pipeline.getBindGroupLayout(0), | |
entries: [ | |
{ binding: 0, resource: texture.createView() }, | |
{ binding: 1, resource: sampler }, | |
], | |
}); | |
const encoder = device.createCommandEncoder(); | |
const pass = encoder.beginRenderPass({ | |
colorAttachments: [ | |
{ | |
view: context.getCurrentTexture().createView(), | |
clearValue: [1, 1, 1, 1], | |
loadOp: 'clear', | |
storeOp: 'store', | |
}, | |
], | |
}); | |
pass.setPipeline(pipeline); | |
pass.setBindGroup(0, bindGroup); | |
pass.draw(4); | |
pass.end(); | |
device.queue.submit([encoder.finish()]); | |
} | |
} | |
function fail(msg) { | |
const elem = document.querySelector('#fail'); | |
const contentElem = elem.querySelector('.content'); | |
elem.style.display = ''; | |
contentElem.textContent = msg; | |
} | |
main(); |
This file contains 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 test mip-selection with offset","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