Title: [248309] trunk/Websites/webkit.org
Revision
248309
Author
justin_...@apple.com
Date
2019-08-06 13:18:43 -0700 (Tue, 06 Aug 2019)

Log Message

[WebGPU] Fix up demos on and add compute demo to webkit.org/demos
https://bugs.webkit.org/show_bug.cgi?id=200454

Reviewed by Jon Lee.

Add the compute-blur demo.
Ensure that WebGPU demos will work on upcoming STP release.

* demos/webgpu/compute-blur.html: Added.
* demos/webgpu/css/style.css: Sync with internal demo repository stylesheet.
(body):
(canvas):
(body.error img):
(body.error input):
(#error p):
* demos/webgpu/hello-cube.html:
* demos/webgpu/hello-triangle.html:
* demos/webgpu/index.html:
* demos/webgpu/resources/compute-blur.png: Added.
* demos/webgpu/resources/hello-cube.png:
* demos/webgpu/resources/textured-cube.png: Added.
* demos/webgpu/scripts/compute-blur.js: Added.
(async.init):
(async.loadImage):
(setUpCompute):
(async.computeBlur):
(async.setUniforms):
(createShaderCode):
* demos/webgpu/scripts/hello-triangle.js:
(async.helloTriangle):
* demos/webgpu/textured-cube.html: Renmaed from Websites/webkit.org/demos/webgpu/hello-cube.html.

Modified Paths

Added Paths

Diff

Modified: trunk/Websites/webkit.org/ChangeLog (248308 => 248309)


--- trunk/Websites/webkit.org/ChangeLog	2019-08-06 19:54:43 UTC (rev 248308)
+++ trunk/Websites/webkit.org/ChangeLog	2019-08-06 20:18:43 UTC (rev 248309)
@@ -1,3 +1,37 @@
+2019-08-06  Justin Fan  <justin_...@apple.com>
+
+        [WebGPU] Fix up demos on and add compute demo to webkit.org/demos
+        https://bugs.webkit.org/show_bug.cgi?id=200454
+
+        Reviewed by Jon Lee.
+
+        Add the compute-blur demo.
+        Ensure that WebGPU demos will work on upcoming STP release. 
+
+        * demos/webgpu/compute-blur.html: Added.
+        * demos/webgpu/css/style.css: Sync with internal demo repository stylesheet.
+        (body):
+        (canvas):
+        (body.error img):
+        (body.error input):
+        (#error p):
+        * demos/webgpu/hello-cube.html:
+        * demos/webgpu/hello-triangle.html:
+        * demos/webgpu/index.html:
+        * demos/webgpu/resources/compute-blur.png: Added.
+        * demos/webgpu/resources/hello-cube.png:
+        * demos/webgpu/resources/textured-cube.png: Added.
+        * demos/webgpu/scripts/compute-blur.js: Added.
+        (async.init):
+        (async.loadImage):
+        (setUpCompute):
+        (async.computeBlur):
+        (async.setUniforms):
+        (createShaderCode):
+        * demos/webgpu/scripts/hello-triangle.js:
+        (async.helloTriangle):
+        * demos/webgpu/textured-cube.html: Renmaed from Websites/webkit.org/demos/webgpu/hello-cube.html.
+
 2019-07-03  Jon Davis  <j...@apple.com>
 
         Added a domain check for validation URLs in Apple Pay demo.

Added: trunk/Websites/webkit.org/demos/webgpu/compute-blur.html (0 => 248309)


--- trunk/Websites/webkit.org/demos/webgpu/compute-blur.html	                        (rev 0)
+++ trunk/Websites/webkit.org/demos/webgpu/compute-blur.html	2019-08-06 20:18:43 UTC (rev 248309)
@@ -0,0 +1,86 @@
+<!-- This demo loosely based off of https://github.com/d3dcoder/d3d12book/tree/master/Chapter%2013%20The%20Compute%20Shader/Blur -->
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=600">
+<meta http-equiv="Content-type" content="text/html; charset=utf-8">
+<title>WebGPU Compute</title>
+<link rel="stylesheet" href=""
+<style>
+body {
+    font-family: system-ui;
+    color: #f7f7ff;
+    background-color: rgb(13, 77, 153);
+    text-align: center;
+}
+canvas {
+    margin: 0 auto;
+}
+p {
+    margin: 0 auto 1em;
+    width: 600px;
+    position: relative;
+}
+
+.slider {
+    -webkit-appearance: none;
+    width: 50%;
+    height: 10px;
+    border-radius: 5px;
+    background: #d3d3d3;
+    outline: none;
+    opacity: 0.7;
+    -webkit-transition: .2s;
+    transition: opacity .2s;
+}
+
+.slider:hover {
+    opacity: 1;
+}
+
+.slider::-webkit-slider-thumb {
+    -webkit-appearance: none;
+    appearance: none;
+    width: 25px;
+    height: 25px;
+    border-radius: 50%;
+    background: rgb(66, 200, 255);
+    cursor: pointer;
+}
+
+.slider::-moz-range-thumb {
+    width: 25px;
+    height: 25px;
+    border-radius: 50%;
+    background: rgb(66, 200, 255);
+    cursor: pointer;
+}
+</style>
+</head>
+<body>
+<div id="contents">
+    <h1>Compute Demo</h1>
+    <p>
+        This demo uses the WebGPU compute pipeline to perform a Gaussian blur of the source image data. Use the slider to set the blur radius.
+    </p>
+    <p>
+        <div class="slidecontainer">
+            Blur radius: 
+            <input type="range" min="0" max="32" value="0" class="slider" id="radiusSlider">
+        </div>
+    </p>
+    <div id="demo">
+        <canvas></canvas>
+    </div>
+</div>
+<div id="error">
+    <h2>WebGPU not available</h2>
+    <p>
+        Make sure you are on a system with WebGPU enabled. In
+        Safari, first make sure the Developer Menu is visible (Preferences →
+        Advanced), then Develop → Experimental Features → WebGPU.
+    </p>
+</div>
+<script src=""
+</body>
+</html>

Modified: trunk/Websites/webkit.org/demos/webgpu/css/style.css (248308 => 248309)


--- trunk/Websites/webkit.org/demos/webgpu/css/style.css	2019-08-06 19:54:43 UTC (rev 248308)
+++ trunk/Websites/webkit.org/demos/webgpu/css/style.css	2019-08-06 20:18:43 UTC (rev 248309)
@@ -1,19 +1,10 @@
-/* This demo is based on webgl-compute-bitonicSort, found at https://github.com/9ballsyndrome/WebGL_Compute_shader. */
-
 body {
-    background-color: rgb(35%, 65%, 85%);
     margin: 0;
     padding: 0;
-    height: 100vh;
-    display: flex;
-    align-items: center;
-    justify-content: center;
 }
 
 canvas {
     display: block;
-    width: 100vw;
-    height: 100vh;
 }
 
 body.error canvas {
@@ -20,6 +11,14 @@
     display: none;
 }
 
+body.error img {
+    display: none;
+}
+
+body.error input {
+    display: none;
+}
+
 h1 {
     font-size: 1.5rem;
 }
@@ -74,4 +73,5 @@
 
 #error p {
     font-size: 30px;
-}
\ No newline at end of file
+}
+

Modified: trunk/Websites/webkit.org/demos/webgpu/hello-cube.html (248308 => 248309)


--- trunk/Websites/webkit.org/demos/webgpu/hello-cube.html	2019-08-06 19:54:43 UTC (rev 248308)
+++ trunk/Websites/webkit.org/demos/webgpu/hello-cube.html	2019-08-06 20:18:43 UTC (rev 248309)
@@ -2,123 +2,121 @@
 <html>
 <head>
 <meta name="viewport" content="width=1000">
-<title>WebGPU Cube demo</title>
+<title>WebGPU Cube</title>
 <script src=""
 <link rel="stylesheet" href=""
+<style>
+body {
+    font-family: system-ui;
+    color: #f7f7ff;
+    background-color: rgb(38, 38, 127);
+    text-align: center;
+}
+canvas {
+    margin: 0 auto;
+}
+</style>
 </head>
 <body>
