Skip to content

Commit

Permalink
alphaToCoverage: show resolve result; draw twice with different color…
Browse files Browse the repository at this point in the history
…s/alphas

This demonstrates how different alpha-to-coverage algorithms produce
different blending results.
  • Loading branch information
kainino0x committed Sep 20, 2024
1 parent 389827c commit dccfdb9
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 41 deletions.
91 changes: 62 additions & 29 deletions sample/alphaToCoverage/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,31 @@ quitIfWebGPUNotAvailable(adapter, device);
// GUI controls
//

const kAlphaSteps = 64;

const kInitConfig = {
width: 8,
height: 8,
alpha: 4,
color1: 0x0000ff,
alpha1: 127,
color2: 0xff0000,
alpha2: 16,
pause: false,
};
const config = { ...kInitConfig };
const updateConfig = () => {
const data = new Float32Array([config.alpha / kAlphaSteps]);
device.queue.writeBuffer(bufConfig, 0, data);
// Update the colors in the (instance-step-mode) vertex buffer
const data = new Uint8Array([
// instance 0 color
(config.color1 >> 16) & 0xff, // R
(config.color1 >> 8) & 0xff, // G
(config.color1 >> 0) & 0xff, // B
config.alpha1,
// instance 1 color
(config.color2 >> 16) & 0xff, // R
(config.color2 >> 8) & 0xff, // G
(config.color2 >> 0) & 0xff, // B
config.alpha2,
]);
device.queue.writeBuffer(bufInstanceColors, 0, data);
};

const gui = new GUI();
Expand All @@ -41,12 +54,16 @@ const gui = new GUI();
settings.add(config, 'width', 1, 16, 1);
settings.add(config, 'height', 1, 16, 1);

const alphaPanel = gui.addFolder('Alpha');
alphaPanel.open();
alphaPanel
.add(config, 'alpha', -2, kAlphaSteps + 2, 1)
.name(`alpha (of ${kAlphaSteps})`);
alphaPanel.add(config, 'pause', false);
const draw1Panel = gui.addFolder('Draw 1');
draw1Panel.open();
draw1Panel.addColor(config, 'color1').name('color');
draw1Panel.add(config, 'alpha1', 0, 255).name('alpha');

const draw2Panel = gui.addFolder('Draw 2');
draw2Panel.open();
draw2Panel.addColor(config, 'color2').name('color');
draw2Panel.add(config, 'alpha2', 0, 255).name('alpha');
draw2Panel.add(config, 'pause', false);

