⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 248309 in webkit


Ignore:
Timestamp:
Aug 6, 2019, 1:18:43 PM (7 years ago)
Author:
Justin Fan
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.
Location:
trunk/Websites/webkit.org
Files:
4 added
7 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/Websites/webkit.org/ChangeLog

    r247122 r248309  
     12019-08-06  Justin Fan  <justin_fan@apple.com>
     2
     3        [WebGPU] Fix up demos on and add compute demo to webkit.org/demos
     4        https://bugs.webkit.org/show_bug.cgi?id=200454
     5
     6        Reviewed by Jon Lee.
     7
     8        Add the compute-blur demo.
     9        Ensure that WebGPU demos will work on upcoming STP release.
     10
     11        * demos/webgpu/compute-blur.html: Added.
     12        * demos/webgpu/css/style.css: Sync with internal demo repository stylesheet.
     13        (body):
     14        (canvas):
     15        (body.error img):
     16        (body.error input):
     17        (#error p):
     18        * demos/webgpu/hello-cube.html:
     19        * demos/webgpu/hello-triangle.html:
     20        * demos/webgpu/index.html:
     21        * demos/webgpu/resources/compute-blur.png: Added.
     22        * demos/webgpu/resources/hello-cube.png:
     23        * demos/webgpu/resources/textured-cube.png: Added.
     24        * demos/webgpu/scripts/compute-blur.js: Added.
     25        (async.init):
     26        (async.loadImage):
     27        (setUpCompute):
     28        (async.computeBlur):
     29        (async.setUniforms):
     30        (createShaderCode):
     31        * demos/webgpu/scripts/hello-triangle.js:
     32        (async.helloTriangle):
     33        * demos/webgpu/textured-cube.html: Renmaed from Websites/webkit.org/demos/webgpu/hello-cube.html.
     34
    1352019-07-03  Jon Davis  <jond@apple.com>
    236
  • trunk/Websites/webkit.org/demos/webgpu/css/style.css

    r244992 r248309  
    1 /* This demo is based on webgl-compute-bitonicSort, found at https://github.com/9ballsyndrome/WebGL_Compute_shader. */
    2 
    31body {
    4     background-color: rgb(35%, 65%, 85%);
    52    margin: 0;
    63    padding: 0;
    7     height: 100vh;
    8     display: flex;
    9     align-items: center;
    10     justify-content: center;
    114}
    125
    136canvas {
    147    display: block;
    15     width: 100vw;
    16     height: 100vh;
    178}
    189
    1910body.error canvas {
     11    display: none;
     12}
     13
     14body.error img {
     15    display: none;
     16}
     17
     18body.error input {
    2019    display: none;
    2120}
     
    7675    font-size: 30px;
    7776}
     77
  • trunk/Websites/webkit.org/demos/webgpu/hello-cube.html

    r244992 r248309  
    33<head>
    44<meta name="viewport" content="width=1000">
    5 <title>WebGPU Cube demo</title>
     5<title>WebGPU Cube</title>
    66<script src="scripts/gl-matrix-min.js"></script>
    77<link rel="stylesheet" href="css/style.css"/>
     8<style>
     9body {
     10    font-family: system-ui;
     11    color: #f7f7ff;
     12    background-color: rgb(38, 38, 127);
     13    text-align: center;
     14}
     15canvas {
     16    margin: 0 auto;
     17}
     18</style>
    819</head>
    920<body>
    10 <canvas></canvas>
     21<div id="contents">
     22    <h1>Simple Cube</h1>
     23    <p>
     24        This demo uses a rotating set of GPUBuffers to upload rotation matrix data every frame.
     25    </p>
     26    <canvas width="1200" height="1200"></canvas>
     27</div>
     28<div id="error">
     29    <h2>WebGPU not available</h2>
     30    <p>
     31        Make sure you are on a system with WebGPU enabled. In
     32        Safari, first make sure the Developer Menu is visible (Preferences →
     33        Advanced), then Develop → Experimental Features → WebGPU.
     34    </p>
     35</div>
    1136<script>
     37if (!navigator.gpu)
     38    document.body.className = 'error';
     39
    1240const positionAttributeNum  = 0;
    13 const texCoordsAttributeNum = 1;
     41const colorAttributeNum = 1;
    1442
    1543const transformBindingNum   = 0;
    16 const textureBindingNum     = 1;
    17 const samplerBindingNum     = 2;
    18 
    19 const vertexBufferIndex     = 0;
     44
    2045const bindGroupIndex        = 0;
    2146
    2247const shader = `
    23 #include <metal_stdlib>
    24 
    25 using namespace metal;
    26 
    27 struct Vertex
     48struct FragmentData {
     49    float4 position : SV_Position;
     50    float4 color : attribute(${colorAttributeNum});
     51}
     52
     53vertex FragmentData vertex_main(
     54    float4 position : attribute(${positionAttributeNum}),
     55    float4 color : attribute(${colorAttributeNum}),
     56    constant float4x4[] modelViewProjectionMatrix : register(b${transformBindingNum}))
    2857{
    29     float4 position [[attribute(${positionAttributeNum})]];
    30     float2 texCoords [[attribute(${texCoordsAttributeNum})]];
    31 };
    32 
    33 struct FragmentData
     58    FragmentData out;
     59    out.position = mul(modelViewProjectionMatrix[0], position);
     60    out.color = color;
     61   
     62    return out;
     63}
     64
     65fragment float4 fragment_main(float4 color : attribute(${colorAttributeNum})) : SV_Target 0
    3466{
    35     float4 position [[position]];
    36     float2 texCoords;
    37 };
    38 
    39 struct Uniform
    40 {
    41     device float4x4* modelViewProjectionMatrix [[id(${transformBindingNum})]];
    42 };
    43 
    44 struct SampledTexture
    45 {
    46     texture2d<float> faceTexture [[id(${textureBindingNum})]];
    47     sampler faceSampler [[id(${samplerBindingNum})]];
    48 };
    49 
    50 vertex FragmentData vertex_main(Vertex vertexIn [[stage_in]],
    51                             const device Uniform& uniforms [[buffer(${bindGroupIndex})]])
    52 {
    53     FragmentData output;
    54     output.position = uniforms.modelViewProjectionMatrix[0] * vertexIn.position;
    55     output.texCoords = vertexIn.texCoords;
    56 
    57     return output;
    58 }
    59 
    60 fragment float4 fragment_main(FragmentData data [[stage_in]],
    61                             const device SampledTexture& args [[buffer(${bindGroupIndex})]])
    62 {
    63     float4 color = args.faceTexture.sample(args.faceSampler, data.texCoords);
    64     if (color.a < 1)
    65         discard_fragment();
    66 
    6767    return color;
    6868}
    69 `
    70 
    71 let device, swapChain, verticesBuffer, bindGroupLayout, pipeline, renderPassDescriptor, queue, textureViewBinding, samplerBinding;
     69`;
     70
     71let device, swapChain, verticesBuffer, bindGroupLayout, pipeline, renderPassDescriptor;
    7272let projectionMatrix = mat4.create();
    7373
    74 const texCoordsOffset = 4 * 4;
    75 const vertexSize = 4 * 6;
    76 function createVerticesArray() {
    77     return new Float32Array([
    78         // float4 position, float2 texCoords
    79         1, -1, 1, 1, 1, 0,
    80         -1, -1, 1, 1, 0, 0,
    81         -1, -1, -1, 1, 0, 1,
    82         1, -1, -1, 1, 1, 1,
    83         1, -1, 1, 1, 1, 0,
    84         -1, -1, -1, 1, 0, 1,
    85 
    86         1, 1, 1, 1, 0, 0,
    87         1, -1, 1, 1, 0, 1,
    88         1, -1, -1, 1, 1, 1,
    89         1, 1, -1, 1, 1, 0,
    90         1, 1, 1, 1, 0, 0,
    91         1, -1, -1, 1, 1, 1,
    92 
    93         -1, 1, 1, 1, 0, 1,
    94         1, 1, 1, 1, 1, 1,
    95         1, 1, -1, 1, 1, 0,
    96         -1, 1, -1, 1, 0, 0,
    97         -1, 1, 1, 1, 0, 1,
    98         1, 1, -1, 1, 1, 0,
    99 
    100         -1, -1, 1, 1, 1, 1,
    101         -1, 1, 1, 1, 1, 0,
    102         -1, 1, -1, 1, 0, 0,
    103         -1, -1, -1, 1, 0, 1,
    104         -1, -1, 1, 1, 1, 1,
    105         -1, 1, -1, 1, 0, 0,
    106 
    107         1, 1, 1, 1, 1, 0,
    108         -1, 1, 1, 1, 0, 0,
    109         -1, -1, 1, 1, 0, 1,
    110         -1, -1, 1, 1, 0, 1,
    111         1, -1, 1, 1, 1, 1,
    112         1, 1, 1, 1, 1, 0,
    113 
    114         1, -1, -1, 1, 0, 1,
    115         -1, -1, -1, 1, 1, 1,
    116         -1, 1, -1, 1, 1, 0,
    117         1, 1, -1, 1, 0, 0,
    118         1, -1, -1, 1, 0, 1,
    119         -1, 1, -1, 1, 1, 0,
    120     ]);
    121 }
     74const colorOffset = 4 * 4;
     75const vertexSize = 4 * 8;
     76const verticesArray = new Float32Array([
     77    // float4 position, float4 color
     78    1, -1, 1, 1, 1, 0, 1, 1,
     79    -1, -1, 1, 1, 0, 0, 1, 1,
     80    -1, -1, -1, 1, 0, 0, 0, 1,
     81    1, -1, -1, 1, 1, 0, 0, 1,
     82    1, -1, 1, 1, 1, 0, 1, 1,
     83    -1, -1, -1, 1, 0, 0, 0, 1,
     84
     85    1, 1, 1, 1, 1, 1, 1, 1,
     86    1, -1, 1, 1, 1, 0, 1, 1,
     87    1, -1, -1, 1, 1, 0, 0, 1,
     88    1, 1, -1, 1, 1, 1, 0, 1,
     89    1, 1, 1, 1, 1, 1, 1, 1,
     90    1, -1, -1, 1, 1, 0, 0, 1,
     91
     92    -1, 1, 1, 1, 0, 1, 1, 1,
     93    1, 1, 1, 1, 1, 1, 1, 1,
     94    1, 1, -1, 1, 1, 1, 0, 1,
     95    -1, 1, -1, 1, 0, 1, 0, 1,
     96    -1, 1, 1, 1, 0, 1, 1, 1,
     97    1, 1, -1, 1, 1, 1, 0, 1,
     98
     99    -1, -1, 1, 1, 0, 0, 1, 1,
     100    -1, 1, 1, 1, 0, 1, 1, 1,
     101    -1, 1, -1, 1, 0, 1, 0, 1,
     102    -1, -1, -1, 1, 0, 0, 0, 1,
     103    -1, -1, 1, 1, 0, 0, 1, 1,
     104    -1, 1, -1, 1, 0, 1, 0, 1,
     105
     106    1, 1, 1, 1, 1, 1, 1, 1,
     107    -1, 1, 1, 1, 0, 1, 1, 1,
     108    -1, -1, 1, 1, 0, 0, 1, 1,
     109    -1, -1, 1, 1, 0, 0, 1, 1,
     110    1, -1, 1, 1, 1, 0, 1, 1,
     111    1, 1, 1, 1, 1, 1, 1, 1,
     112
     113    1, -1, -1, 1, 1, 0, 0, 1,
     114    -1, -1, -1, 1, 0, 0, 0, 1,
     115    -1, 1, -1, 1, 0, 1, 0, 1,
     116    1, 1, -1, 1, 1, 1, 0, 1,
     117    1, -1, -1, 1, 1, 0, 0, 1,
     118    -1, 1, -1, 1, 0, 1, 0, 1,
     119]);
    122120
    123121async function init() {
     
    136134
    137135    const swapChainDescriptor = {
    138         device: device,
     136        device: device, 
    139137        format: "bgra8unorm"
    140138    };
    141139    swapChain = context.configureSwapChain(swapChainDescriptor);
    142140
    143     // WebKit WebGPU accepts only MSL for now.
    144     const shaderModuleDescriptor = { code: shader };
     141    const shaderModuleDescriptor = { code: shader, isWHLSL: true };
    145142    const shaderModule = device.createShaderModule(shaderModuleDescriptor);
    146143
    147     const verticesArray = createVerticesArray();
    148144    const verticesBufferDescriptor = {
    149145        size: verticesArray.byteLength,
    150146        usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST
    151147    };
    152     verticesBuffer = device.createBuffer(verticesBufferDescriptor);
    153     verticesBuffer.setSubData(0, verticesArray.buffer);
    154 
    155     // Input state. Model will change soon to adopt one of https://github.com/kainino0x/gpuweb/pull/2/'s ideas.
     148    let verticesArrayBuffer;
     149    [verticesBuffer, verticesArrayBuffer] = device.createBufferMapped(verticesBufferDescriptor);
     150
     151    const verticesWriteArray = new Float32Array(verticesArrayBuffer);
     152    verticesWriteArray.set(verticesArray);
     153    verticesBuffer.unmap();
     154
     155    // Vertex Input
    156156    const positionAttributeDescriptor = {
    157         shaderLocation: positionAttributeNum,  // [[attribute(0)]].
    158         inputSlot: vertexBufferIndex,       // Used as vertex buffer index in Metal.
     157        shaderLocation: positionAttributeNum,  // [[attribute(0)]]
    159158        offset: 0,
    160159        format: "float4"
    161160    };
    162     const texCoordsAttributeDescriptor = {
    163         shaderLocation: texCoordsAttributeNum,
    164         inputSlot: vertexBufferIndex,
    165         offset: texCoordsOffset,
    166         format: "float2"
     161    const colorAttributeDescriptor = {
     162        shaderLocation: colorAttributeNum,
     163        offset: colorOffset,
     164        format: "float4"
    167165    }
    168166    const vertexBufferDescriptor = {
    169         inputSlot: vertexBufferIndex,
     167        attributeSet: [positionAttributeDescriptor, colorAttributeDescriptor],
    170168        stride: vertexSize,
    171169        stepMode: "vertex"
    172170    };
    173     const inputStateDescriptor = {
    174         indexFormat: "uint32",
    175         attributes: [positionAttributeDescriptor, texCoordsAttributeDescriptor],
    176         inputs: [vertexBufferDescriptor]
    177     };
    178 
    179     // Texture
    180 
    181     // Load texture image
    182     const image = new Image();
    183     const imageLoadPromise = new Promise(resolve => {
    184         image.onload = () => resolve();
    185         image.src = "resources/safari-alpha.png"
    186     });
    187     await Promise.resolve(imageLoadPromise);
    188 
    189     const textureSize = {
    190         width: image.width,
    191         height: image.height,
    192         depth: 1
    193     };
    194 
    195     const textureDescriptor = {
    196         size: textureSize,
    197         arrayLayerCount: 1,
    198         mipLevelCount: 1,
    199         sampleCount: 1,
    200         dimension: "2d",
    201         format: "rgba8unorm",
    202         usage: GPUTextureUsage.TRANSFER_DST | GPUTextureUsage.SAMPLED
    203     };
    204     const texture = device.createTexture(textureDescriptor);
    205 
    206     // Texture data
    207     const canvas2d = document.createElement('canvas');
    208     canvas2d.width = image.width;
    209     canvas2d.height = image.height;
    210     const context2d = canvas2d.getContext('2d');
    211     context2d.drawImage(image, 0, 0);
    212 
    213     const imageData = context2d.getImageData(0, 0, image.width, image.height);
    214 
    215     const textureDataBufferDescriptor = {
    216         size: imageData.data.length,
    217         usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.TRANSFER_DST
    218     };
    219     const textureDataBuffer = device.createBuffer(textureDataBufferDescriptor);
    220     textureDataBuffer.setSubData(0, imageData.data.buffer);
    221 
    222     const dataCopyView = {
    223         buffer: textureDataBuffer,
    224         offset: 0,
    225         rowPitch: image.width * 4,
    226         imageHeight: 0
    227     };
    228     const textureCopyView = {
    229         texture: texture,
    230         mipLevel: 0,
    231         arrayLayer: 0,
    232         origin: { x: 0, y: 0, z: 0 }
    233     };
    234 
    235     const blitCommandEncoder = device.createCommandEncoder();
    236     blitCommandEncoder.copyBufferToTexture(dataCopyView, textureCopyView, textureSize);
    237 
    238     queue = device.getQueue();
    239 
    240     queue.submit([blitCommandEncoder.finish()]);
     171    const vertexInputDescriptor = { vertexBuffers: [vertexBufferDescriptor] };
    241172
    242173    // Bind group binding layout
     
    247178    };
    248179
    249     const textureBindGroupLayoutBinding = {
    250         binding: textureBindingNum,
    251         visibility: GPUShaderStageBit.FRAGMENT,
    252         type: "sampled-texture"
    253     };
    254     textureViewBinding = {
    255         binding: textureBindingNum,
    256         resource: texture.createDefaultView()
    257     };
    258 
    259     const samplerBindGroupLayoutBinding = {
    260         binding: samplerBindingNum,
    261         visibility: GPUShaderStageBit.FRAGMENT,
    262         type: "sampler"
    263     };
    264     samplerBinding = {
    265         binding: samplerBindingNum,
    266         resource: device.createSampler({})
    267     };
    268 
    269     const bindGroupLayoutDescriptor = {
    270         bindings: [transformBufferBindGroupLayoutBinding, textureBindGroupLayoutBinding, samplerBindGroupLayoutBinding]
    271     };
     180    const bindGroupLayoutDescriptor = { bindings: [transformBufferBindGroupLayoutBinding] };
    272181    bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
    273182
     
    311220        colorStates: [colorState],
    312221        depthStencilState: depthStateDescriptor,
    313         inputState: inputStateDescriptor
     222        vertexInput: vertexInputDescriptor
    314223    };
    315224    pipeline = device.createRenderPipeline(pipelineDescriptor);
     
    319228        loadOp: "clear",
    320229        storeOp: "store",
    321         clearColor: { r: 0.5, g: 1.0, b: 1.0, a: 1.0 } // GPUColor
     230        clearColor: { r: 0.15, g: 0.15, b: 0.5, a: 1.0 } // GPUColor
    322231    };
    323232
     
    361270/* Transform Buffers and Bindings */
    362271const transformSize = 4 * 16;
     272
     273const transformBufferDescriptor = {
     274    size: transformSize,
     275    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE
     276};
     277
     278let mappedGroups = [];
     279
     280function render() {
     281    if (mappedGroups.length === 0) {
     282        const [buffer, arrayBuffer] = device.createBufferMapped(transformBufferDescriptor);
     283        const group = device.createBindGroup(createBindGroupDescriptor(buffer));
     284        let mappedGroup = { buffer: buffer, arrayBuffer: arrayBuffer, bindGroup: group };
     285        drawCommands(mappedGroup);
     286    } else
     287        drawCommands(mappedGroups.shift());
     288}
     289
     290function createBindGroupDescriptor(transformBuffer) {
     291    const transformBufferBinding = {
     292        buffer: transformBuffer,
     293        offset: 0,
     294        size: transformSize
     295    };
     296    const transformBufferBindGroupBinding = {
     297        binding: transformBindingNum,
     298        resource: transformBufferBinding
     299    };
     300    return {
     301        layout: bindGroupLayout,
     302        bindings: [transformBufferBindGroupBinding]
     303    };
     304}
     305
     306function drawCommands(mappedGroup) {
     307    updateTransformArray(new Float32Array(mappedGroup.arrayBuffer));
     308    mappedGroup.buffer.unmap();
     309
     310    const commandEncoder = device.createCommandEncoder();
     311    renderPassDescriptor.colorAttachments[0].attachment = swapChain.getCurrentTexture().createDefaultView();
     312    const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
     313
     314    // Encode drawing commands
     315
     316    passEncoder.setPipeline(pipeline);
     317    // Vertex attributes
     318    passEncoder.setVertexBuffers(0, [verticesBuffer], [0]);
     319    // Bind groups
     320    passEncoder.setBindGroup(bindGroupIndex, mappedGroup.bindGroup);
     321    // 36 vertices, 1 instance, 0th vertex, 0th instance.
     322    passEncoder.draw(36, 1, 0, 0);
     323    passEncoder.endPass();
     324
     325    device.getQueue().submit([commandEncoder.finish()]);
     326
     327    // Ready the current buffer for update after GPU is done with it.
     328    mappedGroup.buffer.mapWriteAsync().then((arrayBuffer) => {
     329        mappedGroup.arrayBuffer = arrayBuffer;
     330        mappedGroups.push(mappedGroup);
     331    });
     332
     333    requestAnimationFrame(render);
     334}
     335
    363336function updateTransformArray(array) {
    364337    let viewMatrix = mat4.create();
     
    368341    let modelViewProjectionMatrix = mat4.create();
    369342    mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
    370     for (let i = 0; i < 16; i++) {
    371         array[i] = modelViewProjectionMatrix[i];
    372     }
    373 }
    374 
    375 const transformBufferDescriptor = {
    376     size: transformSize,
    377     usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE
    378 };
    379 
    380 function createBindGroupDescriptor(transformBuffer, textureViewBinding, samplerBinding) {
    381     const transformBufferBinding = {
    382         buffer: transformBuffer,
    383         offset: 0,
    384         size: transformSize
    385     };
    386     const transformBufferBindGroupBinding = {
    387         binding: transformBindingNum,
    388         resource: transformBufferBinding
    389     };
    390     return {
    391         layout: bindGroupLayout,
    392         bindings: [transformBufferBindGroupBinding, textureViewBinding, samplerBinding]
    393     };
    394 }
    395 
    396 let mappedGroups = [];
    397 
    398 function render() {
    399     if (mappedGroups.length == 0) {
    400         const buffer = device.createBuffer(transformBufferDescriptor);
    401         buffer.mapWriteAsync().then(arrayBuffer => {
    402             const group = device.createBindGroup(createBindGroupDescriptor(buffer, textureViewBinding, samplerBinding));
    403 
    404             let mappedGroup = { buffer: buffer, arrayBuffer: arrayBuffer, bindGroup: group };
    405             drawCommands(mappedGroup);
    406         });
    407     } else
    408         drawCommands(mappedGroups.shift());
    409 }
    410 
    411 function drawCommands(mappedGroup) {
    412     updateTransformArray(new Float32Array(mappedGroup.arrayBuffer));
    413     mappedGroup.buffer.unmap();
    414 
    415     const commandEncoder = device.createCommandEncoder();
    416     renderPassDescriptor.colorAttachments[0].attachment = swapChain.getCurrentTexture().createDefaultView();
    417     const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
    418     // Encode drawing commands.
    419     passEncoder.setPipeline(pipeline);
    420     // Vertex attributes
    421     passEncoder.setVertexBuffers(vertexBufferIndex, [verticesBuffer], [0]);
    422     // Bind groups
    423     passEncoder.setBindGroup(bindGroupIndex, mappedGroup.bindGroup);
    424     passEncoder.draw(36, 1, 0, 0);
    425     passEncoder.endPass();
    426 
    427     queue.submit([commandEncoder.finish()]);
    428 
    429     // Ready the current buffer for update after GPU is done with it.
    430     mappedGroup.buffer.mapWriteAsync().then((arrayBuffer) => {
    431         mappedGroup.arrayBuffer = arrayBuffer;
    432         mappedGroups.push(mappedGroup);
    433     });
    434 
    435     requestAnimationFrame(render);
    436 }
    437 
    438 init();
     343    mat4.copy(array, modelViewProjectionMatrix);
     344}
     345
     346window.addEventListener("load", init);
    439347</script>
    440348</body>
  • trunk/Websites/webkit.org/demos/webgpu/hello-triangle.html

    r244992 r248309  
    44<meta name="viewport" content="width=600">
    55<meta http-equiv="Content-type" content="text/html; charset=utf-8">
    6 <title>Web GPU Hello Triangle demo</title>
     6<title>WebGPU Hello Triangle</title>
    77<link rel="stylesheet" href="css/style.css">
    8 <link rel="stylesheet" href="https://webkit.org/wp-content/themes/webkit/style.css">
     8<style>
     9body {
     10    font-family: system-ui;
     11    color: #f7f7ff;
     12    background-color: rgb(38, 38, 127);
     13    text-align: center;
     14}
     15canvas {
     16    margin: 0 auto;
     17}
     18</style>
    919</head>
    1020<body>
    11 <canvas></canvas>
     21<div id="contents">
     22    <h1>Simple Triangle</h1>
     23    <canvas></canvas>
     24</div>
    1225<div id="error">
    13     <h2>Web GPU not available</h2>
     26    <h2>WebGPU not available</h2>
    1427    <p>
    15         Make sure you are on a system with Web GPU enabled. In
     28        Make sure you are on a system with WebGPU enabled. In
    1629        Safari, first make sure the Developer Menu is visible (Preferences →
    17         Advanced), then Develop → Experimental Features → Enable Web GPU.
     30        Advanced), then Develop → Experimental Features → WebGPU.
    1831    </p>
    1932</div>
  • trunk/Websites/webkit.org/demos/webgpu/index.html

    r244992 r248309  
    44<meta name="viewport" content="width=device-width">
    55<meta http-equiv="Content-type" content="text/html; charset=utf-8">
    6 <title>WebMetal demos</title>
     6<title>WebGPU demos</title>
    77<link rel="stylesheet" href="https://webkit.org/wp-content/themes/webkit/style.css">
    88<link rel="stylesheet" href="https://www.apple.com/wss/fonts?family=Myriad+Set+Pro&amp;v=1">
     
    163163    <header>
    164164        <img src="resources/circle.svg">
    165         <h1>Web GPU<br>demos</h1>
     165        <h1>WebGPU<br>demos</h1>
    166166    </header>
    167167    <section class="demos">
    168168        <p class="intro">
    169169            Here are a collection of simple <a
    170             href="https://en.wikipedia.org/wiki/WebGPU">Web GPU</a>
     170            href="https://en.wikipedia.org/wiki/WebGPU">WebGPU</a>
    171171            examples. They should work in the latest <a
    172             href="https://webkit.org/build-archives/">WebKit</a> builds, and
    173             soon in a <a
    174             href="https://developer.apple.com/safari/technology-preview/">Safari
    175              Technology Preview</a> release. The full specification is a work-in-progress on <a
     172            href="https://webkit.org/build-archives/">WebKit</a> builds and
     173            <a href="https://developer.apple.com/safari/technology-preview/">Safari
     174             Technology Preview</a> release. The full <a href="https://gpuweb.github.io/gpuweb/">specification</a> is a work-in-progress on <a
    176175            href="https://github.com/gpuweb/gpuweb
    177176            ">GitHub</a>, and the implementation may differ from the current API.
    178177        </p>
    179178        <p class="howto">
    180             Make sure you are on a system with Web GPU enabled. In Safari, first
     179            Make sure you are on a system with WebGPU enabled. In Safari, first
    181180            make sure the Developer Menu is visible (Preferences → Advanced),
    182             then ensure Develop → Experimental Features → Web GPU is checked.
     181            then ensure Develop → Experimental Features → WebGPU is checked.
    183182        </p>
    184183        <p class="warning">
    185             Web GPU is an experimental technology, and you should not browse the entire
     184            WebGPU is an experimental technology, and you should not browse the entire
    186185            Web with it enabled for now. It doesn't do much validation of content, and
    187186            thus may cause some issues with your computer.
     
    195194        <div class="example">
    196195            <a href="hello-cube.html">
    197             <img src="resources/hello-cube.png"><br>
     196            <img src = "resources/hello-cube.png"><br>
     197            Hello World Cube
     198            </a>
     199        </div>
     200        <div class="example">
     201            <a href="textured-cube.html">
     202            <img src="resources/textured-cube.png"><br>
    198203            Textured Spinning Cube
     204            </a>
     205        </div>
     206        <div class="example">
     207            <a href="compute-blur.html">
     208            <img src="resources/compute-blur.png"><br>
     209            Compute Shader Blur
    199210            </a>
    200211        </div>
  • trunk/Websites/webkit.org/demos/webgpu/scripts/hello-triangle.js

    r244992 r248309  
    1313    const positionLocation = 0;
    1414    const colorLocation = 1;
    15    
    16     const shaderSource = `
    17     #include <metal_stdlib>
    18  
    19     using namespace metal;
    20  
    21     struct Vertex {
    22         float4 position [[attribute(${positionLocation})]];
    23         float4 color [[attribute(${colorLocation})]];
    24     };
    25  
     15
     16    const whlslSource = `
    2617    struct FragmentData {
    27         float4 position [[position]];
    28         float4 color;
    29     };
    30  
    31     vertex FragmentData vertexMain(const Vertex in [[stage_in]])
     18        float4 position : SV_Position;
     19        float4 color : attribute(${colorLocation});
     20    }
     21
     22    vertex FragmentData vertexMain(float4 position : attribute(${positionLocation}), float4 color : attribute(${colorLocation}))
    3223    {
    33         return FragmentData { in.position, in.color };
     24        FragmentData out;
     25
     26        out.position = position;
     27        out.color = color;
     28
     29        return out;
    3430    }
    35  
    36     fragment float4 fragmentMain(const FragmentData in [[stage_in]])
     31
     32    fragment float4 fragmentMain(float4 color : attribute(${colorLocation})) : SV_Target 0
    3733    {
    38         return in.color;
     34        return color;
    3935    }
    4036    `;
    41     const shaderModule = device.createShaderModule({ code: shaderSource });
     37    const shaderModule = device.createShaderModule({ code: whlslSource, isWHLSL: true });
    4238   
    4339    /* GPUPipelineStageDescriptors */
     
    5349   
    5450    /* GPUBufferDescriptor */
    55     const vertexBufferDescriptor = {
     51    const vertexDataBufferDescriptor = {
    5652        size: vertexDataSize,
    5753        usage: GPUBufferUsage.MAP_WRITE | GPUBufferUsage.VERTEX
    5854    };
    5955    /* GPUBuffer */
    60     const vertexBuffer = device.createBuffer(vertexBufferDescriptor);
     56    const vertexBuffer = device.createBuffer(vertexDataBufferDescriptor);
    6157   
    6258    /*** Write Data To GPU ***/
     
    7975    const positionAttribute = {
    8076        shaderLocation: positionLocation,
    81         inputSlot: vertexBufferSlot,
    8277        offset: 0,
    8378        format: "float4"
     
    8580    const colorAttribute = {
    8681        shaderLocation: colorLocation,
    87         inputSlot: vertexBufferSlot,
    8882        offset: colorOffset,
    8983        format: "float4"
    9084    };
    91    
     85
     86    /* GPUVertexBufferDescriptor */
     87    const vertexBufferDescriptor = {
     88        stride: vertexStride,
     89        attributeSet: [positionAttribute, colorAttribute]
     90    };
     91
    9292    /* GPUVertexInputDescriptor */
    9393    const vertexInputDescriptor = {
    94         inputSlot: vertexBufferSlot,
    95         stride: vertexStride,
    96         stepMode: "vertex"
    97     };
    98    
    99     /* GPUInputStateDescriptor */
    100     const inputStateDescriptor = {
    101         attributes: [positionAttribute, colorAttribute],
    102         inputs: [vertexInputDescriptor]
     94        vertexBuffers: [vertexBufferDescriptor]
    10395    };
    10496   
     
    123115        primitiveTopology: "triangle-list",
    124116        colorStates: [colorStateDescriptor],
    125         inputState: inputStateDescriptor
     117        vertexInput: vertexInputDescriptor
    126118    };
    127119    /* GPURenderPipeline */
     
    131123   
    132124    const canvas = document.querySelector("canvas");
    133     let canvasSize = canvas.getBoundingClientRect();
    134     canvas.width = canvasSize.width;
    135     canvas.height = canvasSize.height;
     125    canvas.width = 600;
     126    canvas.height = 600;
    136127
    137128    const gpuContext = canvas.getContext("gpu");
     
    152143   
    153144    /* GPUColor */
    154     const darkBlue = { r: 0, g: 0, b: 0.5, a: 1 };
     145    const darkBlue = { r: 0.15, g: 0.15, b: 0.5, a: 1 };
    155146   
    156147    /* GPURenderPassColorATtachmentDescriptor */
  • trunk/Websites/webkit.org/demos/webgpu/textured-cube.html

    r248308 r248309  
    33<head>
    44<meta name="viewport" content="width=1000">
    5 <title>WebGPU Cube demo</title>
     5<title>WebGPU Cube</title>
    66<script src="scripts/gl-matrix-min.js"></script>
    77<link rel="stylesheet" href="css/style.css"/>
     8<style>
     9body {
     10    font-family: system-ui;
     11    color: #f7f7ff;
     12    background-color: rgb(13, 77, 153);
     13    text-align: center;
     14}
     15canvas {
     16    margin: 0 auto;
     17}
     18p {
     19    margin: 0 8px;
     20}
     21</style>
    822</head>
    923<body>
    10 <canvas></canvas>
     24<div id="contents">
     25    <h1>Textured Cube</h1>
     26    <p>
     27        This demo uploads a PNG image as texture data and uses it on the faces of a cube.
     28    </p>
     29    <canvas width="1200" height="1200"></canvas>
     30</div>
     31<div id="error">
     32    <h2>WebGPU not available</h2>
     33    <p>
     34        Make sure you are on a system with WebGPU enabled. In
     35        Safari, first make sure the Developer Menu is visible (Preferences →
     36        Advanced), then Develop → Experimental Features → WebGPU.
     37    </p>
     38</div>
    1139<script>
     40if (!navigator.gpu)
     41    document.body.className = 'error';
     42
    1243const positionAttributeNum  = 0;
    1344const texCoordsAttributeNum = 1;
     
    1748const samplerBindingNum     = 2;
    1849
    19 const vertexBufferIndex     = 0;
    2050const bindGroupIndex        = 0;
    2151
    2252const shader = `
    23 #include <metal_stdlib>
    24 
    25 using namespace metal;
    26 
    27 struct Vertex
     53struct FragmentData {
     54    float4 position : SV_Position;
     55    float2 texCoords : attribute(${texCoordsAttributeNum});
     56}
     57
     58vertex FragmentData vertex_main(
     59    float4 position : attribute(${positionAttributeNum}),
     60    float2 texCoords : attribute(${texCoordsAttributeNum}),
     61    constant float4x4[] modelViewProjectionMatrix : register(b${transformBindingNum}))
    2862{
    29     float4 position [[attribute(${positionAttributeNum})]];
    30     float2 texCoords [[attribute(${texCoordsAttributeNum})]];
    31 };
    32 
    33 struct FragmentData
     63    FragmentData out;
     64    out.position = mul(modelViewProjectionMatrix[0], position);
     65    out.texCoords = texCoords;
     66   
     67    return out;
     68}
     69
     70fragment float4 fragment_main(
     71    float2 texCoords : attribute(${texCoordsAttributeNum}),
     72    Texture2D<float4> faceTexture : register(t${textureBindingNum}),
     73    sampler faceSampler : register(s${samplerBindingNum})) : SV_Target 0
    3474{
    35     float4 position [[position]];
    36     float2 texCoords;
    37 };
    38 
    39 struct Uniform
    40 {
    41     device float4x4* modelViewProjectionMatrix [[id(${transformBindingNum})]];
    42 };
    43 
    44 struct SampledTexture
    45 {
    46     texture2d<float> faceTexture [[id(${textureBindingNum})]];
    47     sampler faceSampler [[id(${samplerBindingNum})]];
    48 };
    49 
    50 vertex FragmentData vertex_main(Vertex vertexIn [[stage_in]],
    51                             const device Uniform& uniforms [[buffer(${bindGroupIndex})]])
    52 {
    53     FragmentData output;
    54     output.position = uniforms.modelViewProjectionMatrix[0] * vertexIn.position;
    55     output.texCoords = vertexIn.texCoords;
    56 
    57     return output;
    58 }
    59 
    60 fragment float4 fragment_main(FragmentData data [[stage_in]],
    61                             const device SampledTexture& args [[buffer(${bindGroupIndex})]])
    62 {
    63     float4 color = args.faceTexture.sample(args.faceSampler, data.texCoords);
    64     if (color.a < 1)
    65         discard_fragment();
    66 
    67     return color;
    68 }
    69 `
     75    return Sample(faceTexture, faceSampler, texCoords);
     76}
     77`;
    7078
    7179let device, swapChain, verticesBuffer, bindGroupLayout, pipeline, renderPassDescriptor, queue, textureViewBinding, samplerBinding;
     
    7482const texCoordsOffset = 4 * 4;
    7583const vertexSize = 4 * 6;
    76 function createVerticesArray() {
    77     return new Float32Array([
    78         // float4 position, float2 texCoords
    79         1, -1, 1, 1, 1, 0,
    80         -1, -1, 1, 1, 0, 0,
    81         -1, -1, -1, 1, 0, 1,
    82         1, -1, -1, 1, 1, 1,
    83         1, -1, 1, 1, 1, 0,
    84         -1, -1, -1, 1, 0, 1,
    85 
    86         1, 1, 1, 1, 0, 0,
    87         1, -1, 1, 1, 0, 1,
    88         1, -1, -1, 1, 1, 1,
    89         1, 1, -1, 1, 1, 0,
    90         1, 1, 1, 1, 0, 0,
    91         1, -1, -1, 1, 1, 1,
    92 
    93         -1, 1, 1, 1, 0, 1,
    94         1, 1, 1, 1, 1, 1,
    95         1, 1, -1, 1, 1, 0,
    96         -1, 1, -1, 1, 0, 0,
    97         -1, 1, 1, 1, 0, 1,
    98         1, 1, -1, 1, 1, 0,
    99 
    100         -1, -1, 1, 1, 1, 1,
    101         -1, 1, 1, 1, 1, 0,
    102         -1, 1, -1, 1, 0, 0,
    103         -1, -1, -1, 1, 0, 1,
    104         -1, -1, 1, 1, 1, 1,
    105         -1, 1, -1, 1, 0, 0,
    106 
    107         1, 1, 1, 1, 1, 0,
    108         -1, 1, 1, 1, 0, 0,
    109         -1, -1, 1, 1, 0, 1,
    110         -1, -1, 1, 1, 0, 1,
    111         1, -1, 1, 1, 1, 1,
    112         1, 1, 1, 1, 1, 0,
    113 
    114         1, -1, -1, 1, 0, 1,
    115         -1, -1, -1, 1, 1, 1,
    116         -1, 1, -1, 1, 1, 0,
    117         1, 1, -1, 1, 0, 0,
    118         1, -1, -1, 1, 0, 1,
    119         -1, 1, -1, 1, 1, 0,
    120     ]);
    121 }
     84const verticesArray = new Float32Array([
     85    // float4 position, float2 texCoords
     86    1, -1, -1, 1, 0, 1,
     87    -1, -1, -1, 1, 1, 1,
     88    -1, 1, -1, 1, 1, 0,
     89    1, 1, -1, 1, 0, 0,
     90    1, -1, -1, 1, 0, 1,
     91    -1, 1, -1, 1, 1, 0,
     92
     93    1, 1, 1, 1, 0, 0,
     94    1, -1, 1, 1, 0, 1,
     95    1, -1, -1, 1, 1, 1,
     96    1, 1, -1, 1, 1, 0,
     97    1, 1, 1, 1, 0, 0,
     98    1, -1, -1, 1, 1, 1,
     99
     100    1, -1, 1, 1, 1, 0,
     101    -1, -1, 1, 1, 0, 0,
     102    -1, -1, -1, 1, 0, 1,
     103    1, -1, -1, 1, 1, 1,
     104    1, -1, 1, 1, 1, 0,
     105    -1, -1, -1, 1, 0, 1,
     106
     107    -1, 1, 1, 1, 0, 1,
     108    1, 1, 1, 1, 1, 1,
     109    1, 1, -1, 1, 1, 0,
     110    -1, 1, -1, 1, 0, 0,
     111    -1, 1, 1, 1, 0, 1,
     112    1, 1, -1, 1, 1, 0,
     113
     114    -1, -1, 1, 1, 1, 1,
     115    -1, 1, 1, 1, 1, 0,
     116    -1, 1, -1, 1, 0, 0,
     117    -1, -1, -1, 1, 0, 1,
     118    -1, -1, 1, 1, 1, 1,
     119    -1, 1, -1, 1, 0, 0,
     120
     121    1, 1, 1, 1, 1, 0,
     122    -1, 1, 1, 1, 0, 0,
     123    -1, -1, 1, 1, 0, 1,
     124    -1, -1, 1, 1, 0, 1,
     125    1, -1, 1, 1, 1, 1,
     126    1, 1, 1, 1, 1, 0,
     127]);
    122128
    123129async function init() {
     
    126132
    127133    const canvas = document.querySelector('canvas');
    128     let canvasSize = canvas.getBoundingClientRect();
    129     canvas.width = canvasSize.width;
    130     canvas.height = canvasSize.height;
    131134
    132135    const aspect = Math.abs(canvas.width / canvas.height);
     
    136139
    137140    const swapChainDescriptor = {
    138         device: device,
     141        device: device, 
    139142        format: "bgra8unorm"
    140143    };
     
    142145
    143146    // WebKit WebGPU accepts only MSL for now.
    144     const shaderModuleDescriptor = { code: shader };
     147    const shaderModuleDescriptor = { code: shader, isWHLSL: true };
    145148    const shaderModule = device.createShaderModule(shaderModuleDescriptor);
    146149
    147     const verticesArray = createVerticesArray();
    148150    const verticesBufferDescriptor = {
    149151        size: verticesArray.byteLength,
    150152        usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST
    151153    };
    152     verticesBuffer = device.createBuffer(verticesBufferDescriptor);
    153     verticesBuffer.setSubData(0, verticesArray.buffer);
    154 
    155     // Input state. Model will change soon to adopt one of https://github.com/kainino0x/gpuweb/pull/2/'s ideas.
     154    let verticesArrayBuffer;
     155    [verticesBuffer, verticesArrayBuffer] = device.createBufferMapped(verticesBufferDescriptor);
     156
     157    const verticesWriteArray = new Float32Array(verticesArrayBuffer);
     158    verticesWriteArray.set(verticesArray);
     159    verticesBuffer.unmap();
     160
     161    // Vertex Input
     162   
    156163    const positionAttributeDescriptor = {
    157164        shaderLocation: positionAttributeNum,  // [[attribute(0)]].
    158         inputSlot: vertexBufferIndex,       // Used as vertex buffer index in Metal.
    159165        offset: 0,
    160166        format: "float4"
     
    162168    const texCoordsAttributeDescriptor = {
    163169        shaderLocation: texCoordsAttributeNum,
    164         inputSlot: vertexBufferIndex,
    165170        offset: texCoordsOffset,
    166171        format: "float2"
    167172    }
    168173    const vertexBufferDescriptor = {
    169         inputSlot: vertexBufferIndex,
     174        attributeSet: [positionAttributeDescriptor, texCoordsAttributeDescriptor],
    170175        stride: vertexSize,
    171176        stepMode: "vertex"
    172177    };
    173     const inputStateDescriptor = {
    174         indexFormat: "uint32",
    175         attributes: [positionAttributeDescriptor, texCoordsAttributeDescriptor],
    176         inputs: [vertexBufferDescriptor]
    177     };
     178    const vertexInputDescriptor = { vertexBuffers: [vertexBufferDescriptor] };
    178179
    179180    // Texture
     
    215216    const textureDataBufferDescriptor = {
    216217        size: imageData.data.length,
    217         usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.TRANSFER_DST
    218     };
    219     const textureDataBuffer = device.createBuffer(textureDataBufferDescriptor);
    220     textureDataBuffer.setSubData(0, imageData.data.buffer);
     218        usage: GPUBufferUsage.TRANSFER_SRC
     219    };
     220    const [textureDataBuffer, textureArrayBuffer] = device.createBufferMapped(textureDataBufferDescriptor);
     221   
     222    const textureWriteArray = new Uint8Array(textureArrayBuffer);
     223    textureWriteArray.set(imageData.data);
     224    textureDataBuffer.unmap();
    221225
    222226    const dataCopyView = {
     
    311315        colorStates: [colorState],
    312316        depthStencilState: depthStateDescriptor,
    313         inputState: inputStateDescriptor
     317        vertexInput: vertexInputDescriptor
    314318    };
    315319    pipeline = device.createRenderPipeline(pipelineDescriptor);
     
    319323        loadOp: "clear",
    320324        storeOp: "store",
    321         clearColor: { r: 0.5, g: 1.0, b: 1.0, a: 1.0 } // GPUColor
     325        clearColor: { r: 0.05, g: .3, b: .6, a: 1.0 } // GPUColor
    322326    };
    323327
     
    361365/* Transform Buffers and Bindings */
    362366const transformSize = 4 * 16;
    363 function updateTransformArray(array) {
    364     let viewMatrix = mat4.create();
    365     mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -5));
    366     let now = Date.now() / 1000;
    367     mat4.rotate(viewMatrix, viewMatrix, 1, vec3.fromValues(Math.sin(now), Math.cos(now), 0));
    368     let modelViewProjectionMatrix = mat4.create();
    369     mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
    370     for (let i = 0; i < 16; i++) {
    371         array[i] = modelViewProjectionMatrix[i];
    372     }
    373 }
    374367
    375368const transformBufferDescriptor = {
     
    377370    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE
    378371};
     372
     373let mappedGroups = [];
     374
     375function render() {
     376    if (mappedGroups.length === 0) {
     377        const [buffer, arrayBuffer] = device.createBufferMapped(transformBufferDescriptor);
     378        const group = device.createBindGroup(createBindGroupDescriptor(buffer, textureViewBinding, samplerBinding));
     379        let mappedGroup = { buffer: buffer, arrayBuffer: arrayBuffer, bindGroup: group };
     380        drawCommands(mappedGroup);
     381    } else
     382        drawCommands(mappedGroups.shift());
     383}
    379384
    380385function createBindGroupDescriptor(transformBuffer, textureViewBinding, samplerBinding) {
     
    392397        bindings: [transformBufferBindGroupBinding, textureViewBinding, samplerBinding]
    393398    };
    394 }
    395 
    396 let mappedGroups = [];
    397 
    398 function render() {
    399     if (mappedGroups.length == 0) {
    400         const buffer = device.createBuffer(transformBufferDescriptor);
    401         buffer.mapWriteAsync().then(arrayBuffer => {
    402             const group = device.createBindGroup(createBindGroupDescriptor(buffer, textureViewBinding, samplerBinding));
    403 
    404             let mappedGroup = { buffer: buffer, arrayBuffer: arrayBuffer, bindGroup: group };
    405             drawCommands(mappedGroup);
    406         });
    407     } else
    408         drawCommands(mappedGroups.shift());
    409399}
    410400
     
    419409    passEncoder.setPipeline(pipeline);
    420410    // Vertex attributes
    421     passEncoder.setVertexBuffers(vertexBufferIndex, [verticesBuffer], [0]);
     411    passEncoder.setVertexBuffers(0, [verticesBuffer], [0]);
    422412    // Bind groups
    423413    passEncoder.setBindGroup(bindGroupIndex, mappedGroup.bindGroup);
     
    436426}
    437427
    438 init();
     428function updateTransformArray(array) {
     429    let viewMatrix = mat4.create();
     430    mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -5));
     431    let now = Date.now() / 1000;
     432    mat4.rotate(viewMatrix, viewMatrix, 1, vec3.fromValues(Math.sin(now), 1, 1));
     433    let modelViewProjectionMatrix = mat4.create();
     434    mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
     435    mat4.copy(array, modelViewProjectionMatrix);
     436}
     437
     438window.addEventListener("load", init);
    439439</script>
    440440</body>
Note: See TracChangeset for help on using the changeset viewer.