-<canvas></canvas>
+<div id="contents">
+    <h1>Simple Cube</h1>
+    <p>
+        This demo uses a rotating set of GPUBuffers to upload rotation matrix data every frame.
+    </p>
+    <canvas width="1200" height="1200"></canvas>
+</div>
+<div id="error">
+    <h2>WebGPU not available</h2>
+    <p>
+        Make sure you are on a system with WebGPU enabled. In
+        Safari, first make sure the Developer Menu is visible (Preferences →
+        Advanced), then Develop → Experimental Features → WebGPU.
+    </p>
+</div>
 <script>
+if (!navigator.gpu)
+    document.body.className = 'error';
+
 const positionAttributeNum  = 0;
-const texCoordsAttributeNum = 1;
+const colorAttributeNum = 1;
 
 const transformBindingNum   = 0;
-const textureBindingNum     = 1;
-const samplerBindingNum     = 2;
 
-const vertexBufferIndex     = 0;
 const bindGroupIndex        = 0;
 
 const shader = `
-#include <metal_stdlib>
+struct FragmentData {
+    float4 position : SV_Position;
+    float4 color : attribute(${colorAttributeNum});
+}
 
-using namespace metal;
-
-struct Vertex
+vertex FragmentData vertex_main(
+    float4 position : attribute(${positionAttributeNum}), 
+    float4 color : attribute(${colorAttributeNum}), 
+    constant float4x4[] modelViewProjectionMatrix : register(b${transformBindingNum}))
 {
-    float4 position [[attribute(${positionAttributeNum})]];
-    float2 texCoords [[attribute(${texCoordsAttributeNum})]];
-};
-
-struct FragmentData
-{
-    float4 position [[position]];
-    float2 texCoords;
-};
-
-struct Uniform
-{
-    device float4x4* modelViewProjectionMatrix [[id(${transformBindingNum})]];
-};
-
-struct SampledTexture
-{
-    texture2d<float> faceTexture [[id(${textureBindingNum})]];
-    sampler faceSampler [[id(${samplerBindingNum})]];
-};
-
-vertex FragmentData vertex_main(Vertex vertexIn [[stage_in]],
-                            const device Uniform& uniforms [[buffer(${bindGroupIndex})]])
-{
-    FragmentData output;
-    output.position = uniforms.modelViewProjectionMatrix[0] * vertexIn.position;
-    output.texCoords = vertexIn.texCoords;
-
-    return output;
+    FragmentData out;
+    out.position = mul(modelViewProjectionMatrix[0], position);
+    out.color = color;
+    
+    return out;
 }
 
-fragment float4 fragment_main(FragmentData data [[stage_in]],
-                            const device SampledTexture& args [[buffer(${bindGroupIndex})]])
+fragment float4 fragment_main(float4 color : attribute(${colorAttributeNum})) : SV_Target 0
 {
-    float4 color = args.faceTexture.sample(args.faceSampler, data.texCoords);
-    if (color.a < 1)
-        discard_fragment();
-
     return color;
 }
-`
+`;
 
-let device, swapChain, verticesBuffer, bindGroupLayout, pipeline, renderPassDescriptor, queue, textureViewBinding, samplerBinding;
+let device, swapChain, verticesBuffer, bindGroupLayout, pipeline, renderPassDescriptor;
 let projectionMatrix = mat4.create();
 
-const texCoordsOffset = 4 * 4;
-const vertexSize = 4 * 6;
-function createVerticesArray() {
-    return new Float32Array([
-        // float4 position, float2 texCoords
-        1, -1, 1, 1, 1, 0, 
-        -1, -1, 1, 1, 0, 0, 
-        -1, -1, -1, 1, 0, 1, 
-        1, -1, -1, 1, 1, 1,
-        1, -1, 1, 1, 1, 0,
-        -1, -1, -1, 1, 0, 1,
+const colorOffset = 4 * 4;
+const vertexSize = 4 * 8;
+const verticesArray = new Float32Array([
+    // float4 position, float4 color
+    1, -1, 1, 1, 1, 0, 1, 1,
+    -1, -1, 1, 1, 0, 0, 1, 1,
+    -1, -1, -1, 1, 0, 0, 0, 1,
+    1, -1, -1, 1, 1, 0, 0, 1,
+    1, -1, 1, 1, 1, 0, 1, 1,
+    -1, -1, -1, 1, 0, 0, 0, 1,
 
-        1, 1, 1, 1, 0, 0,
-        1, -1, 1, 1, 0, 1,
-        1, -1, -1, 1, 1, 1,
-        1, 1, -1, 1, 1, 0,
-        1, 1, 1, 1, 0, 0,
-        1, -1, -1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, -1, 1, 1, 1, 0, 1, 1,
+    1, -1, -1, 1, 1, 0, 0, 1,
+    1, 1, -1, 1, 1, 1, 0, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, -1, -1, 1, 1, 0, 0, 1,
 
-        -1, 1, 1, 1, 0, 1,
-        1, 1, 1, 1, 1, 1,
-        1, 1, -1, 1, 1, 0,
-        -1, 1, -1, 1, 0, 0,
-        -1, 1, 1, 1, 0, 1,
-        1, 1, -1, 1, 1, 0,
+    -1, 1, 1, 1, 0, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, -1, 1, 1, 1, 0, 1,
+    -1, 1, -1, 1, 0, 1, 0, 1,
+    -1, 1, 1, 1, 0, 1, 1, 1,
+    1, 1, -1, 1, 1, 1, 0, 1,
 
-        -1, -1, 1, 1, 1, 1,
-        -1, 1, 1, 1, 1, 0,
-        -1, 1, -1, 1, 0, 0,
-        -1, -1, -1, 1, 0, 1,
-        -1, -1, 1, 1, 1, 1,
-        -1, 1, -1, 1, 0, 0,
+    -1, -1, 1, 1, 0, 0, 1, 1,
+    -1, 1, 1, 1, 0, 1, 1, 1,
+    -1, 1, -1, 1, 0, 1, 0, 1,
+    -1, -1, -1, 1, 0, 0, 0, 1,
+    -1, -1, 1, 1, 0, 0, 1, 1,
+    -1, 1, -1, 1, 0, 1, 0, 1,
 
-        1, 1, 1, 1, 1, 0,
-        -1, 1, 1, 1, 0, 0,
-        -1, -1, 1, 1, 0, 1,
-        -1, -1, 1, 1, 0, 1,
-        1, -1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 0,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    -1, 1, 1, 1, 0, 1, 1, 1,
+    -1, -1, 1, 1, 0, 0, 1, 1,
+    -1, -1, 1, 1, 0, 0, 1, 1,
+    1, -1, 1, 1, 1, 0, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
 
-        1, -1, -1, 1, 0, 1,
-        -1, -1, -1, 1, 1, 1,
-        -1, 1, -1, 1, 1, 0,
-        1, 1, -1, 1, 0, 0,
-        1, -1, -1, 1, 0, 1,
-        -1, 1, -1, 1, 1, 0,
-    ]);
-}
+    1, -1, -1, 1, 1, 0, 0, 1,
+    -1, -1, -1, 1, 0, 0, 0, 1,
+    -1, 1, -1, 1, 0, 1, 0, 1,
+    1, 1, -1, 1, 1, 1, 0, 1,
+    1, -1, -1, 1, 1, 0, 0, 1,
+    -1, 1, -1, 1, 0, 1, 0, 1,
+]);
 
 async function init() {
     const adapter = await navigator.gpu.requestAdapter();
@@ -135,110 +133,43 @@
     const context = canvas.getContext('gpu');
 
     const swapChainDescriptor = { 
-        device: device,
+        device: device, 
         format: "bgra8unorm"
     };
     swapChain = context.configureSwapChain(swapChainDescriptor);
 
-    // WebKit WebGPU accepts only MSL for now.
-    const shaderModuleDescriptor = { code: shader };
+    const shaderModuleDescriptor = { code: shader, isWHLSL: true };
     const shaderModule = device.createShaderModule(shaderModuleDescriptor);
 
-    const verticesArray = createVerticesArray();
     const verticesBufferDescriptor = { 
         size: verticesArray.byteLength, 
         usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST
     };
-    verticesBuffer = device.createBuffer(verticesBufferDescriptor);
-    verticesBuffer.setSubData(0, verticesArray.buffer);
+    let verticesArrayBuffer;
+    [verticesBuffer, verticesArrayBuffer] = device.createBufferMapped(verticesBufferDescriptor);
 
-    // Input state. Model will change soon to adopt one of https://github.com/kainino0x/gpuweb/pull/2/'s ideas.
+    const verticesWriteArray = new Float32Array(verticesArrayBuffer);
+    verticesWriteArray.set(verticesArray);
+    verticesBuffer.unmap();
+
+    // Vertex Input
     const positionAttributeDescriptor = {
-        shaderLocation: positionAttributeNum,  // [[attribute(0)]].
-        inputSlot: vertexBufferIndex,       // Used as vertex buffer index in Metal.
+        shaderLocation: positionAttributeNum,  // [[attribute(0)]]
         offset: 0,
         format: "float4"
     };
-    const texCoordsAttributeDescriptor = {
-        shaderLocation: texCoordsAttributeNum,
-        inputSlot: vertexBufferIndex,
-        offset: texCoordsOffset,
-        format: "float2"
+    const colorAttributeDescriptor = {
+        shaderLocation: colorAttributeNum,
+        offset: colorOffset,
+        format: "float4"
     }
     const vertexBufferDescriptor = {
-        inputSlot: vertexBufferIndex,
+        attributeSet: [positionAttributeDescriptor, colorAttributeDescriptor],
         stride: vertexSize,
         stepMode: "vertex"
     };
-    const inputStateDescriptor = {
-        indexFormat: "uint32",
-        attributes: [positionAttributeDescriptor, texCoordsAttributeDescriptor], 
-        inputs: [vertexBufferDescriptor]
-    };
+    const vertexInputDescriptor = { vertexBuffers: [vertexBufferDescriptor] };
 
-    // Texture
-
-    // Load texture image
-    const image = new Image();
-    const imageLoadPromise = new Promise(resolve => { 
-        image._onload_ = () => resolve(); 
-        image.src = ""
-    });
-    await Promise.resolve(imageLoadPromise);
-
-    const textureSize = {
-        width: image.width,
-        height: image.height,
-        depth: 1
-    };
-
-    const textureDescriptor = {
-        size: textureSize,
-        arrayLayerCount: 1,
-        mipLevelCount: 1,
-        sampleCount: 1,
-        dimension: "2d",
-        format: "rgba8unorm",
-        usage: GPUTextureUsage.TRANSFER_DST | GPUTextureUsage.SAMPLED
-    };
-    const texture = device.createTexture(textureDescriptor);
-
-    // Texture data 
-    const canvas2d = document.createElement('canvas');
-    canvas2d.width = image.width;
-    canvas2d.height = image.height;
-    const context2d = canvas2d.getContext('2d');
-    context2d.drawImage(image, 0, 0);
-
-    const imageData = context2d.getImageData(0, 0, image.width, image.height);
-
-    const textureDataBufferDescriptor = {
-        size: imageData.data.length,
-        usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.TRANSFER_DST
-    };
-    const textureDataBuffer = device.createBuffer(textureDataBufferDescriptor);
-    textureDataBuffer.setSubData(0, imageData.data.buffer);
-
-    const dataCopyView = {
-        buffer: textureDataBuffer,
-        offset: 0,
-        rowPitch: image.width * 4,
-        imageHeight: 0
-    };
-    const textureCopyView = {
-        texture: texture,
-        mipLevel: 0,
-        arrayLayer: 0,
-        origin: { x: 0, y: 0, z: 0 }
-    };
-
-    const blitCommandEncoder = device.createCommandEncoder();
-    blitCommandEncoder.copyBufferToTexture(dataCopyView, textureCopyView, textureSize);
-
-    queue = device.getQueue();
-
-    queue.submit([blitCommandEncoder.finish()]);
-
     // Bind group binding layout
     const transformBufferBindGroupLayoutBinding = {
         binding: transformBindingNum, // id[[(0)]]
@@ -246,29 +177,7 @@
         type: "uniform-buffer"
     };
 
-    const textureBindGroupLayoutBinding = {
-        binding: textureBindingNum,
-        visibility: GPUShaderStageBit.FRAGMENT,
-        type: "sampled-texture"
-    };
-    textureViewBinding = {
-        binding: textureBindingNum,
-        resource: texture.createDefaultView()
-    };
-
-    const samplerBindGroupLayoutBinding = {
-        binding: samplerBindingNum,
-        visibility: GPUShaderStageBit.FRAGMENT,
-        type: "sampler"
-    };
-    samplerBinding = {
-        binding: samplerBindingNum,
-        resource: device.createSampler({})
-    };
-
-    const bindGroupLayoutDescriptor = { 
-        bindings: [transformBufferBindGroupLayoutBinding, textureBindGroupLayoutBinding, samplerBindGroupLayoutBinding] 
-    };
+    const bindGroupLayoutDescriptor = { bindings: [transformBufferBindGroupLayoutBinding] };
     bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
 
     // Pipeline
@@ -310,7 +219,7 @@
         primitiveTopology: "triangle-list",
         colorStates: [colorState],
         depthStencilState: depthStateDescriptor,
-        inputState: inputStateDescriptor
+        vertexInput: vertexInputDescriptor
     };
     pipeline = device.createRenderPipeline(pipelineDescriptor);
 
@@ -318,7 +227,7 @@
         // attachment is acquired in render loop.
         loadOp: "clear",
         storeOp: "store",
-        clearColor: { r: 0.5, g: 1.0, b: 1.0, a: 1.0 } // GPUColor
+        clearColor: { r: 0.15, g: 0.15, b: 0.5, a: 1.0 } // GPUColor
     };
 
     // Depth stencil texture
@@ -360,17 +269,6 @@
 
 /* Transform Buffers and Bindings */
 const transformSize = 4 * 16;
-function updateTransformArray(array) {
-    let viewMatrix = mat4.create();
-    mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -5));
-    let now = Date.now() / 1000;
-    mat4.rotate(viewMatrix, viewMatrix, 1, vec3.fromValues(Math.sin(now), Math.cos(now), 0));
-    let modelViewProjectionMatrix = mat4.create();
-    mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
-    for (let i = 0; i < 16; i++) {
-        array[i] = modelViewProjectionMatrix[i];
-    }
-}
 
 const transformBufferDescriptor = {
     size: transformSize,
@@ -377,7 +275,19 @@
     usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE
 };
 
-function createBindGroupDescriptor(transformBuffer, textureViewBinding, samplerBinding) {
+let mappedGroups = [];
+
+function render() {
+    if (mappedGroups.length === 0) {
+        const [buffer, arrayBuffer] = device.createBufferMapped(transformBufferDescriptor);
+        const group = device.createBindGroup(createBindGroupDescriptor(buffer));
+        let mappedGroup = { buffer: buffer, arrayBuffer: arrayBuffer, bindGroup: group };
+        drawCommands(mappedGroup);
+    } else
+        drawCommands(mappedGroups.shift());
+}
+
+function createBindGroupDescriptor(transformBuffer) {
     const transformBufferBinding = {
         buffer: transformBuffer,
         offset: 0,
@@ -389,25 +299,10 @@
     };
     return {
         layout: bindGroupLayout,
-        bindings: [transformBufferBindGroupBinding, textureViewBinding, samplerBinding]
+        bindings: [transformBufferBindGroupBinding]
     };
 }
 
-let mappedGroups = [];
-
-function render() {
-    if (mappedGroups.length == 0) {
-        const buffer = device.createBuffer(transformBufferDescriptor);
-        buffer.mapWriteAsync().then(arrayBuffer => {
-            const group = device.createBindGroup(createBindGroupDescriptor(buffer, textureViewBinding, samplerBinding));
-
-            let mappedGroup = { buffer: buffer, arrayBuffer: arrayBuffer, bindGroup: group };
-            drawCommands(mappedGroup);
-        });
-    } else
-        drawCommands(mappedGroups.shift());
-}
-
 function drawCommands(mappedGroup) {
     updateTransformArray(new Float32Array(mappedGroup.arrayBuffer));
     mappedGroup.buffer.unmap();
@@ -415,16 +310,19 @@
     const commandEncoder = device.createCommandEncoder();
     renderPassDescriptor.colorAttachments[0].attachment = swapChain.getCurrentTexture().createDefaultView();
     const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
-    // Encode drawing commands.
+
+    // Encode drawing commands
+
     passEncoder.setPipeline(pipeline);
     // Vertex attributes
-    passEncoder.setVertexBuffers(vertexBufferIndex, [verticesBuffer], [0]);
+    passEncoder.setVertexBuffers(0, [verticesBuffer], [0]);
     // Bind groups
     passEncoder.setBindGroup(bindGroupIndex, mappedGroup.bindGroup);
+    // 36 vertices, 1 instance, 0th vertex, 0th instance.
     passEncoder.draw(36, 1, 0, 0);
     passEncoder.endPass();
 
-    queue.submit([commandEncoder.finish()]);
+    device.getQueue().submit([commandEncoder.finish()]);
 
     // Ready the current buffer for update after GPU is done with it.
     mappedGroup.buffer.mapWriteAsync().then((arrayBuffer) => {
@@ -435,7 +333,17 @@
     requestAnimationFrame(render);
 }
 
-init();
+function updateTransformArray(array) {
+    let viewMatrix = mat4.create();
+    mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -5));
+    let now = Date.now() / 1000;
+    mat4.rotate(viewMatrix, viewMatrix, 1, vec3.fromValues(Math.sin(now), Math.cos(now), 0));
+    let modelViewProjectionMatrix = mat4.create();
+    mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
+    mat4.copy(array, modelViewProjectionMatrix);
+}
+
+window.addEventListener("load", init);
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>

Modified: trunk/Websites/webkit.org/demos/webgpu/hello-triangle.html (248308 => 248309)


--- trunk/Websites/webkit.org/demos/webgpu/hello-triangle.html	2019-08-06 19:54:43 UTC (rev 248308)
+++ trunk/Websites/webkit.org/demos/webgpu/hello-triangle.html	2019-08-06 20:18:43 UTC (rev 248309)
@@ -3,18 +3,31 @@
 <head>
 <meta name="viewport" content="width=600">
 <meta http-equiv="Content-type" content="text/html; charset=utf-8">
-<title>Web GPU Hello Triangle demo</title>
+<title>WebGPU Hello Triangle</title>
 <link rel="stylesheet" href=""
-<link rel="stylesheet" href=""
+<style>
+body {
+    font-family: system-ui;
+    color: #f7f7ff;
+    background-color: rgb(38, 38, 127);
+    text-align: center;
+}
+canvas {
+    margin: 0 auto;
+}
+</style>
 </head>
 <body>
-<canvas></canvas>
+<div id="contents">
+    <h1>Simple Triangle</h1>
+    <canvas></canvas>
+</div>
 <div id="error">
-    <h2>Web GPU not available</h2>
+    <h2>WebGPU not available</h2>
     <p>
-        Make sure you are on a system with Web GPU enabled. In
+        Make sure you are on a system with WebGPU enabled. In
         Safari, first make sure the Developer Menu is visible (Preferences →
-        Advanced), then Develop → Experimental Features → Enable Web GPU.
+        Advanced), then Develop → Experimental Features → WebGPU.
     </p>
 </div>
 <script src=""

Modified: trunk/Websites/webkit.org/demos/webgpu/index.html (248308 => 248309)


--- trunk/Websites/webkit.org/demos/webgpu/index.html	2019-08-06 19:54:43 UTC (rev 248308)
+++ trunk/Websites/webkit.org/demos/webgpu/index.html	2019-08-06 20:18:43 UTC (rev 248309)
@@ -3,7 +3,7 @@
 <head>
 <meta name="viewport" content="width=device-width">
 <meta http-equiv="Content-type" content="text/html; charset=utf-8">
-<title>WebMetal demos</title>
+<title>WebGPU demos</title>
 <link rel="stylesheet" href=""
 <link rel="stylesheet" href=""
 <style>
@@ -162,27 +162,26 @@
 <body>
     <header>
         <img src=""
-        <h1>Web GPU<br>demos</h1>
+        <h1>WebGPU<br>demos</h1>
     </header>
     <section class="demos">
         <p class="intro">
             Here are a collection of simple <a
-            href="" GPU</a>
+            href=""
             examples. They should work in the latest <a
-            href="" builds, and 
-            soon in a <a
-            href=""
-             Technology Preview</a> release. The full specification is a work-in-progress on <a
+            href="" builds and 
+            <a href=""
+             Technology Preview</a> release. The full <a href="" is a work-in-progress on <a
             href=""
             ">GitHub</a>, and the implementation may differ from the current API.
         </p>
         <p class="howto">
-            Make sure you are on a system with Web GPU enabled. In Safari, first
+            Make sure you are on a system with WebGPU enabled. In Safari, first
             make sure the Developer Menu is visible (Preferences → Advanced),
-            then ensure Develop → Experimental Features → Web GPU is checked.
+            then ensure Develop → Experimental Features → WebGPU is checked.
         </p>
         <p class="warning">
-            Web GPU is an experimental technology, and you should not browse the entire
+            WebGPU is an experimental technology, and you should not browse the entire
             Web with it enabled for now. It doesn't do much validation of content, and
             thus may cause some issues with your computer.
         </p>
@@ -194,10 +193,22 @@
         </div>
         <div class="example">
             <a href=""
-            <img src=""
+            <img src = ""
+            Hello World Cube
+            </a>
+        </div>
+        <div class="example">
+            <a href=""
+            <img src=""
             Textured Spinning Cube
             </a>
         </div>
+        <div class="example">
+            <a href=""
+            <img src=""
+            Compute Shader Blur
+            </a>
+        </div>
     </section>
 </div>
 </body>

Added: trunk/Websites/webkit.org/demos/webgpu/resources/compute-blur.png


(Binary files differ)
Index: trunk/Websites/webkit.org/demos/webgpu/resources/compute-blur.png =================================================================== --- trunk/Websites/webkit.org/demos/webgpu/resources/compute-blur.png 2019-08-06 19:54:43 UTC (rev 248308) +++ trunk/Websites/webkit.org/demos/webgpu/resources/compute-blur.png 2019-08-06 20:18:43 UTC (rev 248309) Property changes on: trunk/Websites/webkit.org/demos/webgpu/resources/compute-blur.png ___________________________________________________________________

Added: svn:mime-type

+image/png \ No newline at end of property

Modified: trunk/Websites/webkit.org/demos/webgpu/resources/hello-cube.png


(Binary files differ)

Added: trunk/Websites/webkit.org/demos/webgpu/resources/textured-cube.png


(Binary files differ)
Index: trunk/Websites/webkit.org/demos/webgpu/resources/textured-cube.png =================================================================== --- trunk/Websites/webkit.org/demos/webgpu/resources/textured-cube.png 2019-08-06 19:54:43 UTC (rev 248308) +++ trunk/Websites/webkit.org/demos/webgpu/resources/textured-cube.png 2019-08-06 20:18:43 UTC (rev 248309) Property changes on: trunk/Websites/webkit.org/demos/webgpu/resources/textured-cube.png ___________________________________________________________________

Added: svn:mime-type

+image/png \ No newline at end of property

Added: trunk/Websites/webkit.org/demos/webgpu/scripts/compute-blur.js (0 => 248309)


--- trunk/Websites/webkit.org/demos/webgpu/scripts/compute-blur.js	                        (rev 0)
+++ trunk/Websites/webkit.org/demos/webgpu/scripts/compute-blur.js	2019-08-06 20:18:43 UTC (rev 248309)
@@ -0,0 +1,354 @@
+const threadsPerThreadgroup = 32;
+
+const sourceBufferBindingNum = 0;
+const outputBufferBindingNum = 1;
+const uniformsBufferBindingNum = 2;
+
+// Enough space to store 1 radius and 33 weights.
+const maxUniformsSize = (32 + 2) * Float32Array.BYTES_PER_ELEMENT;
+
+let image, context2d, device;
+
+const width = 600;
+
+async function init() {
+    if (!navigator.gpu) {
+        document.body.className = "error";
+        return;
+    }
+
+    const slider = document.querySelector("input");
+    const canvas = document.querySelector("canvas");
+    context2d = canvas.getContext("2d");
+
+    const adapter = await navigator.gpu.requestAdapter();
+    device = await adapter.requestDevice();
+    image = await loadImage(canvas);
+
+    setUpCompute();
+
+    let busy = false;
+    let inputQueue = [];
+    slider._oninput_ = async () => {
+        inputQueue.push(slider.value);
+        
+        if (busy)
+            return;
+
+        busy = true;
+        while (inputQueue.length != 0)
+            await computeBlur(inputQueue.shift());
+        busy = false;
+    };
+}
+
+async function loadImage(canvas) {
+    /* Image */
+    const image = new Image();
+    const imageLoadPromise = new Promise(resolve => { 
+        image._onload_ = () => resolve(); 
+        image.src = ""
+    });
+    await Promise.resolve(imageLoadPromise);
+
+    canvas.height = width;
+    canvas.width = width;
+
+    context2d.drawImage(image, 0, 0, width, width);
+
+    return image;
+}
+
+let originalData, imageSize;
+let originalBuffer, storageBuffer, resultsBuffer, uniformsBuffer;
+let horizontalBindGroup, verticalBindGroup, horizontalPipeline, verticalPipeline;
+
+function setUpCompute() {
+    originalData = context2d.getImageData(0, 0, image.width, image.height);
+    imageSize = originalData.data.length;
+
+    // Buffer creation
+    let originalArrayBuffer;
+    [originalBuffer, originalArrayBuffer] = device.createBufferMapped({ size: imageSize, usage: GPUBufferUsage.STORAGE });
+    const imageWriteArray = new Uint8ClampedArray(originalArrayBuffer);
+    imageWriteArray.set(originalData.data);
+    originalBuffer.unmap();
+
+    storageBuffer = device.createBuffer({ size: imageSize, usage: GPUBufferUsage.STORAGE });
+    resultsBuffer = device.createBuffer({ size: imageSize, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.MAP_READ });
+    uniformsBuffer = device.createBuffer({ size: maxUniformsSize, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE });
+
+    // Bind buffers to kernel   
+    const bindGroupLayout = device.createBindGroupLayout({
+        bindings: [{
+            binding: sourceBufferBindingNum,
+            visibility: GPUShaderStageBit.COMPUTE,
+            type: "storage-buffer"
+        }, {
+            binding: outputBufferBindingNum,
+            visibility: GPUShaderStageBit.COMPUTE,
+            type: "storage-buffer"
+        }, {
+            binding: uniformsBufferBindingNum,
+            visibility: GPUShaderStageBit.COMPUTE,
+            type: "uniform-buffer"
+        }]
+    });
+
+    horizontalBindGroup = device.createBindGroup({
+        layout: bindGroupLayout,
+        bindings: [{
+            binding: sourceBufferBindingNum,
+            resource: {
+                buffer: originalBuffer,
+                size: imageSize
+            }
+        }, {
+            binding: outputBufferBindingNum,
+            resource: {
+                buffer: storageBuffer,
+                size: imageSize
+            }
+        }, {
+            binding: uniformsBufferBindingNum,
+            resource: {
+                buffer: uniformsBuffer,
+                size: maxUniformsSize
+            }
+        }]
+    });
+
+    verticalBindGroup = device.createBindGroup({
+        layout: bindGroupLayout,
+        bindings: [{
+            binding: sourceBufferBindingNum,
+            resource: {
+                buffer: storageBuffer,
+                size: imageSize
+            }
+        }, {
+            binding: outputBufferBindingNum,
+            resource: {
+                buffer: resultsBuffer,
+                size: imageSize
+            }
+        }, {
+            binding: uniformsBufferBindingNum,
+            resource: {
+                buffer: uniformsBuffer,
+                size: maxUniformsSize
+            }
+        }]
+    });
+
+    // Set up pipelines
+    const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
+
+    const shaderModule = device.createShaderModule({ code: createShaderCode(image), isWHLSL: true });
+
+    horizontalPipeline = device.createComputePipeline({ 
+        layout: pipelineLayout, 
+        computeStage: {
+            module: shaderModule,
+            entryPoint: "horizontal"
+        }
+    });
+
+    verticalPipeline = device.createComputePipeline({
+        layout: pipelineLayout,
+        computeStage: {
+            module: shaderModule,
+            entryPoint: "vertical"
+        }
+    });
+}
+
+async function computeBlur(radius) {
+    if (radius == 0) {
+        context2d.drawImage(image, 0, 0, width, width);
+        return;
+    }
+    const setUniformsPromise = setUniforms(radius);
+    const uniformsMappingPromise = uniformsBuffer.mapWriteAsync();
+
+    const [uniforms, uniformsArrayBuffer] = await Promise.all([setUniformsPromise, uniformsMappingPromise]);
+
+    const uniformsWriteArray = new Float32Array(uniformsArrayBuffer);
+    uniformsWriteArray.set(uniforms);
+    uniformsBuffer.unmap();
+
+    // Run horizontal pass first
+    const commandEncoder = device.createCommandEncoder();
+    const passEncoder = commandEncoder.beginComputePass();
+    passEncoder.setBindGroup(0, horizontalBindGroup);
+    passEncoder.setPipeline(horizontalPipeline);
+    const numXGroups = Math.ceil(image.width / threadsPerThreadgroup);
+    passEncoder.dispatch(numXGroups, image.height, 1);
+    passEncoder.endPass();
+
+    // Run vertical pass
+    const verticalPassEncoder = commandEncoder.beginComputePass();
+    verticalPassEncoder.setBindGroup(0, verticalBindGroup);
+    verticalPassEncoder.setPipeline(verticalPipeline);
+    const numYGroups = Math.ceil(image.height / threadsPerThreadgroup);
+    verticalPassEncoder.dispatch(image.width, numYGroups, 1);
+    verticalPassEncoder.endPass();
+
+    device.getQueue().submit([commandEncoder.finish()]);
+
+    // Draw resultsBuffer as imageData back into context2d
+    const resultArrayBuffer = await resultsBuffer.mapReadAsync();
+    const resultArray = new Uint8ClampedArray(resultArrayBuffer);
+    context2d.putImageData(new ImageData(resultArray, image.width, image.height), 0, 0);
+    resultsBuffer.unmap();
+}
+
+window.addEventListener("load", init);
+
+/* Helpers */
+
+let uniformsCache = new Map();
+
+async function setUniforms(radius)
+{
+    let uniforms = uniformsCache.get(radius);
+    if (uniforms != undefined)
+        return uniforms;
+
+    const sigma = radius / 2.0;
+    const twoSigma2 = 2.0 * sigma * sigma;
+
+    uniforms = [radius];
+    let weightSum = 0;
+
+    for (let i = 0; i <= radius; ++i) {
+        const weight = Math.exp(-i * i / twoSigma2);
+        uniforms.push(weight);
+        weightSum += (i == 0) ? weight : weight * 2;
+    }
+
+    // Compensate for loss in brightness
+    const brightnessScale =  1 - (0.1 / 32.0) * radius;
+    weightSum *= brightnessScale;
+    for (let i = 1; i < uniforms.length; ++i)
+        uniforms[i] /= weightSum;
+        
+    uniformsCache.set(radius, uniforms);
+
+    return uniforms;
+}
+
+const byteMask = (1 << 8) - 1;
+
+function createShaderCode(image) {
+    return `
+uint getR(uint rgba)
+{
+    return rgba & ${byteMask};
+}
+
+uint getG(uint rgba)
+{
+    return (rgba >> 8) & ${byteMask};
+}
+
+uint getB(uint rgba)
+{
+    return (rgba >> 16) & ${byteMask};
+}
+
+uint getA(uint rgba)
+{
+    return (rgba >> 24) & ${byteMask};
+}
+
+uint makeRGBA(uint r, uint g, uint b, uint a)
+{
+    return r + (g << 8) + (b << 16) + (a << 24);
+}
+
+void accumulateChannels(thread uint[] channels, uint startColor, float weight)
+{
+    channels[0] += uint(float(getR(startColor)) * weight);
+    channels[1] += uint(float(getG(startColor)) * weight);
+    channels[2] += uint(float(getB(startColor)) * weight);
+    channels[3] += uint(float(getA(startColor)) * weight);
+
+    // Compensate for brightness-adjusted weights.
+    if (channels[0] > 255)
+        channels[0] = 255;
+
+    if (channels[1] > 255)
+        channels[1] = 255;
+
+    if (channels[2] > 255)
+        channels[2] = 255;
+
+    if (channels[3] > 255)
+        channels[3] = 255;
+}
+
+uint horizontallyOffsetIndex(uint index, int offset, int rowStart, int rowEnd)
+{
+    int offsetIndex = int(index) + offset;
+
+    if (offsetIndex < rowStart || offsetIndex >= rowEnd)
+        return index;
+    
+    return uint(offsetIndex);
+}
+
+uint verticallyOffsetIndex(uint index, int offset, uint length)
+{
+    int realOffset = offset * ${image.width};
+    int offsetIndex = int(index) + realOffset;
+
+    if (offsetIndex < 0 || offsetIndex >= int(length))
+        return index;
+    
+    return uint(offsetIndex);
+}
+
+[numthreads(${threadsPerThreadgroup}, 1, 1)]
+compute void horizontal(constant uint[] source : register(u${sourceBufferBindingNum}),
+                        device uint[] output : register(u${outputBufferBindingNum}),
+                        constant float[] uniforms : register(b${uniformsBufferBindingNum}),
+                        float3 dispatchThreadID : SV_DispatchThreadID)
+{
+    int radius = int(uniforms[0]);
+    int rowStart = ${image.width} * int(dispatchThreadID.y);
+    int rowEnd = ${image.width} * (1 + int(dispatchThreadID.y));
+    uint globalIndex = uint(rowStart) + uint(dispatchThreadID.x);
+
+    uint[4] channels;
+
+    for (int i = -radius; i <= radius; ++i) {
+        uint startColor = source[horizontallyOffsetIndex(globalIndex, i, rowStart, rowEnd)];
+        float weight = uniforms[uint(abs(i) + 1)];
+        accumulateChannels(@channels, startColor, weight);
+    }
+
+    output[globalIndex] = makeRGBA(channels[0], channels[1], channels[2], channels[3]);
+}
+
+[numthreads(1, ${threadsPerThreadgroup}, 1)]
+compute void vertical(constant uint[] source : register(u${sourceBufferBindingNum}),
+                        device uint[] output : register(u${outputBufferBindingNum}),
+                        constant float[] uniforms : register(b${uniformsBufferBindingNum}),
+                        float3 dispatchThreadID : SV_DispatchThreadID)
+{
+    int radius = int(uniforms[0]);
+    uint globalIndex = uint(dispatchThreadID.x) * ${image.height} + uint(dispatchThreadID.y);
+
+    uint[4] channels;
+
+    for (int i = -radius; i <= radius; ++i) {
+        uint startColor = source[verticallyOffsetIndex(globalIndex, i, source.length)];
+        float weight = uniforms[uint(abs(i) + 1)];
+        accumulateChannels(@channels, startColor, weight);
+    }
+
+    output[globalIndex] = makeRGBA(channels[0], channels[1], channels[2], channels[3]);
+}
+`;
+}
\ No newline at end of file

Modified: trunk/Websites/webkit.org/demos/webgpu/scripts/hello-triangle.js (248308 => 248309)


--- trunk/Websites/webkit.org/demos/webgpu/scripts/hello-triangle.js	2019-08-06 19:54:43 UTC (rev 248308)
+++ trunk/Websites/webkit.org/demos/webgpu/scripts/hello-triangle.js	2019-08-06 20:18:43 UTC (rev 248309)
@@ -12,33 +12,29 @@
     /* GPUShaderModule */
     const positionLocation = 0;
     const colorLocation = 1;
-    
-    const shaderSource = `
-    #include <metal_stdlib>
-  
-    using namespace metal;
-  
-    struct Vertex {
-        float4 position [[attribute(${positionLocation})]];
-        float4 color [[attribute(${colorLocation})]];
-    };
-  
+
+    const whlslSource = `
     struct FragmentData {
-        float4 position [[position]];
-        float4 color;
-    };
-  
-    vertex FragmentData vertexMain(const Vertex in [[stage_in]]) 
+        float4 position : SV_Position;
+        float4 color : attribute(${colorLocation});
+    }
+
+    vertex FragmentData vertexMain(float4 position : attribute(${positionLocation}), float4 color : attribute(${colorLocation}))
     {
-        return FragmentData { in.position, in.color };
+        FragmentData out;
+
+        out.position = position;
+        out.color = color;
+
+        return out;
     }
-  
-    fragment float4 fragmentMain(const FragmentData in [[stage_in]])
+
+    fragment float4 fragmentMain(float4 color : attribute(${colorLocation})) : SV_Target 0
     {
-        return in.color;
+        return color;
     }
     `;
