From 5769a88326cec14495070ee5ae5b79b958fff14e Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 13 Nov 2024 17:32:32 -0800 Subject: [PATCH] Replace alphaToCoverage with external alphaToCoverageEmulator --- sample/alphaToCoverage/index.html | 29 -- sample/alphaToCoverage/main.ts | 264 ------------------ sample/alphaToCoverage/meta.ts | 25 -- .../renderWithAlphaToCoverage.wgsl | 24 -- .../showMultisampleTexture.wgsl | 77 ----- sample/alphaToCoverageEmulator/meta.ts | 10 + src/main.ts | 2 +- src/samples.ts | 4 +- 8 files changed, 13 insertions(+), 422 deletions(-) delete mode 100644 sample/alphaToCoverage/index.html delete mode 100644 sample/alphaToCoverage/main.ts delete mode 100644 sample/alphaToCoverage/meta.ts delete mode 100644 sample/alphaToCoverage/renderWithAlphaToCoverage.wgsl delete mode 100644 sample/alphaToCoverage/showMultisampleTexture.wgsl create mode 100644 sample/alphaToCoverageEmulator/meta.ts diff --git a/sample/alphaToCoverage/index.html b/sample/alphaToCoverage/index.html deleted file mode 100644 index 6cc028d0..00000000 --- a/sample/alphaToCoverage/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - webgpu-samples: alphaToCoverage - - - - - - - - diff --git a/sample/alphaToCoverage/main.ts b/sample/alphaToCoverage/main.ts deleted file mode 100644 index cde25c0a..00000000 --- a/sample/alphaToCoverage/main.ts +++ /dev/null @@ -1,264 +0,0 @@ -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 kInitConfig = { - sizeLog2: 3, - showResolvedColor: true, - color1: 0x0000ff, - alpha1: 127, - color2: 0xff0000, - alpha2: 16, - pause: false, -}; -const config = { ...kInitConfig }; - -const gui = new GUI(); -gui.width = 300; -{ - const buttons = { - initial() { - Object.assign(config, kInitConfig); - gui.updateDisplay(); - }, - }; - - const settings = gui.addFolder('Settings'); - settings.open(); - settings.add(config, 'sizeLog2', 0, 8, 1).name('size = 2**'); - settings.add(config, 'showResolvedColor', true); - - 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'); -} - -// -// 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, -}); - -// -// GPU state controlled by the config gui -// - -const bufInstanceColors = device.createBuffer({ - usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX, - size: 8, -}); - -let multisampleTexture: GPUTexture, multisampleTextureView: GPUTextureView; -let resolveTexture: GPUTexture, resolveTextureView: GPUTextureView; -let lastSize = 0; -function resetMultisampleTexture() { - const size = 2 ** config.sizeLog2; - if (lastSize !== size) { - if (multisampleTexture) { - multisampleTexture.destroy(); - } - multisampleTexture = device.createTexture({ - format: 'rgba8unorm', - usage: - GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING, - size: [size, size], - sampleCount: 4, - }); - multisampleTextureView = multisampleTexture.createView(); - - if (resolveTexture) { - resolveTexture.destroy(); - } - resolveTexture = device.createTexture({ - format: 'rgba8unorm', - usage: - GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING, - size: [size, size], - }); - resolveTextureView = resolveTexture.createView(); - - lastSize = size; - } -} - -function applyConfig() { - // 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); - - resetMultisampleTexture(); -} - -// -// 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, - buffers: [ - { - stepMode: 'instance', - arrayStride: 4, - attributes: [{ shaderLocation: 0, format: 'unorm8x4', offset: 0 }], - }, - ], - }, - fragment: { - module: renderWithAlphaToCoverageModule, - targets: [{ format: 'rgba8unorm' }], - }, - multisample: { count: 4, alphaToCoverageEnabled: true }, - primitive: { topology: 'triangle-list' }, -}); - -// -// "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() { - applyConfig(); - - const showMultisampleTextureBG = device.createBindGroup({ - layout: showMultisampleTextureBGL, - entries: [ - { binding: 0, resource: multisampleTextureView }, - { binding: 1, resource: resolveTextureView }, - ], - }); - - const commandEncoder = device.createCommandEncoder(); - // clear resolveTextureView to gray if it won't be used - if (!config.showResolvedColor) { - const pass = commandEncoder.beginRenderPass({ - colorAttachments: [ - { - view: resolveTextureView, - clearValue: [0.3, 0.3, 0.3, 1], - loadOp: 'clear', - storeOp: 'store', - }, - ], - }); - pass.end(); - } - // renderWithAlphaToCoverage pass - { - const pass = commandEncoder.beginRenderPass({ - label: 'renderWithAlphaToCoverage pass', - colorAttachments: [ - { - view: multisampleTextureView, - resolveTarget: config.showResolvedColor - ? resolveTextureView - : undefined, - clearValue: [0, 0, 0, 1], // black background - loadOp: 'clear', - storeOp: 'store', - }, - ], - }); - pass.setPipeline(renderWithAlphaToCoveragePipeline); - pass.setVertexBuffer(0, bufInstanceColors); - pass.draw(6, 2); - pass.end(); - } - // showMultisampleTexture pass - { - const pass = commandEncoder.beginRenderPass({ - label: 'showMultisampleTexture 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()]); -} - -function frame() { - if (!config.pause) { - // 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(); - render(); - requestAnimationFrame(frame); -} - -requestAnimationFrame(frame); diff --git a/sample/alphaToCoverage/meta.ts b/sample/alphaToCoverage/meta.ts deleted file mode 100644 index 8cb35bc7..00000000 --- a/sample/alphaToCoverage/meta.ts +++ /dev/null @@ -1,25 +0,0 @@ -export default { - name: 'Alpha-to-Coverage', - description: ` -Alpha-to-coverage is an alternative to alpha testing and alpha blending. See: - - -This sample 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, each with the configured color and alpha value. Then, it visualizes the -contents of the resulting 4-sample texture: the circles show the 4 samples of -each texel; the background shows the "resolved" results (average of 4 samples). - -The algorithm that converts alpha to a coverage sample mask varies per device. -This results in different average "blending" proportions between the black -background, the first draw, and the second draw. -Device differences include different tile sizes (e.g. 1x1, 2x2, or 4x4), -"moving" samples (or not) around with in the tile as alpha increases, etc. -`, - filename: __DIRNAME__, - sources: [ - { path: 'main.ts' }, - { path: './renderWithAlphaToCoverage.wgsl' }, - { path: './showMultisampleTexture.wgsl' }, - ], -}; diff --git a/sample/alphaToCoverage/renderWithAlphaToCoverage.wgsl b/sample/alphaToCoverage/renderWithAlphaToCoverage.wgsl deleted file mode 100644 index 352f4043..00000000 --- a/sample/alphaToCoverage/renderWithAlphaToCoverage.wgsl +++ /dev/null @@ -1,24 +0,0 @@ -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), color); -} - -@fragment -fn fmain(vary: Varying) -> @location(0) vec4f { - return vary.color; -} - diff --git a/sample/alphaToCoverage/showMultisampleTexture.wgsl b/sample/alphaToCoverage/showMultisampleTexture.wgsl deleted file mode 100644 index 99b22a03..00000000 --- a/sample/alphaToCoverage/showMultisampleTexture.wgsl +++ /dev/null @@ -1,77 +0,0 @@ -@group(0) @binding(0) var tex: texture_multisampled_2d; -@group(0) @binding(1) var resolved: texture_2d; - -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); -} - -// Standard sample positions for 4xMSAA (assuming the device conforms to spec) -const kSampleCount = 4; -const kSamplePositions: array = array( - vec2f(0.375, 0.125), - vec2f(0.875, 0.375), - vec2f(0.125, 0.625), - 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); - let dimMax = max(dim.x, dim.y); - - let xy = vary.uv * f32(dimMax); - let xyInt = vec2u(xy); - let xyFrac = xy % vec2f(1, 1); - - // Show the visualization only if the resolution is large enough to see it - if (dpdx(xy.x) < kGridEdgeHalfWidth * 2) & (dpdy(xy.y) < kGridEdgeHalfWidth * 2) { - // Check if we're close to a sample; if so, visualize the sample value - for (var sampleIndex = 0; sampleIndex < kSampleCount; sampleIndex += 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); - } - } - - // Otherwise, show the multisample-resolved result as the background - let val = textureLoad(resolved, xyInt, /*level*/ 0).rgb; - return vec4f(val, 1); -} diff --git a/sample/alphaToCoverageEmulator/meta.ts b/sample/alphaToCoverageEmulator/meta.ts new file mode 100644 index 00000000..9972c519 --- /dev/null +++ b/sample/alphaToCoverageEmulator/meta.ts @@ -0,0 +1,10 @@ +export default { + name: 'Alpha-to-Coverage Inspector/Emulator', + description: '', + filename: __DIRNAME__, + external: { + url: 'https://kai.graphics/alpha-to-coverage-emulator/', + sourceURL: 'https://github.com/kainino0x/alpha-to-coverage-emulator', + }, + sources: [], +}; diff --git a/src/main.ts b/src/main.ts index 1043eb65..6791c882 100644 --- a/src/main.ts +++ b/src/main.ts @@ -199,7 +199,7 @@ function setSampleIFrame( sampleContainerElem.innerHTML = ''; if (filename) { const src = external ? external.url : `${filename}${search}`; - sampleContainerElem.appendChild(el('iframe', { src })); + sampleContainerElem.appendChild(el('iframe', { src, allowfullscreen: '' })); sampleContainerElem.style.height = sources.length > 0 ? '600px' : '100%'; if (external) { diff --git a/src/samples.ts b/src/samples.ts index 8f2c400d..ce2ed1c4 100644 --- a/src/samples.ts +++ b/src/samples.ts @@ -1,5 +1,5 @@ import aBuffer from '../sample/a-buffer/meta'; -import alphaToCoverage from '../sample/alphaToCoverage/meta'; +import alphaToCoverageEmulator from '../sample/alphaToCoverageEmulator/meta'; import animometer from '../sample/animometer/meta'; import bitonicSort from '../sample/bitonicSort/meta'; import bundleCulling from '../sample/bundleCulling/meta'; @@ -93,7 +93,6 @@ export const pageCategories: PageCategory[] = [ renderBundles, occlusionQuery, samplerParameters, - alphaToCoverage, }, }, @@ -161,6 +160,7 @@ export const pageCategories: PageCategory[] = [ clusteredShading, spookyball, marchingCubes, + alphaToCoverageEmulator, }, },