blob: 3f04aec26fc15564ebbeece5e0af8f425d7e756a [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 createInputStateDescriptor() {
return {
indexFormat: "uint32",
attributes: [{
shaderLocation: 0,
inputSlot: 0,
format: "float2"
}, {
shaderLocation: 1,
inputSlot: 1,
format: "float3"
}],
inputs: [{
inputSlot: 0,
stride: 4 * 2
}, {
inputSlot: 1,
stride: 4 * 3,
stepMode: "instance"
}]
}
}
function createAndSetVertexBuffer(device, vertices) {
const floatArray = new Float32Array(vertices);
const buffer = device.createBuffer({ size: floatArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
buffer.setSubData(0, floatArray.buffer);
return 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 inputStateDescriptor = createInputStateDescriptor();
const pipeline = createBasicPipeline(shaderModule, device, null, null, inputStateDescriptor);
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 = device.createBuffer({ size: greenArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST | GPUBufferUsage.MAP_WRITE });
colorBuffer.setSubData(0, 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();
/* setSubData immediately after a submit should not affect the preceding draw call. */
drawAndSubmitCommands(device, pipeline, attachment, middleBuffer, colorBuffer);
colorBuffer.setSubData(0, blueArray.buffer);
/* destroy right after a submit should not affect the draw call. */
colorBuffer.setSubData(0, 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();
if (window.testRunner)
testRunner.notifyDone();
}
test();
</script>