-    const shaderModule = device.createShaderModule({ code: shaderSource });
+    const shaderModule = device.createShaderModule({ code: whlslSource, isWHLSL: true });
     
     /* GPUPipelineStageDescriptors */
     const vertexStageDescriptor = { module: shaderModule, entryPoint: "vertexMain" };
@@ -52,12 +48,12 @@
     const vertexDataSize = vertexStride * 3;
     
     /* GPUBufferDescriptor */
-    const vertexBufferDescriptor = { 
+    const vertexDataBufferDescriptor = { 
         size: vertexDataSize,
         usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.VERTEX
     };
     /* GPUBuffer */
-    const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
+    const vertexBuffer = device.createBuffer(vertexDataBufferDescriptor);
     
     /*** Write Data To GPU ***/
     
@@ -78,30 +74,26 @@
     /* GPUVertexAttributeDescriptors */
     const positionAttribute = {
         shaderLocation: positionLocation,
-        inputSlot: vertexBufferSlot,
         offset: 0,
         format: "float4"
     };
     const colorAttribute = {
         shaderLocation: colorLocation,
-        inputSlot: vertexBufferSlot,
         offset: colorOffset,
         format: "float4"
     };
-    
+
+    /* GPUVertexBufferDescriptor */
+    const vertexBufferDescriptor = {
+        stride: vertexStride,
+        attributeSet: [positionAttribute, colorAttribute]
+    };
+
     /* GPUVertexInputDescriptor */
     const vertexInputDescriptor = {
-        inputSlot: vertexBufferSlot,
-        stride: vertexStride,
-        stepMode: "vertex"
+        vertexBuffers: [vertexBufferDescriptor]
     };
     
-    /* GPUInputStateDescriptor */
-    const inputStateDescriptor = {
-        attributes: [positionAttribute, colorAttribute],
-        inputs: [vertexInputDescriptor]
-    };
-    
     /*** Finish Pipeline State ***/
     
     /* GPUBlendDescriptors */
@@ -122,7 +114,7 @@
         fragmentStage: fragmentStageDescriptor,
         primitiveTopology: "triangle-list",
         colorStates: [colorStateDescriptor],
-        inputState: inputStateDescriptor
+        vertexInput: vertexInputDescriptor
     };
     /* GPURenderPipeline */
     const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
