Skip to content

Instantly share code, notes, and snippets.

@JusSn
Last active January 22, 2019 00:56
Show Gist options
  • Save JusSn/46814680aeec5a007537970a6d47a050 to your computer and use it in GitHub Desktop.
Save JusSn/46814680aeec5a007537970a6d47a050 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=600">
<title>WebGPU Triangles demo</title>
<script src="gl-matrix-min.js"></script>
</head>
<body>
<canvas height=1000 width=1000></canvas>
<script>
const shader = `
#include <metal_stdlib>
using namespace metal;
struct VertexIn
{
float4 position [[attribute(0)]];
};
struct VertexOut
{
float4 position [[position]];
float4 color;
};
struct Uniform
{
device float4x4* rotation [[id(0)]];
device float4* color [[id(1)]];
};
vertex VertexOut vertex_main(VertexIn vertexIn [[stage_in]],
const device Uniform& uniforms [[buffer(1)]])
{
VertexOut vertexOut;
vertexOut.position = uniforms.rotation[0] * vertexIn.position;
vertexOut.color = uniforms.color[0];
return vertexOut;
}
fragment float4 fragment_main(VertexOut v [[stage_in]])
{
return v.color;
}
`
const float4Size = 4 * 4;
const verticesSize = float4Size * 3;
function populateVerticesArray(array) {
// float4 position
array[0] = -0.8;
array[1] = -0.8;
array[2] = 1;
array[3] = 1;
array[4] = 0;
array[5] = 0.8;
array[6] = 1;
array[7] = 1;
array[8] = 0.8;
array[9] = -0.8;
array[10] = 1;
array[11] = 1;
}
const colorsSize = float4Size;
function populateColorArray(array) {
// float4 rgba
array[0] = 0;
array[1] = 0;
array[2] = 1;
array[3] = 1;
}
const transformSize = float4Size * 16;
function populateTransformArray(array) {
const now = Date.now() / 1000;
const matrix = mat4.create();
mat4.fromZRotation(matrix, now % (2 * Math.PI));
for (let i = 0; i < 16; i++) {
array[i] = matrix[i];
}
}
let device, context, verticesBuffer, mappedTransformArray, uniformsBindGroup, pipeline, renderPassDescriptor, queue;
async function init() {
const adapter = await webgpu.requestAdapter({});
device = adapter.createDevice();
const canvas = document.querySelector('canvas');
context = canvas.getContext('webgpu');
context.configure({ device: device, format:"B8G8R8A8Unorm", width: canvas.width, height: canvas.height });
// WebKit WebGPU accepts only MSL for now.
const shaderModule = device.createShaderModule({ code: shader });
// Buffer mapping model TBD.
verticesBuffer = device.createBuffer({ size: verticesSize, usage: WebGPUBufferUsage.VERTEX });
const mappedArray = new Float32Array(verticesBuffer.mapping);
populateVerticesArray(mappedArray);
// Input state
const vertexAttributeDescriptor = {
shaderLocation: 0, // Not used
inputSlot: 0, // Used as vertex buffer index in Metal.
offset: 0,
format: WebGPUVertexFormat.FLOAT_R32_G32_B32_A32
};
const vertexInputDescriptor = {
stride: float4Size,
stepMode: WebGPUInputStepMode.VERTEX
};
const inputStateDescriptor = {
indexFormat: WebGPUIndexFormat.UINT32,
attributes: [vertexAttributeDescriptor],
inputs: [vertexInputDescriptor]
};
// Bind group buffer bindings
const transformBuffer = device.createBuffer({ size: transformSize, usage:WebGPUBufferUsage.UNIFORM });
mappedTransformArray = new Float32Array(transformBuffer.mapping);
const transformBufferBindGroupLayoutBinding = {
binding: 0,
visibility: WebGPUShaderStageBit.VERTEX,
type: "uniformBuffer"
};
const colorsBuffer = device.createBuffer({ size: colorsSize, usage: WebGPUBufferUsage.UNIFORM });
const mappedColorArray = new Float32Array(colorsBuffer.mapping);
populateColorArray(mappedColorArray);
const colorBufferBindGroupLayoutBinding = {
binding: 1,
visibility: WebGPUShaderStageBit.VERTEX,
type: "uniformBuffer"
};
const uniformsBindGroupLayoutDescriptor = {
bindings: [transformBufferBindGroupLayoutBinding, colorBufferBindGroupLayoutBinding]
};
const uniformsBindGroupLayout = device.createBindGroupLayout(uniformsBindGroupLayoutDescriptor);
const transformBufferBinding = {
buffer: transformBuffer,
offset: 0,
size: transformSize
};
const transformBufferBindGroupBinding = {
binding: 0,
resource: transformBufferBinding
};
const colorBufferBinding = {
buffer: colorsBuffer,
offset: 0,
size: colorsSize
};
const colorBufferBindGroupBinding = {
binding: 1,
resource: colorBufferBinding
};
const uniformsBindGroupDescriptor = {
layout: uniformsBindGroupLayout,
bindings: [transformBufferBindGroupBinding, colorBufferBindGroupBinding]
};
uniformsBindGroup = device.createBindGroup(uniformsBindGroupDescriptor);
// Pipeline
const pipelineLayoutDescriptor = { bindGroupLayouts: [uniformsBindGroupLayout] };
const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
const vertexStageDescriptor = {
module: shaderModule,
entryPoint: "vertex_main"
};
const fragmentStageDescriptor = {
module: shaderModule,
entryPoint: "fragment_main"
};
const pipelineDescriptor = {
layout: pipelineLayout,
vertexStage: vertexStageDescriptor,
fragmentStage: fragmentStageDescriptor,
primitiveTopology: "triangleList",
inputState: inputStateDescriptor
};
pipeline = device.createRenderPipeline(pipelineDescriptor);
let colorAttachment = { clearColor: { r: 0.5, g: 1.0, b: 1.0, a: 1.0 } };
renderPassDescriptor = { colorAttachments: [colorAttachment] };
queue = device.getQueue();
render();
}
function render() {
populateTransformArray(mappedTransformArray);
const commandBuffer = device.createCommandBuffer();
renderPassDescriptor.colorAttachments[0].attachment = context.getNextTexture().createDefaultTextureView();
const passEncoder = commandBuffer.beginRenderPass(renderPassDescriptor);
// Encode drawing commands.
passEncoder.setPipeline(pipeline);
// Vertex attributes
passEncoder.setVertexBuffers(0, [verticesBuffer], [0]);
// Bind groups
passEncoder.setBindGroup(1, uniformsBindGroup);
passEncoder.draw(3, 1, 0, 0);
const endCommandBuffer = passEncoder.endPass();
queue.submit([endCommandBuffer]);
context.present();
requestAnimationFrame(render);
}
init();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment