-
Notifications
You must be signed in to change notification settings - Fork 313
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
329 additions
and
6 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||
<title>webgpu-samples: alphaToCoverage</title> | ||
<style> | ||
:root { | ||
color-scheme: light dark; | ||
} | ||
html, body { | ||
margin: 0; /* remove default margin */ | ||
height: 100%; /* make body fill the browser window */ | ||
display: flex; | ||
place-content: center center; | ||
} | ||
canvas { | ||
width: min(100vw, 100vh); | ||
height: min(100vw, 100vh); | ||
display: block; | ||
} | ||
</style> | ||
<script src="../../third_party/gif.js/gif.js"></script> | ||
<script defer type="module" src="main.js"></script> | ||
<script defer type="module" src="../../js/iframe-helper.js"></script> | ||
</head> | ||
<body> | ||
<canvas></canvas> | ||
</body> | ||
</html> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
import { GUI } from 'dat.gui'; | ||
|
||
import showMultisampleTextureWGSL from './showMultisampleTexture.wgsl'; | ||
import renderWithAlphaToCoverageWGSL from './renderWithAlphaToCoverage.wgsl'; | ||
import { quitIfWebGPUNotAvailable } from '../util'; | ||
|
||
const canvas = document.querySelector('canvas') as HTMLCanvasElement; | ||
const adapter = await navigator.gpu?.requestAdapter(); | ||
const device = await adapter?.requestDevice(); | ||
quitIfWebGPUNotAvailable(adapter, device); | ||
|
||
// | ||
// GUI controls | ||
// | ||
|
||
const kAlphaSteps = 64; | ||
|
||
const kInitConfig = { | ||
width: 8, | ||
height: 8, | ||
alpha: 4, | ||
pause: false, | ||
}; | ||
const config = { ...kInitConfig }; | ||
const updateConfig = () => { | ||
const data = new Float32Array([config.alpha / kAlphaSteps]); | ||
device.queue.writeBuffer(bufConfig, 0, data); | ||
}; | ||
|
||
const gui = new GUI(); | ||
{ | ||
const buttons = { | ||
initial() { | ||
Object.assign(config, kInitConfig); | ||
gui.updateDisplay(); | ||
}, | ||
}; | ||
|
||
const settings = gui.addFolder('Settings'); | ||
settings.open(); | ||
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); | ||
|
||
gui.add(buttons, 'initial').name('reset to initial'); | ||
} | ||
|
||
// | ||
// Canvas setup | ||
// | ||
|
||
function updateCanvasSize() { | ||
const devicePixelRatio = window.devicePixelRatio; | ||
canvas.width = canvas.clientWidth * devicePixelRatio; | ||
canvas.height = canvas.clientHeight * devicePixelRatio; | ||
} | ||
updateCanvasSize(); | ||
const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); | ||
|
||
const context = canvas.getContext('webgpu') as GPUCanvasContext; | ||
context.configure({ | ||
device, | ||
format: presentationFormat, | ||
alphaMode: 'premultiplied', | ||
}); | ||
|
||
// | ||
// Config buffer | ||
// | ||
|
||
const bufConfig = device.createBuffer({ | ||
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM, | ||
size: 128, | ||
}); | ||
|
||
// | ||
// Pipeline to render to a multisampled texture using alpha-to-coverage | ||
// | ||
|
||
const renderWithAlphaToCoverageModule = device.createShaderModule({ | ||
code: renderWithAlphaToCoverageWGSL, | ||
}); | ||
const renderWithAlphaToCoveragePipeline = device.createRenderPipeline({ | ||
label: 'renderWithAlphaToCoveragePipeline', | ||
layout: 'auto', | ||
vertex: { module: renderWithAlphaToCoverageModule }, | ||
fragment: { | ||
module: renderWithAlphaToCoverageModule, | ||
targets: [{ format: 'rgba16float' }], | ||
}, | ||
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 | ||
// | ||
|
||
const showMultisampleTextureModule = device.createShaderModule({ | ||
code: showMultisampleTextureWGSL, | ||
}); | ||
const showMultisampleTexturePipeline = device.createRenderPipeline({ | ||
label: 'showMultisampleTexturePipeline', | ||
layout: 'auto', | ||
vertex: { module: showMultisampleTextureModule }, | ||
fragment: { | ||
module: showMultisampleTextureModule, | ||
targets: [{ format: presentationFormat }], | ||
}, | ||
primitive: { topology: 'triangle-list' }, | ||
}); | ||
const showMultisampleTextureBGL = | ||
showMultisampleTexturePipeline.getBindGroupLayout(0); | ||
|
||
function render() { | ||
updateConfig(); | ||
|
||
const multisampleTexture = device.createTexture({ | ||
format: 'rgba16float', | ||
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING, | ||
size: [config.width, config.height], | ||
sampleCount: 4, | ||
}); | ||
const multisampleTextureView = multisampleTexture.createView(); | ||
|
||
const showMultisampleTextureBG = device.createBindGroup({ | ||
layout: showMultisampleTextureBGL, | ||
entries: [{ binding: 0, resource: multisampleTextureView }], | ||
}); | ||
|
||
const commandEncoder = device.createCommandEncoder(); | ||
// renderWithAlphaToCoverage pass | ||
{ | ||
const pass = commandEncoder.beginRenderPass({ | ||
label: 'renderWithAlphaToCoverage pass', | ||
colorAttachments: [ | ||
{ | ||
view: multisampleTextureView, | ||
clearValue: [0, 0, 0, 1], // will be overwritten | ||
loadOp: 'clear', | ||
storeOp: 'store', | ||
}, | ||
], | ||
}); | ||
pass.setPipeline(renderWithAlphaToCoveragePipeline); | ||
pass.setBindGroup(0, renderWithAlphaToCoverageBG); | ||
pass.draw(6); | ||
pass.end(); | ||
} | ||
// showMultisampleTexture pass | ||
{ | ||
const pass = commandEncoder.beginRenderPass({ | ||
label: 'showMulitsampleTexture pass', | ||
colorAttachments: [ | ||
{ | ||
view: context.getCurrentTexture().createView(), | ||
clearValue: [1, 0, 1, 1], // error color, will be overwritten | ||
loadOp: 'clear', | ||
storeOp: 'store', | ||
}, | ||
], | ||
}); | ||
pass.setPipeline(showMultisampleTexturePipeline); | ||
pass.setBindGroup(0, showMultisampleTextureBG); | ||
pass.draw(6); | ||
pass.end(); | ||
} | ||
device.queue.submit([commandEncoder.finish()]); | ||
|
||
multisampleTexture.destroy(); | ||
} | ||
|
||
function frame() { | ||
if (!config.pause) { | ||
config.alpha = ((performance.now() / 10000) % 1) * (kAlphaSteps + 4) - 2; | ||
gui.updateDisplay(); | ||
} | ||
updateCanvasSize(); | ||
render(); | ||
requestAnimationFrame(frame); | ||
} | ||
|
||
requestAnimationFrame(frame); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export default { | ||
name: 'Alpha-to-Coverage', | ||
description: | ||
'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.', | ||
filename: __DIRNAME__, | ||
sources: [ | ||
{ path: 'main.ts' }, | ||
{ path: './renderWithAlphaToCoverage.wgsl' }, | ||
{ path: './showMultisampleTexture.wgsl' }, | ||
], | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
struct Config { | ||
alpha: f32, | ||
}; | ||
@group(0) @binding(0) var<uniform> config: Config; | ||
|
||
struct Varying { | ||
@builtin(position) pos: vec4f, | ||
} | ||
|
||
@vertex | ||
fn vmain( | ||
@builtin(vertex_index) vertex_index: u32, | ||
) -> 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)); | ||
} | ||
|
||
@fragment | ||
fn fmain(vary: Varying) -> @location(0) vec4f { | ||
return vec4f(1, 1, 1, config.alpha); | ||
} | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
@group(0) @binding(0) var tex: texture_multisampled_2d<f32>; | ||
|
||
struct Varying { | ||
@builtin(position) pos: vec4f, | ||
@location(0) uv: vec2f, | ||
} | ||
|
||
const kMipLevels = 4; | ||
const baseMipSize: u32 = 16; | ||
|
||
const kSquare = array( | ||
vec2f(0, 0), vec2f(0, 1), vec2f(1, 0), | ||
vec2f(1, 0), vec2f(0, 1), vec2f(1, 1), | ||
); | ||
|
||
@vertex | ||
fn vmain( | ||
@builtin(vertex_index) vertex_index: u32, | ||
) -> Varying { | ||
|
||
let uv = kSquare[vertex_index]; | ||
let pos = vec4(uv * vec2(2, -2) + vec2(-1, 1), 0.0, 1.0); | ||
|
||
return Varying(pos, uv); | ||
} | ||
|
||
const kSampleCount = 4; | ||
const kSamplePositions: array<vec2f, kSampleCount> = array( | ||
vec2f(0.375, 0.125), | ||
vec2f(0.875, 0.375), | ||
vec2f(0.125, 0.625), | ||
vec2f(0.625, 0.875), | ||
); | ||
|
||
@fragment | ||
fn fmain(vary: Varying) -> @location(0) vec4f { | ||
let dim = textureDimensions(tex); | ||
let dimMax = max(dim.x, dim.y); | ||
|
||
let xy = vary.uv * f32(dimMax); | ||
let xyInt = vec2u(xy); | ||
let xyFrac = xy % vec2f(1, 1); | ||
|
||
if xyInt.x >= dim.x || xyInt.y >= dim.y { | ||
return vec4f(0, 0, 0, 1); | ||
} | ||
|
||
// 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 val = textureLoad(tex, xyInt, sampleIndex).rgb; | ||
return vec4f(val, 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); | ||
} |
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
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
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
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
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
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