@@ -130,9 +122,8 @@
     /*** Swap Chain Setup ***/
     
     const canvas = document.querySelector("canvas");
-    let canvasSize = canvas.getBoundingClientRect();
-    canvas.width = canvasSize.width;
-    canvas.height = canvasSize.height;
+    canvas.width = 600;
+    canvas.height = 600;
 
     const gpuContext = canvas.getContext("gpu");
     
@@ -151,7 +142,7 @@
     const renderAttachment = swapChainTexture.createDefaultView();
     
     /* GPUColor */
-    const darkBlue = { r: 0, g: 0, b: 0.5, a: 1 };
+    const darkBlue = { r: 0.15, g: 0.15, b: 0.5, a: 1 };
     
     /* GPURenderPassColorATtachmentDescriptor */
     const colorAttachmentDescriptor = {

Copied: trunk/Websites/webkit.org/demos/webgpu/textured-cube.html (from rev 248308, trunk/Websites/webkit.org/demos/webgpu/hello-cube.html) (0 => 248309)


--- trunk/Websites/webkit.org/demos/webgpu/textured-cube.html	                        (rev 0)
+++ trunk/Websites/webkit.org/demos/webgpu/textured-cube.html	2019-08-06 20:18:43 UTC (rev 248309)
@@ -0,0 +1,441 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=1000">
+<title>WebGPU Cube</title>
+<script src=""
+<link rel="stylesheet" href=""
+<style>
+body {
+    font-family: system-ui;
+    color: #f7f7ff;
+    background-color: rgb(13, 77, 153);
+    text-align: center;
+}
+canvas {
+    margin: 0 auto;
+}
+p {
+    margin: 0 8px;
+}
+</style>
+</head>
+<body>
+<div id="contents">
+    <h1>Textured Cube</h1>
+    <p>
+        This demo uploads a PNG image as texture data and uses it on the faces of a cube.
+    </p>
+    <canvas width="1200" height="1200"></canvas>
+</div>
+<div id="error">
+    <h2>WebGPU not available</h2>
+    <p>
+        Make sure you are on a system with WebGPU enabled. In
+        Safari, first make sure the Developer Menu is visible (Preferences →
+        Advanced), then Develop → Experimental Features → WebGPU.
+    </p>
+</div>
+<script>
+if (!navigator.gpu)
+    document.body.className = 'error';
+
+const positionAttributeNum  = 0;
+const texCoordsAttributeNum = 1;
+
+const transformBindingNum   = 0;
+const textureBindingNum     = 1;
+const samplerBindingNum     = 2;
+
+const bindGroupIndex        = 0;
+
+const shader = `
+struct FragmentData {
+    float4 position : SV_Position;
+    float2 texCoords : attribute(${texCoordsAttributeNum});
+}
+
+vertex FragmentData vertex_main(
+    float4 position : attribute(${positionAttributeNum}), 
+    float2 texCoords : attribute(${texCoordsAttributeNum}), 
+    constant float4x4[] modelViewProjectionMatrix : register(b${transformBindingNum}))
+{
+    FragmentData out;
+    out.position = mul(modelViewProjectionMatrix[0], position);
+    out.texCoords = texCoords;
+    
+    return out;
+}
+
+fragment float4 fragment_main(
+    float2 texCoords : attribute(${texCoordsAttributeNum}),
+    Texture2D<float4> faceTexture : register(t${textureBindingNum}),
+    sampler faceSampler : register(s${samplerBindingNum})) : SV_Target 0
+{
+    return Sample(faceTexture, faceSampler, texCoords);
+}
+`;
+
+let device, swapChain, verticesBuffer, bindGroupLayout, pipeline, renderPassDescriptor, queue, textureViewBinding, samplerBinding;
+let projectionMatrix = mat4.create();
+
+const texCoordsOffset = 4 * 4;
+const vertexSize = 4 * 6;
+const verticesArray = new Float32Array([
+    // float4 position, float2 texCoords
+    1, -1, -1, 1, 0, 1,
+    -1, -1, -1, 1, 1, 1,
+    -1, 1, -1, 1, 1, 0,
+    1, 1, -1, 1, 0, 0,
+    1, -1, -1, 1, 0, 1,
+    -1, 1, -1, 1, 1, 0,
+
+    1, 1, 1, 1, 0, 0,
+    1, -1, 1, 1, 0, 1,
+    1, -1, -1, 1, 1, 1,
+    1, 1, -1, 1, 1, 0,
+    1, 1, 1, 1, 0, 0,
+    1, -1, -1, 1, 1, 1,
+
+    1, -1, 1, 1, 1, 0, 
+    -1, -1, 1, 1, 0, 0, 
+    -1, -1, -1, 1, 0, 1, 
+    1, -1, -1, 1, 1, 1,
+    1, -1, 1, 1, 1, 0,
+    -1, -1, -1, 1, 0, 1,
+
+    -1, 1, 1, 1, 0, 1,
+    1, 1, 1, 1, 1, 1,
+    1, 1, -1, 1, 1, 0,
+    -1, 1, -1, 1, 0, 0,
+    -1, 1, 1, 1, 0, 1,
+    1, 1, -1, 1, 1, 0,
+
+    -1, -1, 1, 1, 1, 1,
+    -1, 1, 1, 1, 1, 0,
+    -1, 1, -1, 1, 0, 0,
+    -1, -1, -1, 1, 0, 1,
+    -1, -1, 1, 1, 1, 1,
+    -1, 1, -1, 1, 0, 0,
+
+    1, 1, 1, 1, 1, 0,
+    -1, 1, 1, 1, 0, 0,
+    -1, -1, 1, 1, 0, 1,
+    -1, -1, 1, 1, 0, 1,
+    1, -1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 0,
+]);
+
+async function init() {
+    const adapter = await navigator.gpu.requestAdapter();
+    device = await adapter.requestDevice();
+
+    const canvas = document.querySelector('canvas');
+
+    const aspect = Math.abs(canvas.width / canvas.height);
+    mat4.perspective(projectionMatrix, (2 * Math.PI) / 5, aspect, 1, 100.0);
+
+    const context = canvas.getContext('gpu');
+
+    const swapChainDescriptor = { 
+        device: device, 
+        format: "bgra8unorm"
+    };
+    swapChain = context.configureSwapChain(swapChainDescriptor);
+
+    // WebKit WebGPU accepts only MSL for now.
+    const shaderModuleDescriptor = { code: shader, isWHLSL: true };
+    const shaderModule = device.createShaderModule(shaderModuleDescriptor);
+
+    const verticesBufferDescriptor = { 
+        size: verticesArray.byteLength, 
+        usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST
+    };
+    let verticesArrayBuffer;
+    [verticesBuffer, verticesArrayBuffer] = device.createBufferMapped(verticesBufferDescriptor);
+
+    const verticesWriteArray = new Float32Array(verticesArrayBuffer);
+    verticesWriteArray.set(verticesArray);
+    verticesBuffer.unmap();
+
+    // Vertex Input
+    
+    const positionAttributeDescriptor = {
+        shaderLocation: positionAttributeNum,  // [[attribute(0)]].
+        offset: 0,
+        format: "float4"
+    };
+    const texCoordsAttributeDescriptor = {
+        shaderLocation: texCoordsAttributeNum,
+        offset: texCoordsOffset,
+        format: "float2"
+    }
+    const vertexBufferDescriptor = {
+        attributeSet: [positionAttributeDescriptor, texCoordsAttributeDescriptor],
+        stride: vertexSize,
+        stepMode: "vertex"
+    };
+    const vertexInputDescriptor = { vertexBuffers: [vertexBufferDescriptor] };
+
+    // Texture
+
+    // Load texture image
+    const image = new Image();
+    const imageLoadPromise = new Promise(resolve => { 
+        image._onload_ = () => resolve(); 
+        image.src = ""
+    });
+    await Promise.resolve(imageLoadPromise);
+
+    const textureSize = {
+        width: image.width,
+        height: image.height,
+        depth: 1
+    };
+
+    const textureDescriptor = {
+        size: textureSize,
+        arrayLayerCount: 1,
+        mipLevelCount: 1,
+        sampleCount: 1,
+        dimension: "2d",
+        format: "rgba8unorm",
+        usage: GPUTextureUsage.TRANSFER_DST | GPUTextureUsage.SAMPLED
+    };
+    const texture = device.createTexture(textureDescriptor);
+
+    // Texture data 
+    const canvas2d = document.createElement('canvas');
+    canvas2d.width = image.width;
+    canvas2d.height = image.height;
+    const context2d = canvas2d.getContext('2d');
+    context2d.drawImage(image, 0, 0);
+
+    const imageData = context2d.getImageData(0, 0, image.width, image.height);
+
+    const textureDataBufferDescriptor = {
+        size: imageData.data.length,
+        usage: GPUBufferUsage.TRANSFER_SRC
+    };
+    const [textureDataBuffer, textureArrayBuffer] = device.createBufferMapped(textureDataBufferDescriptor);
+    
+    const textureWriteArray = new Uint8Array(textureArrayBuffer);
+    textureWriteArray.set(imageData.data);
+    textureDataBuffer.unmap();
+
+    const dataCopyView = {
+        buffer: textureDataBuffer,
+        offset: 0,
+        rowPitch: image.width * 4,
+        imageHeight: 0
+    };
+    const textureCopyView = {
+        texture: texture,
+        mipLevel: 0,
+        arrayLayer: 0,
+        origin: { x: 0, y: 0, z: 0 }
+    };
+
+    const blitCommandEncoder = device.createCommandEncoder();
+    blitCommandEncoder.copyBufferToTexture(dataCopyView, textureCopyView, textureSize);
+
+    queue = device.getQueue();
+
+    queue.submit([blitCommandEncoder.finish()]);
+
+    // Bind group binding layout
+    const transformBufferBindGroupLayoutBinding = {
+        binding: transformBindingNum, // id[[(0)]]
+        visibility: GPUShaderStageBit.VERTEX,
+        type: "uniform-buffer"
+    };
+
+    const textureBindGroupLayoutBinding = {
+        binding: textureBindingNum,
+        visibility: GPUShaderStageBit.FRAGMENT,
+        type: "sampled-texture"
+    };
+    textureViewBinding = {
+        binding: textureBindingNum,
+        resource: texture.createDefaultView()
+    };
+
+    const samplerBindGroupLayoutBinding = {
+        binding: samplerBindingNum,
+        visibility: GPUShaderStageBit.FRAGMENT,
+        type: "sampler"
+    };
+    samplerBinding = {
+        binding: samplerBindingNum,
+        resource: device.createSampler({})
+    };
+
+    const bindGroupLayoutDescriptor = { 
+        bindings: [transformBufferBindGroupLayoutBinding, textureBindGroupLayoutBinding, samplerBindGroupLayoutBinding] 
+    };
+    bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
+
+    // Pipeline
+    const depthStateDescriptor = {
+        depthWriteEnabled: true,
+        depthCompare: "less"
+    };
+
+    const pipelineLayoutDescriptor = { bindGroupLayouts: [bindGroupLayout] };
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+    const vertexStageDescriptor = {
+        module: shaderModule,
+        entryPoint: "vertex_main"
+    };
+    const fragmentStageDescriptor = {
+        module: shaderModule,
+        entryPoint: "fragment_main"
+    };
+    const colorState = {
+        format: "bgra8unorm",
+        alphaBlend: {
+            srcFactor: "src-alpha",
+            dstFactor: "one-minus-src-alpha",
+            operation: "add"
+        },
+        colorBlend: {
+            srcFactor: "src-alpha",
+            dstFactor: "one-minus-src-alpha",
+            operation: "add"
+        },
+        writeMask: GPUColorWriteBits.ALL
+    };
+    const pipelineDescriptor = {
+        layout: pipelineLayout,
+
+        vertexStage: vertexStageDescriptor,
+        fragmentStage: fragmentStageDescriptor,
+
+        primitiveTopology: "triangle-list",
+        colorStates: [colorState],
+        depthStencilState: depthStateDescriptor,
+        vertexInput: vertexInputDescriptor
+    };
+    pipeline = device.createRenderPipeline(pipelineDescriptor);
+
+    let colorAttachment = {
+        // attachment is acquired in render loop.
+        loadOp: "clear",
+        storeOp: "store",
+        clearColor: { r: 0.05, g: .3, b: .6, a: 1.0 } // GPUColor
+    };
+
+    // Depth stencil texture
+
+    // GPUExtent3D
+    const depthSize = {
+        width: canvas.width,
+        height: canvas.height,
+        depth: 1
+    };
+
+    const depthTextureDescriptor = {
+        size: depthSize,
+        arrayLayerCount: 1,
+        mipLevelCount: 1,
+        sampleCount: 1,
+        dimension: "2d",
+        format: "depth32float-stencil8",
+        usage: GPUTextureUsage.OUTPUT_ATTACHMENT
+    };
+
+    const depthTexture = device.createTexture(depthTextureDescriptor);
+
+    // GPURenderPassDepthStencilAttachmentDescriptor
+    const depthAttachment = {
+        attachment: depthTexture.createDefaultView(),
+        depthLoadOp: "clear",
+        depthStoreOp: "store",
+        clearDepth: 1.0
+    };
+
+    renderPassDescriptor = { 
+        colorAttachments: [colorAttachment],
+        depthStencilAttachment: depthAttachment
+    };
+
+    render();
+}
+
+/* Transform Buffers and Bindings */
+const transformSize = 4 * 16;
+
+const transformBufferDescriptor = {
+    size: transformSize,
+    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE
+};
+
+let mappedGroups = [];
+
+function render() {
+    if (mappedGroups.length === 0) {
+        const [buffer, arrayBuffer] = device.createBufferMapped(transformBufferDescriptor);
+        const group = device.createBindGroup(createBindGroupDescriptor(buffer, textureViewBinding, samplerBinding));
+        let mappedGroup = { buffer: buffer, arrayBuffer: arrayBuffer, bindGroup: group };
+        drawCommands(mappedGroup);
+    } else
+        drawCommands(mappedGroups.shift());
+}
+
+function createBindGroupDescriptor(transformBuffer, textureViewBinding, samplerBinding) {
+    const transformBufferBinding = {
+        buffer: transformBuffer,
+        offset: 0,
+        size: transformSize
+    };
+    const transformBufferBindGroupBinding = {
+        binding: transformBindingNum,
+        resource: transformBufferBinding
+    };
+    return {
+        layout: bindGroupLayout,
+        bindings: [transformBufferBindGroupBinding, textureViewBinding, samplerBinding]
+    };
+}
+
+function drawCommands(mappedGroup) {
+    updateTransformArray(new Float32Array(mappedGroup.arrayBuffer));
+    mappedGroup.buffer.unmap();
+
+    const commandEncoder = device.createCommandEncoder();
+    renderPassDescriptor.colorAttachments[0].attachment = swapChain.getCurrentTexture().createDefaultView();
+    const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    // Encode drawing commands.
+    passEncoder.setPipeline(pipeline);
+    // Vertex attributes
+    passEncoder.setVertexBuffers(0, [verticesBuffer], [0]);
+    // Bind groups
+    passEncoder.setBindGroup(bindGroupIndex, mappedGroup.bindGroup);
+    passEncoder.draw(36, 1, 0, 0);
+    passEncoder.endPass();
+
+    queue.submit([commandEncoder.finish()]);
+
+    // Ready the current buffer for update after GPU is done with it.
+    mappedGroup.buffer.mapWriteAsync().then((arrayBuffer) => {
+        mappedGroup.arrayBuffer = arrayBuffer;
+        mappedGroups.push(mappedGroup);
+    });
+
+    requestAnimationFrame(render);
+}
+
+function updateTransformArray(array) {
+    let viewMatrix = mat4.create();
+    mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -5));
+    let now = Date.now() / 1000;
+    mat4.rotate(viewMatrix, viewMatrix, 1, vec3.fromValues(Math.sin(now), 1, 1));
+    let modelViewProjectionMatrix = mat4.create();
+    mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
+    mat4.copy(array, modelViewProjectionMatrix);
+}
+
+window.addEventListener("load", init);
+</script>
+</body>
+</html>
\ No newline at end of file
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to