Skip to content

Commit 89dd2b1

Browse files
authored
Add a basic WebGPU example (shader-slang#5923)
* Add a basic WebGPU example This helps to address shader-slang#5656. * Use serial await rather than Promise.all
1 parent b2d51ad commit 89dd2b1

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed

examples/wgpu-html5/README.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Simple WebGPU example
2+
3+
## Description
4+
5+
This is a simple example showing how WebGPU and Slang can be used together.
6+
The resulting application shows a green triangle rendered on a black background.
7+
8+
More serious applications are adviced to make use of Slang's reflection API.
9+
10+
## Instructions
11+
12+
Get `slangc` from https://github.com/shader-slang/slang/releases/latest, or build it using the instructions under `docs/building.md`, and make sure that `slangc` is in [the `PATH` of your shell](https://en.wikipedia.org/wiki/PATH_(variable)).
13+
14+
Compile the Slang shaders `shader.slang` into WGSL shaders named `shader.vertex.wgsl` and `shader.fragment.wgsl`:
15+
16+
$ slangc -target wgsl -stage vertex -entry vertexMain -o shader.vertex.wgsl shader.slang
17+
$ slangc -target wgsl -stage fragment -entry fragmentMain -o shader.fragment.wgsl shader.slang
18+
19+
Alternatively, you can run `build.py` which does the same thing.
20+
21+
Start a web server, for example by running the following command in this directory:
22+
23+
$ python -m http.server
24+
25+
Finally, visit `http://localhost:8000/` to see the application running in your browser.

examples/wgpu-html5/build.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python3
2+
3+
import subprocess
4+
import sys
5+
6+
for args in [['slangc',
7+
'-target', 'wgsl',
8+
'-stage', '{}'.format(stage),
9+
'-entry', '{}Main'.format(stage),
10+
'-o', 'shader.{}.wgsl'.format(stage),
11+
'shader.slang']
12+
for stage in ['vertex', 'fragment']]:
13+
print("Running '{}'...".format(' '.join(args)))
14+
result = subprocess.run(args)
15+
if result.returncode != 0:
16+
print('Failed!')
17+
sys.exit(1)
18+
else:
19+
print('Succeeded!')

examples/wgpu-html5/example.js

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"use strict";
2+
3+
let Example = {
4+
initialize: async function (canvas) {
5+
async function render(shaders) {
6+
if (!navigator.gpu) {
7+
throw new Error("WebGPU not supported on this browser.");
8+
}
9+
const adapter = await navigator.gpu.requestAdapter();
10+
if (!adapter) {
11+
throw new Error("No appropriate GPUAdapter found.");
12+
}
13+
const device = await adapter.requestDevice();
14+
const context = canvas.getContext("webgpu");
15+
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
16+
context.configure({
17+
device: device,
18+
format: canvasFormat,
19+
});
20+
21+
const vertexBufferLayout = {
22+
arrayStride: 8,
23+
attributes: [{
24+
format: "float32x2",
25+
offset: 0,
26+
shaderLocation: 0,
27+
}],
28+
};
29+
30+
const pipeline = device.createRenderPipeline({
31+
label: "Pipeline",
32+
layout: "auto",
33+
vertex: {
34+
module: device.createShaderModule({
35+
label: "Vertex shader module",
36+
code: shaders.vertex
37+
}),
38+
entryPoint: "vertexMain",
39+
buffers: [vertexBufferLayout]
40+
},
41+
fragment: {
42+
module: device.createShaderModule({
43+
label: "Fragment shader module",
44+
code: shaders.fragment
45+
}),
46+
entryPoint: "fragmentMain",
47+
targets: [{
48+
format: canvasFormat
49+
}]
50+
}
51+
});
52+
53+
const vertices = new Float32Array([
54+
0.0, -0.8,
55+
+0.8, +0.8,
56+
-0.8, +0.8,
57+
]);
58+
const vertexBuffer = device.createBuffer({
59+
label: "Triangle vertices",
60+
size: vertices.byteLength,
61+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
62+
});
63+
const bufferOffset = 0;
64+
device.queue.writeBuffer(vertexBuffer, bufferOffset, vertices);
65+
66+
const encoder = device.createCommandEncoder();
67+
const pass = encoder.beginRenderPass({
68+
colorAttachments: [{
69+
view: context.getCurrentTexture().createView(),
70+
loadOp: "clear",
71+
clearValue: { r: 0, g: 0, b: 0, a: 1 },
72+
storeOp: "store",
73+
}]
74+
});
75+
pass.setPipeline(pipeline);
76+
const vertexBufferSlot = 0;
77+
pass.setVertexBuffer(vertexBufferSlot, vertexBuffer);
78+
pass.draw(vertices.length / 2);
79+
pass.end();
80+
const commandBuffer = encoder.finish();
81+
device.queue.submit([commandBuffer]);
82+
}
83+
84+
render({
85+
vertex: await fetch("shader.vertex.wgsl").then(r => r.text()),
86+
fragment: await fetch("shader.fragment.wgsl").then(r => r.text()),
87+
});
88+
}
89+
}

examples/wgpu-html5/index.html

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<html>
2+
<head>
3+
<title>WebGPU Triangle</title>
4+
<script src="example.js"></script>
5+
</head>
6+
<body>
7+
<center>
8+
<canvas width="512" height="512"></canvas>
9+
</center>
10+
<script type="text/javascript">
11+
const canvas = document.querySelector("canvas");
12+
Example.initialize(canvas);
13+
</script>
14+
</body>
15+
</html>

examples/wgpu-html5/shader.slang

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
struct VertexStageInput
2+
{
3+
float4 position : POSITION0;
4+
};
5+
6+
struct VertexStageOutput
7+
{
8+
float4 positionClipSpace : SV_POSITION;
9+
};
10+
11+
struct FragmentStageOutput
12+
{
13+
float4 color : SV_TARGET;
14+
};
15+
16+
VertexStageOutput vertexMain(VertexStageInput input) : SV_Position
17+
{
18+
VertexStageOutput output;
19+
output.positionClipSpace = float4(input.position.xy, 1);
20+
return output;
21+
}
22+
23+
FragmentStageOutput fragmentMain() : SV_Target
24+
{
25+
FragmentStageOutput output;
26+
output.color = float4(0, 1, 0, 1);
27+
return output;
28+
}

0 commit comments

Comments
 (0)