blob: d7115ba6cba2c7850a3be7b9629dd8381c4bc658 [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>
const shaderCode = `
#include <metal_stdlib>
using namespace metal;
struct Vertex {
float4 position [[position]];
};
struct VertexArguments {
device Vertex* v0;
device Vertex* v1;
device Vertex* v2;
};
vertex Vertex vertex_main(
const device VertexArguments& args0 [[buffer(0)]],
const device VertexArguments& args1 [[buffer(1)]],
uint vid [[vertex_id]])
{
switch (vid)
{
case 0: return args0.v0[0];
case 1: return args0.v1[0];
case 2: return args0.v2[0];
case 3: return args1.v0[0];
case 4: return args1.v1[0];
default: return args1.v2[0];
}
}
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 = WebGPUShaderStageBit.VERTEX) {
return {
binding: bindNum,
visibility: stage,
type: "uniformBuffer"
};
}
const bufferSize = 4 * 4;
// FIXME: Keep up to date with buffer upload decisions.
function createFloat4Buffer(device, a, b) {
const buffer = device.createBuffer({ size: bufferSize, usage: WebGPUBufferUsage.UNIFORM });
const arrayBuffer = buffer.mapping;
const floatArray = new Float32Array(arrayBuffer);
floatArray[0] = a;
floatArray[1] = b;
floatArray[2] = 0;
floatArray[3] = 1;
return buffer;
}
function createBufferBinding(buffer) {
return {
buffer: buffer,
offset: 0,
size: bufferSize
};
}
async function test() {
const device = await getBasicDevice();
const canvas = document.querySelector("canvas");
const context = createBasicContext(canvas, device);
// FIXME: Replace with non-MSL shaders.
const shaderModule = device.createShaderModule({ code: shaderCode });
// Create vertex data WebGPUBuffers.
const upperLeft = createFloat4Buffer(device, -1, 1);
const upperMiddle = createFloat4Buffer(device, 0, 1);
const upperRight = createFloat4Buffer(device, 1, 1);
const lowerLeft = createFloat4Buffer(device, -1, -1);
const lowerRight = createFloat4Buffer(device, 1, -1);
// Color data buffer.
const green = createFloat4Buffer(device, 0, 1);
// Create buffer WebGPUBindGroupLayoutBindings.
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, WebGPUShaderStageBit.FRAGMENT);
// WebGPUBindGroupLayouts
const leftTriangleBGLayout = device.createBindGroupLayout({ bindings: [layoutUL, layoutUM, layoutLL, layoutG] });
const rightTriangleBGLayout = device.createBindGroupLayout({ bindings: [layoutUR, layoutUM, layoutLR] });
const middleTriangleBGLayout = device.createBindGroupLayout({ bindings: [layoutUM, layoutLL, layoutLR, layoutG] });
// WebGPUPipelineLayout and WebGPURenderPipeline
const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [leftTriangleBGLayout, middleTriangleBGLayout, rightTriangleBGLayout] });
const pipeline = createBasicPipeline(shaderModule, device, pipelineLayout, null, "triangleList");
// WebGPUBufferBindings
const bindingUL = createBufferBinding(upperLeft);
const bindingUM = createBufferBinding(upperMiddle);
const bindingUR = createBufferBinding(upperRight);
const bindingLL = createBufferBinding(lowerLeft);
const bindingLR = createBufferBinding(lowerRight);
const bindingG = createBufferBinding(green);
// WebGPUBindGroupBindings
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 };
// WebGPUBindGroups
const leftTriangleBG = device.createBindGroup({
layout: leftTriangleBGLayout,
bindings: [bgBindingUL, bgBindingUM, bgBindingLL, bgBindingG]
});
const rightTriangleBG = device.createBindGroup({
layout: rightTriangleBGLayout,
bindings: [bgBindingUR, bgBindingUM, bgBindingLR]
});
const middleTriangleBG = device.createBindGroup({
layout: middleTriangleBGLayout,
bindings: [bgBindingUM, bgBindingLL, bgBindingLR, bgBindingG]
});
const commandBuffer = device.createCommandBuffer();
const passEncoder = beginBasicRenderPass(context, commandBuffer);
passEncoder.setPipeline(pipeline);
// Draw upper triangles.
passEncoder.setBindGroup(0, leftTriangleBG);
passEncoder.setBindGroup(1, rightTriangleBG);
passEncoder.draw(6, 1, 0, 0);
// Draw lower triangle.
passEncoder.setBindGroup(0, middleTriangleBG);
passEncoder.draw(3, 1, 0, 0);
const endCommandBuffer = passEncoder.endPass();
const queue = device.getQueue();
queue.submit([endCommandBuffer]);
context.present();
}
test();
</script>