| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <title>WebGPU Hello Triangles</title> |
| <meta name="assert" content="WebGPU correctly renders a green canvas."> |
| <link rel="match" href="draw-indexed-triangles-expected.html"> |
| <p>Pass if square canvas below is completely green.</p> |
| <canvas width="400" height="400"></canvas> |
| <script src="js/webgpu-functions.js"></script> |
| <script> |
| if (window.testRunner) |
| testRunner.waitUntilDone(); |
| |
| const shaderCode = ` |
| #include <metal_stdlib> |
| |
| using namespace metal; |
| |
| struct VertexIn |
| { |
| float4 position [[attribute(0)]]; |
| float green [[attribute(1)]]; |
| }; |
| |
| struct VertexOut |
| { |
| float4 position [[position]]; |
| float4 color; |
| }; |
| |
| vertex VertexOut vertex_main(VertexIn vertexIn [[stage_in]]) |
| { |
| VertexOut vOut; |
| vOut.position = vertexIn.position; |
| vOut.color = float4(0, vertexIn.green, 0, 1); |
| return vOut; |
| } |
| |
| fragment float4 fragment_main(VertexOut v [[stage_in]]) |
| { |
| return v.color; |
| } |
| ` |
| |
| function createVertexBuffer(device) { |
| const vertexArray = new Float32Array([ |
| // float4 xyzw, float g |
| -1, 1, 0, 1, 0, |
| -1, 1, 0, 1, 1, |
| -1, -1, 0, 1, 0, |
| -1, -1, 0, 1, 1, |
| 1, 1, 0, 1, 0, |
| 1, 1, 0, 1, 1, |
| 1, -1, 0, 1, 0, |
| 1, -1, 0, 1, 1 |
| ]); |
| const buffer = device.createBuffer({ size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST }); |
| buffer.setSubData(0, vertexArray.buffer); |
| |
| return buffer; |
| } |
| |
| const indexBufferOffset = 2048; // Test a buffer offset for index array. |
| const indexOffset = -9001; // Test a base index to add to index array values. |
| |
| function createIndexBuffer(device) { |
| const offsetArray = [1, 3, 5, 3, 5, 7].map(v => { return v - indexOffset; }); |
| const indexArray = new Uint32Array([1, 3, 5, 3, 5, 7].map(v => { return v - indexOffset; })); |
| const buffer = device.createBuffer({ size: indexArray.byteLength + indexBufferOffset, usage: GPUBufferUsage.INDEX | GPUBufferUsage.TRANSFER_DST }); |
| buffer.setSubData(indexBufferOffset, indexArray.buffer); |
| |
| return buffer; |
| } |
| |
| function createInputStateDescriptor() { |
| return { |
| indexFormat: "uint32", |
| attributes: [{ |
| shaderLocation: 0, |
| inputSlot: 0, |
| format: "float4" |
| }, { |
| shaderLocation: 1, |
| inputSlot: 0, |
| offset: 4 * 4, |
| format: "float" |
| }], |
| inputs: [{ |
| inputSlot: 0, |
| stride: 4 * 5 |
| }] |
| }; |
| } |
| |
| async function test() { |
| const device = await getBasicDevice(); |
| const canvas = document.querySelector("canvas"); |
| const swapChain = createBasicSwapChain(canvas, device); |
| // FIXME: Replace with non-MSL shaders. |
| const shaderModule = device.createShaderModule({ code: shaderCode }); |
| const vertexBuffer = createVertexBuffer(device); |
| const indexBuffer = createIndexBuffer(device); |
| const pipeline = createBasicPipeline(shaderModule, device, null, null, createInputStateDescriptor(), null, "triangle-list"); |
| const commandEncoder = device.createCommandEncoder(); |
| const passEncoder = beginBasicRenderPass(swapChain, commandEncoder); |
| |
| passEncoder.setIndexBuffer(indexBuffer, indexBufferOffset); |
| passEncoder.setVertexBuffers(0, [vertexBuffer], [0]); |
| passEncoder.setPipeline(pipeline); |
| passEncoder.drawIndexed(6, 1, 0, indexOffset, 0); |
| passEncoder.endPass(); |
| |
| device.getQueue().submit([commandEncoder.finish()]); |
| vertexBuffer.destroy(); |
| |
| if (window.testRunner) |
| testRunner.notifyDone(); |
| } |
| |
| test(); |
| </script> |