blob: abaaa2ad2d7a455b642c7bd52ea1055bf3fb6fe0 [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-command-buffer-races-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
{
float2 xy [[attribute(0)]];
float3 rgb [[attribute(1)]];
};
struct VertexOut
{
float4 position [[position]];
float4 color;
};
vertex VertexOut vertex_main(VertexIn vertexIn [[stage_in]])
{
VertexOut vOut;
vOut.position = float4(vertexIn.xy, 0, 1);
vOut.color = float4(vertexIn.rgb, 1);
return vOut;
}
fragment float4 fragment_main(VertexOut v [[stage_in]])
{
return v.color;
}
`
function createVertexInputDescriptor() {
return {
indexFormat: "uint32",
vertexBuffers: [{
stride: 4 * 2,
attributeSet: [{
format: "float2",
shaderLocation: 0
}]
}, {
stride: 4 * 3,
stepMode: "instance",
attributeSet: [{
format: "float3",
shaderLocation: 1
}]
}]
}
}
function createAndSetVertexBuffer(device, vertices) {
const vertexArray = new Float32Array(vertices)
return createBufferWithData(device, { size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX }, vertexArray.buffer);
}
function drawAndSubmitCommands(device, pipeline, attachment, vertexBuffer, colorBuffer) {
const commandEncoder = device.createCommandEncoder();
const encoder = commandEncoder.beginRenderPass({ colorAttachments: [attachment] });
encoder.setVertexBuffers(0, [vertexBuffer, colorBuffer], [0, 0]);
encoder.setPipeline(pipeline);
encoder.draw(3, 1, 0, 0);
encoder.endPass();
device.getQueue().submit([commandEncoder.finish()]);
}
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 vertexInputDescriptor = createVertexInputDescriptor();
const pipeline = createBasicPipeline(shaderModule, device, null, null, vertexInputDescriptor);
const upperLeftBuffer = createAndSetVertexBuffer(device, [-1, 1, -1, -1, 0, 1]);
const middleBuffer = createAndSetVertexBuffer(device, [0, 1, -1, -1, 1, -1]);
const upperRightBuffer = createAndSetVertexBuffer(device, [0, 1, 1, 1, 1, -1]);
const green = [0, 1, 0];
const blue = [0, 0, 1];
const greenArray = new Float32Array(green);
const blueArray = new Float32Array(blue);
const colorBuffer = createBufferWithData(device, { size: greenArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE }, greenArray.buffer);
const attachment = {
attachment: swapChain.getCurrentTexture().createDefaultView(),
loadOp: "load",
storeOp: "store",
clearColor: { r: 1, g: 0, b: 0, a: 1 }
};
/* mapWriteAsync should resolve after GPU commands are complete, so triangle should be green. */
drawAndSubmitCommands(device, pipeline, attachment, upperLeftBuffer, colorBuffer);
await colorBuffer.mapWriteAsync().then(ab => {
let array = new Float32Array(ab);
array.set(blue);
colorBuffer.unmap();
});
await colorBuffer.mapWriteAsync().then(ab => {
let array = new Float32Array(ab);
array.set(green);
});
/* colorBuffer that is still mapped should be not submitted to draw a blue triangle. */
drawAndSubmitCommands(device, pipeline, attachment, upperLeftBuffer, colorBuffer);
/* colorBuffer does not actually contain "green" again until this call. */
colorBuffer.unmap();
/* Writing data immediately after a submit should not affect the preceding draw call. */
drawAndSubmitCommands(device, pipeline, attachment, middleBuffer, colorBuffer);
await mapWriteDataToBuffer(colorBuffer, blueArray.buffer);
/* destroy right after a submit should not affect the draw call. */
await mapWriteDataToBuffer(colorBuffer, greenArray.buffer);
drawAndSubmitCommands(device, pipeline, attachment, upperRightBuffer, colorBuffer);
upperRightBuffer.destroy();
/* draw command with a destroyed buffer should fail */
colorBuffer.destroy();
drawAndSubmitCommands(device, pipeline, attachment, middleBuffer, colorBuffer);
upperLeftBuffer.destroy();
middleBuffer.destroy();
upperRightBuffer.destroy();
}
test().then(function() {
if (window.testRunner)
testRunner.notifyDone();
}, function() {
if (window.testRunner)
testRunner.notifyDone();
});
</script>