blob: 2bff4a93fa857d82c2aef4ff2ed126b0fb287faf [file] [log] [blame]
<!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="buffer-resource-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 VertexInput {
float4 position [[attribute(0)]];
};
struct Vertex {
float4 position [[position]];
};
struct VertexArguments {
device Vertex* v0;
device Vertex* v1;
device Vertex* v2;
};
vertex Vertex vertex_main(
VertexInput input [[stage_in]],
const device VertexArguments& args0 [[buffer(0)]],
const device VertexArguments& args1 [[buffer(1)]],
uint vid [[vertex_id]])
{
switch (vid)
{
case 0:
case 1:
case 2: {
Vertex out;
out.position = input.position;
return out;
}
case 3: return *args0.v0;
case 4: return *args0.v1;
case 5: return *args0.v2;
case 6: return *args1.v0;
case 7: return *args1.v1;
default: return *args1.v2;
}
}
struct FragmentArguments {
device float4* color;
};
fragment float4 fragment_main(const device FragmentArguments& args [[buffer(0)]])
{
return args.color[0];
}
`
const bindingNums = {
UL: 0,
UM: 1,
UR: 2,
LL: 3,
LR: 4,
G: 5
};
function createUniformBufferBindGroupLayout(bindNum, stage = GPUShaderStageBit.VERTEX) {
return {
binding: bindNum,
visibility: stage,
type: "uniform-buffer"
};
}
const vertexSize = 4 * 4;
const verticesBufferSize = vertexSize * 3;
function createAndUploadVerticesBuffer(device) {
const buffer = device.createBuffer({ size:verticesBufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
const arrayBuffer = new Float32Array([
0, 1, 0, 1,
-1, -1, 0, 1,
1, -1, 0, 1
]).buffer;
buffer.setSubData(0, arrayBuffer);
return buffer;
}
function createFloat4Buffer(device, a, b, promises) {
const buffer = device.createBuffer({ size: vertexSize, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE });
const promise = buffer.mapWriteAsync().then(mapping => {
const mappedArray = new Float32Array(mapping);
mappedArray.set([a, b, 0, 1]);
buffer.unmap();
});
promises.push(promise);
return buffer;
}
function createBufferBinding(buffer) {
return { buffer: buffer, size: vertexSize };
}
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 });
// Create vertex data GPUBuffers.
const verticesBuffer = createAndUploadVerticesBuffer(device);
let bufferPromises = [];
const upperLeft = createFloat4Buffer(device, -1, 1, bufferPromises);
const upperMiddle = createFloat4Buffer(device, 0, 1, bufferPromises);
const upperRight = createFloat4Buffer(device, 1, 1, bufferPromises);
const lowerLeft = createFloat4Buffer(device, -1, -1, bufferPromises);
const lowerRight = createFloat4Buffer(device, 1, -1, bufferPromises);
// Color data buffer.
const green = createFloat4Buffer(device, 0, 1, bufferPromises);
// Create vertex input state.
const inputState = {
indexFormat: "uint32",
attributes: [{
shaderLocation: 0,
inputSlot: 0,
format: "float4"
}],
inputs: [{
inputSlot: 0,
stride: vertexSize
}]
};
// Create buffer GPUBindGroupLayoutBindings.
const layoutUL = createUniformBufferBindGroupLayout(bindingNums.UL);
const layoutUM = createUniformBufferBindGroupLayout(bindingNums.UM);
const layoutUR = createUniformBufferBindGroupLayout(bindingNums.UR);
const layoutLL = createUniformBufferBindGroupLayout(bindingNums.LL);
const layoutLR = createUniformBufferBindGroupLayout(bindingNums.LR);
const layoutG = createUniformBufferBindGroupLayout(bindingNums.G, GPUShaderStageBit.FRAGMENT);
// GPUBindGroupLayouts
const leftTriangleBGLayout = device.createBindGroupLayout({ bindings: [layoutUL, layoutUM, layoutLL, layoutG] });
const rightTriangleBGLayout = device.createBindGroupLayout({ bindings: [layoutUR, layoutUM, layoutLR] });
// GPUPipelineLayout and GPURenderPipeline
const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [leftTriangleBGLayout, rightTriangleBGLayout] });
const pipeline = createBasicPipeline(shaderModule, device, null, pipelineLayout, inputState, null, "triangle-list");
// GPUBufferBindings
const bindingUL = createBufferBinding(upperLeft);
const bindingUM = createBufferBinding(upperMiddle);
const bindingUR = createBufferBinding(upperRight);
const bindingLL = createBufferBinding(lowerLeft);
const bindingLR = createBufferBinding(lowerRight);
const bindingG = createBufferBinding(green);
// GPUBindGroupBindings
const bgBindingUL = { binding: bindingNums.UL, resource: bindingUL };
const bgBindingUM = { binding: bindingNums.UM, resource: bindingUM };
const bgBindingUR = { binding: bindingNums.UR, resource: bindingUR };
const bgBindingLL = { binding: bindingNums.LL, resource: bindingLL };
const bgBindingLR = { binding: bindingNums.LR, resource: bindingLR };
const bgBindingG = { binding: bindingNums.G, resource: bindingG };
// GPUBindGroups
const leftTriangleBG = device.createBindGroup({
layout: leftTriangleBGLayout,
bindings: [bgBindingUL, bgBindingUM, bgBindingLL, bgBindingG]
});
const rightTriangleBG = device.createBindGroup({
layout: rightTriangleBGLayout,
bindings: [bgBindingUR, bgBindingUM, bgBindingLR]
});
Promise.all(bufferPromises).then(() => {
const commandEncoder = device.createCommandEncoder();
const passEncoder = beginBasicRenderPass(swapChain, commandEncoder);
passEncoder.setPipeline(pipeline);
// Vertex data for upper triangles.
passEncoder.setBindGroup(0, leftTriangleBG);
passEncoder.setBindGroup(1, rightTriangleBG);
// Lower triangle.
passEncoder.setVertexBuffers(0, [verticesBuffer], [0]);
passEncoder.draw(9, 1, 0, 0);
passEncoder.endPass();
const queue = device.getQueue();
queue.submit([commandEncoder.finish()]);
if (window.testRunner)
testRunner.notifyDone();
});
}
test();
</script>