gui.add(buttons, 'initial').name('reset to initial');
}
Expand Down Expand Up @@ -74,8 +91,8 @@ context.configure({
// Config buffer
//

const bufConfig = device.createBuffer({
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM,
const bufInstanceColors = device.createBuffer({
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
size: 128,
});

Expand All @@ -89,21 +106,23 @@ const renderWithAlphaToCoverageModule = device.createShaderModule({
const renderWithAlphaToCoveragePipeline = device.createRenderPipeline({
label: 'renderWithAlphaToCoveragePipeline',
layout: 'auto',
vertex: { module: renderWithAlphaToCoverageModule },
vertex: {
module: renderWithAlphaToCoverageModule,
buffers: [
{
stepMode: 'instance',
arrayStride: 4,
attributes: [{ shaderLocation: 0, format: 'unorm8x4', offset: 0 }],
},
],
},
fragment: {
module: renderWithAlphaToCoverageModule,
targets: [{ format: 'rgba16float' }],
targets: [{ format: 'rgba8unorm' }],
},
multisample: { count: 4, alphaToCoverageEnabled: true },
primitive: { topology: 'triangle-list' },
});
const renderWithAlphaToCoverageBGL =
renderWithAlphaToCoveragePipeline.getBindGroupLayout(0);

const renderWithAlphaToCoverageBG = device.createBindGroup({
layout: renderWithAlphaToCoverageBGL,
entries: [{ binding: 0, resource: { buffer: bufConfig } }],
});

//
// "Debug" view of the actual texture contents
Expand All @@ -129,16 +148,26 @@ function render() {
updateConfig();

const multisampleTexture = device.createTexture({
format: 'rgba16float',
format: 'rgba8unorm',
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
size: [config.width, config.height],
sampleCount: 4,
});
const multisampleTextureView = multisampleTexture.createView();

const resolveTexture = device.createTexture({
format: 'rgba8unorm',
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
size: [config.width, config.height],
});
const resolveTextureView = resolveTexture.createView();

const showMultisampleTextureBG = device.createBindGroup({
layout: showMultisampleTextureBGL,
entries: [{ binding: 0, resource: multisampleTextureView }],
entries: [
{ binding: 0, resource: multisampleTextureView },
{ binding: 1, resource: resolveTextureView },
],
});

const commandEncoder = device.createCommandEncoder();
Expand All @@ -149,21 +178,22 @@ function render() {
colorAttachments: [
{
view: multisampleTextureView,
clearValue: [0, 0, 0, 1], // will be overwritten
resolveTarget: resolveTextureView,
clearValue: [0, 0, 0, 1], // black background
loadOp: 'clear',
storeOp: 'store',
},
],
});
pass.setPipeline(renderWithAlphaToCoveragePipeline);
pass.setBindGroup(0, renderWithAlphaToCoverageBG);
pass.draw(6);
pass.setVertexBuffer(0, bufInstanceColors);
pass.draw(6, 2);
pass.end();
}
// showMultisampleTexture pass
{
const pass = commandEncoder.beginRenderPass({
label: 'showMulitsampleTexture pass',
label: 'showMultisampleTexture pass',
colorAttachments: [
{
view: context.getCurrentTexture().createView(),
Expand All @@ -185,7 +215,10 @@ function render() {

function frame() {
if (!config.pause) {
config.alpha = ((performance.now() / 10000) % 1) * (kAlphaSteps + 4) - 2;
// scrub alpha2 over 15 seconds
let alpha = ((performance.now() / 15000) % 1) * (255 + 20) - 10;
alpha = Math.max(0, Math.min(alpha, 255));
config.alpha2 = alpha;
gui.updateDisplay();
}
updateCanvasSize();
Expand Down
14 changes: 13 additions & 1 deletion sample/alphaToCoverage/meta.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
export default {
name: 'Alpha-to-Coverage',
description:

Check failure on line 3 in sample/alphaToCoverage/meta.ts

View workflow job for this annotation

GitHub Actions / build

Delete `⏎···`
'Visualizes how alpha-to-coverage translates alpha values into sample coverage on your device. This varies per device; for example, not all devices guarantee that once a sample pops in, it will stay; some devices repeat at 2x2 pixels, others at 4x4; etc. The circles show the 4 samples of each pixel; the background checkerboard shows where the pixels are.',
`
Visualizes how alpha-to-coverage translates alpha values into sample
coverage on your device. It draws two full-screen quads into a 4-sample
texture, with the configured color and alpha values. Then, it visualizes the
contents of that 4-sample texture: the circles show the 4 samples of each
pixel; the background checkerboard shows where the pixels are.
The algorithm that converts alpha to a coverage sample mask varies per
device. This results in different proportions between the black background,
the first draw, and the second draw.
Examples: Some devices use 1x1 patterns, others 2x2, others 4x4. Not all devices guarantee that once a sample "pops in", it will stay there at higher alpha values. Some devices "move" samples around at certain alpha thresholds even without increasing the total sample count.
`,
filename: __DIRNAME__,
sources: [
{ path: 'main.ts' },
Expand Down
12 changes: 5 additions & 7 deletions sample/alphaToCoverage/renderWithAlphaToCoverage.wgsl
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
struct Config {
alpha: f32,
};
@group(0) @binding(0) var<uniform> config: Config;

struct Varying {
@builtin(position) pos: vec4f,
// Color from instance-step-mode vertex buffer
@location(0) color: vec4f,
}

@vertex
fn vmain(
@builtin(vertex_index) vertex_index: u32,
@location(0) color: vec4f,
) -> Varying {
var square = array(
vec2f(-1, -1), vec2f(-1, 1), vec2f( 1, -1),
vec2f( 1, -1), vec2f(-1, 1), vec2f( 1, 1),
);

return Varying(vec4(square[vertex_index], 0, 1));
return Varying(vec4(square[vertex_index], 0, 1), color);
}

@fragment
fn fmain(vary: Varying) -> @location(0) vec4f {
return vec4f(1, 1, 1, config.alpha);
return vary.color;
}

29 changes: 25 additions & 4 deletions sample/alphaToCoverage/showMultisampleTexture.wgsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@group(0) @binding(0) var tex: texture_multisampled_2d<f32>;
@group(0) @binding(1) var resolved: texture_2d<f32>;

struct Varying {
@builtin(position) pos: vec4f,
Expand All @@ -24,6 +25,7 @@ fn vmain(
return Varying(pos, uv);
}

// Standard sample positions for 4xMSAA (assuming the device conforms to spec)
const kSampleCount = 4;
const kSamplePositions: array<vec2f, kSampleCount> = array(
vec2f(0.375, 0.125),
Expand All @@ -32,6 +34,12 @@ const kSamplePositions: array<vec2f, kSampleCount> = array(
vec2f(0.625, 0.875),
);

// Compute dimensions for drawing a nice-looking visualization
const kSampleDistanceFromCloseEdge = 0.125; // from the standard sample positions
const kGridEdgeHalfWidth = 0.025;
const kSampleInnerRadius = kSampleDistanceFromCloseEdge - kGridEdgeHalfWidth;
const kSampleOuterRadius = kSampleDistanceFromCloseEdge + kGridEdgeHalfWidth;

@fragment
fn fmain(vary: Varying) -> @location(0) vec4f {
let dim = textureDimensions(tex);
Expand All @@ -45,14 +53,27 @@ fn fmain(vary: Varying) -> @location(0) vec4f {
return vec4f(0, 0, 0, 1);
}

// check if we're close to a sample, and return it
// Check if we're close to a sample, and return it
for (var sampleIndex = 0; sampleIndex < kSampleCount; sampleIndex += 1) {
if distance(xyFrac, kSamplePositions[sampleIndex]) < 0.1 {
let distanceFromSample = distance(xyFrac, kSamplePositions[sampleIndex]);
if distanceFromSample < kSampleInnerRadius {
// Draw a circle for the sample value
let val = textureLoad(tex, xyInt, sampleIndex).rgb;
return vec4f(val, 1);
} else if distanceFromSample < kSampleOuterRadius {
// Draw a ring around the circle
return vec4f(0, 0, 0, 1);
}

}

// If close to a grid edge, render the grid
let distanceToGridEdge = abs((xyFrac + 0.5) % 1 - 0.5);
if min(distanceToGridEdge.x, distanceToGridEdge.y) < kGridEdgeHalfWidth {
return vec4f(0, 0, 0, 1);
}

// if not close to a sample, render background checkerboard
return vec4f(f32(xyInt.x % 4 + 1) / 8, 0, f32(xyInt.y % 4 + 1) / 8, 1);
// Otherwise, show the multisample-resolved result as the background
let val = textureLoad(resolved, xyInt, /*level*/ 0).rgb;
return vec4f(val, 1);
}

0 comments on commit dccfdb9

Please sign in to comment.