summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorAnders Leino <aleino@nvidia.com>2024-12-30 11:42:25 +0200
committerGitHub <noreply@github.com>2024-12-30 11:42:25 +0200
commit89dd2b1278f9e457d9d343742a51de27502ebca1 (patch)
tree22899e27c4131d86f395d2c501d92a694eb6a137 /examples
parentb2d51adc4b433c44298d35bb19637496c99ce44b (diff)
Add a basic WebGPU example (#5923)
* Add a basic WebGPU example This helps to address #5656. * Use serial await rather than Promise.all
Diffstat (limited to 'examples')
-rw-r--r--examples/wgpu-html5/README.md25
-rwxr-xr-xexamples/wgpu-html5/build.py19
-rw-r--r--examples/wgpu-html5/example.js89
-rw-r--r--examples/wgpu-html5/index.html15
-rw-r--r--examples/wgpu-html5/shader.slang28
5 files changed, 176 insertions, 0 deletions
diff --git a/examples/wgpu-html5/README.md b/examples/wgpu-html5/README.md
new file mode 100644
index 000000000..f0bcc5fa5
--- /dev/null
+++ b/examples/wgpu-html5/README.md
@@ -0,0 +1,25 @@
+# Simple WebGPU example
+
+## Description
+
+This is a simple example showing how WebGPU and Slang can be used together.
+The resulting application shows a green triangle rendered on a black background.
+
+More serious applications are adviced to make use of Slang's reflection API.
+
+## Instructions
+
+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)).
+
+Compile the Slang shaders `shader.slang` into WGSL shaders named `shader.vertex.wgsl` and `shader.fragment.wgsl`:
+
+ $ slangc -target wgsl -stage vertex -entry vertexMain -o shader.vertex.wgsl shader.slang
+ $ slangc -target wgsl -stage fragment -entry fragmentMain -o shader.fragment.wgsl shader.slang
+
+Alternatively, you can run `build.py` which does the same thing.
+
+Start a web server, for example by running the following command in this directory:
+
+ $ python -m http.server
+
+Finally, visit `http://localhost:8000/` to see the application running in your browser. \ No newline at end of file
diff --git a/examples/wgpu-html5/build.py b/examples/wgpu-html5/build.py
new file mode 100755
index 000000000..2b23dadab
--- /dev/null
+++ b/examples/wgpu-html5/build.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+
+import subprocess
+import sys
+
+for args in [['slangc',
+ '-target', 'wgsl',
+ '-stage', '{}'.format(stage),
+ '-entry', '{}Main'.format(stage),
+ '-o', 'shader.{}.wgsl'.format(stage),
+ 'shader.slang']
+ for stage in ['vertex', 'fragment']]:
+ print("Running '{}'...".format(' '.join(args)))
+ result = subprocess.run(args)
+ if result.returncode != 0:
+ print('Failed!')
+ sys.exit(1)
+ else:
+ print('Succeeded!')
diff --git a/examples/wgpu-html5/example.js b/examples/wgpu-html5/example.js
new file mode 100644
index 000000000..c9d9552fd
--- /dev/null
+++ b/examples/wgpu-html5/example.js
@@ -0,0 +1,89 @@
+"use strict";
+
+let Example = {
+ initialize: async function (canvas) {
+ async function render(shaders) {
+ if (!navigator.gpu) {
+ throw new Error("WebGPU not supported on this browser.");
+ }
+ const adapter = await navigator.gpu.requestAdapter();
+ if (!adapter) {
+ throw new Error("No appropriate GPUAdapter found.");
+ }
+ const device = await adapter.requestDevice();
+ const context = canvas.getContext("webgpu");
+ const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
+ context.configure({
+ device: device,
+ format: canvasFormat,
+ });
+
+ const vertexBufferLayout = {
+ arrayStride: 8,
+ attributes: [{
+ format: "float32x2",
+ offset: 0,
+ shaderLocation: 0,
+ }],
+ };
+
+ const pipeline = device.createRenderPipeline({
+ label: "Pipeline",
+ layout: "auto",
+ vertex: {
+ module: device.createShaderModule({
+ label: "Vertex shader module",
+ code: shaders.vertex
+ }),
+ entryPoint: "vertexMain",
+ buffers: [vertexBufferLayout]
+ },
+ fragment: {
+ module: device.createShaderModule({
+ label: "Fragment shader module",
+ code: shaders.fragment
+ }),
+ entryPoint: "fragmentMain",
+ targets: [{
+ format: canvasFormat
+ }]
+ }
+ });
+
+ const vertices = new Float32Array([
+ 0.0, -0.8,
+ +0.8, +0.8,
+ -0.8, +0.8,
+ ]);
+ const vertexBuffer = device.createBuffer({
+ label: "Triangle vertices",
+ size: vertices.byteLength,
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
+ });
+ const bufferOffset = 0;
+ device.queue.writeBuffer(vertexBuffer, bufferOffset, vertices);
+
+ const encoder = device.createCommandEncoder();
+ const pass = encoder.beginRenderPass({
+ colorAttachments: [{
+ view: context.getCurrentTexture().createView(),
+ loadOp: "clear",
+ clearValue: { r: 0, g: 0, b: 0, a: 1 },
+ storeOp: "store",
+ }]
+ });
+ pass.setPipeline(pipeline);
+ const vertexBufferSlot = 0;
+ pass.setVertexBuffer(vertexBufferSlot, vertexBuffer);
+ pass.draw(vertices.length / 2);
+ pass.end();
+ const commandBuffer = encoder.finish();
+ device.queue.submit([commandBuffer]);
+ }
+
+ render({
+ vertex: await fetch("shader.vertex.wgsl").then(r => r.text()),
+ fragment: await fetch("shader.fragment.wgsl").then(r => r.text()),
+ });
+ }
+}
diff --git a/examples/wgpu-html5/index.html b/examples/wgpu-html5/index.html
new file mode 100644
index 000000000..975a8c65f
--- /dev/null
+++ b/examples/wgpu-html5/index.html
@@ -0,0 +1,15 @@
+<html>
+ <head>
+ <title>WebGPU Triangle</title>
+ <script src="example.js"></script>
+ </head>
+ <body>
+ <center>
+ <canvas width="512" height="512"></canvas>
+ </center>
+ <script type="text/javascript">
+ const canvas = document.querySelector("canvas");
+ Example.initialize(canvas);
+ </script>
+ </body>
+</html>
diff --git a/examples/wgpu-html5/shader.slang b/examples/wgpu-html5/shader.slang
new file mode 100644
index 000000000..0721a1fff
--- /dev/null
+++ b/examples/wgpu-html5/shader.slang
@@ -0,0 +1,28 @@
+struct VertexStageInput
+{
+ float4 position : POSITION0;
+};
+
+struct VertexStageOutput
+{
+ float4 positionClipSpace : SV_POSITION;
+};
+
+struct FragmentStageOutput
+{
+ float4 color : SV_TARGET;
+};
+
+VertexStageOutput vertexMain(VertexStageInput input) : SV_Position
+{
+ VertexStageOutput output;
+ output.positionClipSpace = float4(input.position.xy, 1);
+ return output;
+}
+
+FragmentStageOutput fragmentMain() : SV_Target
+{
+ FragmentStageOutput output;
+ output.color = float4(0, 1, 0, 1);
+ return output;
+}