blob: cb970b74fd52cb2b5b1be2b567f2a38e73c655fb [file]
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=600">
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>WebGPU Hello Triangle</title>
<link rel="stylesheet" href="css/style.css">
<style>
body {
text-align: center;
}
canvas {
margin: 0 auto;
}
</style>
</head>
<body>
<canvas></canvas>
<script>
globalThis.testRunner?.waitUntilDone();
const log = console.debug;
async function helloTriangle() {
if (!navigator.gpu || GPUBufferUsage.COPY_SRC === undefined) {
document.body.className = 'error';
return;
}
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
/*** Vertex Buffer Setup ***/
/* Vertex Data */
const vertexStride = 8 * 4;
const vertexDataSize = vertexStride * 3;
/* GPUBufferDescriptor */
const vertexDataBufferDescriptor = {
size: vertexDataSize,
usage: GPUBufferUsage.VERTEX
};
/* GPUBuffer */
const vertexBuffer = device.createBuffer(vertexDataBufferDescriptor);
/*** Shader Setup ***/
const wgslSource = `
struct Vertex {
@builtin(position) Position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) multiplier: f32,
}
@vertex fn vsmain(@builtin(vertex_index) VertexIndex: u32) -> Vertex
{
var pos: array<vec2<f32>, 3> = array<vec2<f32>, 3>(
vec2<f32>( 0.0, 0.5),
vec2<f32>(-0.5, -0.5),
vec2<f32>( 0.5, -0.5)
);
var vertex_out : Vertex;
vertex_out.Position = vec4<f32>(pos[VertexIndex] * 1.5, 0.0, 1.0);
vertex_out.color = vec4<f32>(1.0, 1.0, 1.0, 1.0) - vec4<f32>(pos[VertexIndex] + vec2<f32>(0.5, 0.5), 0.0, 0.0);
vertex_out.multiplier = f32(VertexIndex * 50);
return vertex_out;
}
@fragment fn fsmain(in: Vertex) -> @location(0) vec4<f32>
{
return in.color * in.multiplier;
}
`;
const shaderModule = device.createShaderModule({ code: wgslSource });
/* GPUPipelineStageDescriptors */
const vertexStageDescriptor = { module: shaderModule, entryPoint: "vsmain" };
const fragmentStageDescriptor = { module: shaderModule, entryPoint: "fsmain", targets: [ {format: "rgba16float" }, ], };
/* GPURenderPipelineDescriptor */
const renderPipelineDescriptor = {
layout: 'auto',
vertex: vertexStageDescriptor,
fragment: fragmentStageDescriptor,
primitive: {topology: "triangle-list" },
};
/* GPURenderPipeline */
const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
/*** Swap Chain Setup ***/
const canvas = document.querySelector("canvas");
canvas.width = 600;
canvas.height = 600;
const gpuContext = canvas.getContext("webgpu");
/* GPUCanvasConfiguration */
const canvasConfiguration = { device: device, format: "rgba16float", toneMapping: { mode: 'extended' } };
gpuContext.configure(canvasConfiguration);
/* GPUTexture */
const currentTexture = gpuContext.getCurrentTexture();
/*** Render Pass Setup ***/
/* Acquire Texture To Render To */
/* GPUTextureView */
const renderAttachment = currentTexture.createView();
/* GPUColor */
const darkBlue = { r: 1, g: 1, b: 1, a: 1 };
/* GPURenderPassColorATtachmentDescriptor */
const colorAttachmentDescriptor = {
view: renderAttachment,
loadOp: "clear",
storeOp: "store",
clearValue: darkBlue
};
/* GPURenderPassDescriptor */
const renderPassDescriptor = { colorAttachments: [colorAttachmentDescriptor] };
/*** Rendering ***/
/* GPUCommandEncoder */
const commandEncoder = device.createCommandEncoder();
/* GPURenderPassEncoder */
const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
renderPassEncoder.setPipeline(renderPipeline);
const vertexBufferSlot = 0;
renderPassEncoder.setVertexBuffer(vertexBufferSlot, vertexBuffer, 0);
renderPassEncoder.draw(3, 1, 0, 0); // 3 vertices, 1 instance, 0th vertex, 0th instance.
renderPassEncoder.end();
/* GPUComamndBuffer */
const commandBuffer = commandEncoder.finish();
/* GPUQueue */
const queue = device.queue;
queue.submit([commandBuffer]);
await queue.onSubmittedWorkDone();
log('Pass');
globalThis.testRunner?.dumpAsText();
globalThis.testRunner?.notifyDone();
}
window.addEventListener("DOMContentLoaded", helloTriangle);
</script>
</body>
</html>