Last active
December 11, 2018 01:54
-
-
Save grorg/5cbb17ba9a1e1b78f3a6a54aaffb03c4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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="simple-triangle-strip-expected.html"> | |
<p>Pass if square canvas below is completely green.</p> | |
<canvas width="400" height="400"></canvas> | |
<script> | |
const shaderCode = ` | |
#include <metal_stdlib> | |
using namespace metal; | |
struct Vertex | |
{ | |
float4 position [[position]]; | |
}; | |
vertex Vertex vertex_main(const Vertex& vertex [[buffer(0)]) | |
{ | |
Vertex v; | |
// switch (vid) { | |
// case 0: | |
// v.position = float4(-1, 1, 0, 1); | |
// break; | |
// case 1: | |
// v.position = float4(-1, -1, 0, 1); | |
// break; | |
// case 2: | |
// v.position = float4(1, 1, 0, 1); | |
// break; | |
// default: | |
// v.position = float4(1, -1, 0, 1); | |
// } | |
v.position = vertex.position; | |
return v; | |
} | |
fragment float4 fragment_main(Vertex vertexIn [[stage_in]]) | |
{ | |
return float4(0.0, 1.0, 0.0, 1.0); | |
} | |
` | |
async function getBasicDevice() { | |
// FIXME: requestAdapter should take a WebGPUAdapterDescriptor. | |
const adapter = await window.webgpu.requestAdapter({}); | |
const device = adapter.createDevice(); | |
return device; | |
} | |
function createBasicContext(canvas, device) { | |
const context = canvas.getContext("webgpu"); | |
// FIXME: Implement and specify a WebGPUTextureUsageEnum. | |
context.configure({ device: device, format:"B8G8R8A8Unorm", width: canvas.width, height: canvas.height }); | |
return context; | |
} | |
function createBasicPipeline(shaderModule, device, inputState) { | |
vertexStageDescriptor = { | |
module: shaderModule, | |
stage: WebGPUShaderStage.VERTEX, | |
entryPoint: "vertex_main" | |
}; | |
fragmentStageDescriptor = { | |
module: shaderModule, | |
stage: WebGPUShaderStage.FRAGMENT, | |
entryPoint: "fragment_main" | |
}; | |
pipelineDescriptor = { | |
stages: [vertexStageDescriptor, fragmentStageDescriptor], | |
primitiveTopology: "triangleStrip", | |
inputState | |
}; | |
return device.createRenderPipeline(pipelineDescriptor); | |
} | |
function beginBasicRenderPass(context, commandBuffer) { | |
const basicAttachment = { | |
attachment: context.getNextTexture().createDefaultTextureView(), | |
clearColor: { r: 1.0, g: 0, b: 0, a: 1.0 } | |
} | |
// FIXME: Flesh out the rest of WebGPURenderPassDescriptor. | |
return commandBuffer.beginRenderPass({ colorAttachments : [basicAttachment] }); | |
} | |
function encodeBasicCommands(renderPassEncoder, renderPipeline, buffer) { | |
renderPassEncoder.setVertexBuffers(0, [buffer], [0]); | |
renderPassEncoder.setPipeline(renderPipeline); | |
renderPassEncoder.draw(4, 1, 0, 0); | |
return renderPassEncoder.endPass(); | |
} | |
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 }); | |
const size = 4 * 4 * 4; | |
const buffer = device.createBuffer({ size, usage: WebGPUBufferUsage.MAP_WRITE }); | |
// mapWriteAsync returns a then-able object, which hopefully | |
// should be enough for it to operate as a Promise. | |
// I'm not sure this will actually work. | |
const mappedMemory = await buffer.mapWriteAsync(0, size); | |
ASSERT(!mappedMemory.isPending()) | |
// This will not be called getPointer(), but it is at the moment. | |
const floatArray = new Float32Array(mappedMemory.getPointer()); | |
ASSERT(floatArray.length == 16); | |
floatArray[0] = -1; | |
floatArray[1] = 1; | |
... | |
floatArray[15] = 1; | |
// We're done writing to the buffer. | |
buffer.unmap(); | |
const inputStateDescriptor = { | |
attributes: [ | |
{ | |
shaderLocation: 0, | |
// inputSlot used to go here, but it will now just be the index | |
offset: 0, | |
format: WebGPUVertexFormat.FLOAT_R32_G32_B32_A32 // I don't think this matters here. | |
} | |
], | |
inputs: [ | |
{ | |
// inputSlot - not needed any more. | |
stride: 4 * 4, | |
stepMode: WebGPUInputStepMode.VERTEX | |
} | |
] | |
}; | |
const pipeline = createBasicPipeline(shaderModule, device, inputStateDescriptor); | |
const commandBuffer = device.createCommandBuffer(); | |
const passEncoder = beginBasicRenderPass(context, commandBuffer); | |
const endCommandBuffer = encodeBasicCommands(passEncoder, pipeline, buffer); | |
const queue = device.getQueue(); | |
queue.submit([endCommandBuffer]); | |
context.present(); | |
} | |
test(); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment