Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active February 15, 2024 00:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save greggman/c554126635f9dc6f8f846a336f86f359 to your computer and use it in GitHub Desktop.
Save greggman/c554126635f9dc6f8f846a336f86f359 to your computer and use it in GitHub Desktop.
WebGPU CTS fragment builtins multisampling sample_index triangles

WebGPU CTS fragment builtins multisampling sample_index triangles

view on jsgist

html, body {
color: white;
background-color: black;
}
canvas { margin: 1em; }
<canvas></canvas>
const samplePositionsToFragmentPositions = points => points.map(([x, y]) => [x, 16 - y]);
// These are sample positions based on a 16x16 grid with 0,0 at the top left.
// For example 8,8 would be a fragment coordinate of 0.5, 0.5
// Based on: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_standard_multisample_quality_levels
const kMultisamplingTables = new Map/*<number, readonly (readonly number[])[]>*/([
[1, samplePositionsToFragmentPositions([[8, 8]])],
[
2,
samplePositionsToFragmentPositions([
[4, 4],
[12, 12],
]),
],
[
4,
samplePositionsToFragmentPositions([
[6, 2],
[14, 6],
[2, 10],
[10, 14],
]),
],
[
8,
samplePositionsToFragmentPositions([
[9, 5],
[7, 11],
[13, 9],
[5, 3],
[3, 13],
[1, 7],
[11, 15],
[15, 1],
]),
],
[
16,
samplePositionsToFragmentPositions([
[9, 9],
[7, 5],
[5, 10],
[12, 7],
[3, 6],
[10, 13],
[13, 11],
[11, 3],
[6, 14],
[8, 1],
[4, 2],
[2, 12],
[0, 8],
[15, 4],
[14, 15],
[1, 0],
]),
],
]);
/**
* For a given sampleCount returns an array of 2d fragment offsets
* where each offset is between 0 and 1.
*/
function getMultisampleFragmentOffsets(sampleCount/*: number*/) {
return kMultisamplingTables.get(sampleCount);
}
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const pixelSize = 64;
const sampleCount = 4;
const fragmentOffsets = getMultisampleFragmentOffsets(sampleCount);
const centerX = 5;
const centerY = 5;
/*
[
[1, 1],
[2, 1],
[3, 1],
[-1, 1],
[-3, 1],
[1, 2],
[2, 2],
[3, 2],
[-2, 2],
[3, 3],
[-2, 3],
[-3, 3],
[1, -1],
[-1, 3],
[-1, 1],
[3, -1],
[-1, -1],
[-2, -1],
[-3, -1],
[2, -2],
[-1, -2],
[-2, -2],
[-3, -2],
[1, -3],
[2, -3],
[3, -3],
[-3, -3]
].forEach(([x, y]) => {
render(x, y);
});
*/
[1, 2, 3, -1, -2, -3].forEach(y => {
[1, 2, 3, -1, -2, -3].forEach(x => {
render(x, y);
});
});
function render(x, y) {
log(`${x}, ${y}`);
const canvas = document.createElement('canvas');
canvas.width = pixelSize * 10;
canvas.height = pixelSize * 10;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
const { width, height } = canvas;
for (let x = 0; x < width; x += pixelSize / 16) {
ctx.fillStyle = x % pixelSize === 0 ? '#888': '#444';
ctx.fillRect(x, 0, 1, height);
}
for (let y = 0; y < height; y += pixelSize / 16) {
ctx.fillStyle = y % pixelSize === 0 ? '#888': '#444';
ctx.fillRect(0, y, width, 1);
}
ctx.save();
ctx.translate(pixelSize * centerX, pixelSize * centerY);
ctx.fillStyle = '#FFF';
ctx.fillRect(-pixelSize * 2, -pixelSize * 2, pixelSize * 4, 1);
ctx.fillRect(-pixelSize * 2, pixelSize * 2, pixelSize * 4, 1);
ctx.fillRect(-pixelSize * 2, -pixelSize * 2, 1, pixelSize * 4);
ctx.fillRect( pixelSize * 2, -pixelSize * 2, 1, pixelSize * 4);
ctx.restore();
for (let y = 0; y < height; y += pixelSize) {
for (let x = 0; x < width; x += pixelSize) {
ctx.save();
ctx.translate(x, y);
ctx.scale(pixelSize / 16, pixelSize / 16);
// draw sample points
fragmentOffsets.forEach((offset, i) => {
ctx.beginPath();
ctx.arc(...offset, 0.5, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
ctx.save();
ctx.translate(...offset)
ctx.scale(0.3, 0.3);
ctx.fillStyle = '#ccc';
ctx.fillText(i, 2, -2);
ctx.restore();
});
// draw center
ctx.beginPath();
ctx.arc(8, 8, 0.5, 0, Math.PI * 2);
ctx.fillStyle = '#0FF';
ctx.fill();
ctx.restore();
}
}
const s = 1;
ctx.save();
ctx.translate(pixelSize * centerX, pixelSize * centerY);
ctx.fillStyle = '#FFF';
ctx.fillRect(-pixelSize * 2, -pixelSize * 2, pixelSize * 4, s);
ctx.fillRect(-pixelSize * 2, pixelSize * 2, pixelSize * 4, s);
ctx.fillRect(-pixelSize * 2, -pixelSize * 2, s, pixelSize * 4);
ctx.fillRect( pixelSize * 2, -pixelSize * 2, s, pixelSize * 4);
ctx.restore();
const clipSpacePoints = [
[ x + 0.2, -y, 0, 1],
[-x + 0.2, y, 0, 1],
[ x + 0.2, y, 0, 1],
];
ctx.save();
ctx.translate(pixelSize * centerX, pixelSize * centerY);
ctx.scale(pixelSize * 2, -pixelSize * 2);
ctx.lineWidth = 2 / (pixelSize * 4) * s;
ctx.fillStyle = 'rgba(0,255,0,0.02)';
ctx.strokeStyle = '#ff0';
for (let vNdx = 0; vNdx < clipSpacePoints.length; vNdx += 3) {
ctx.beginPath();
for (let i = 0; i < 3; ++i) {
ctx.lineTo(...clipSpacePoints[vNdx + i].slice(0, 2));
}
ctx.closePath();
ctx.fill();
ctx.stroke();
}
ctx.restore();
ctx.fillStyle = '#0ff';
ctx.font = '20px monospace';
ctx.fillText(`${x}, ${y}`, 20, 20);
}
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = args.join(' ');
document.body.appendChild(elem);
}
{"name":"WebGPU CTS fragment builtins multisampling sample_index triangles","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