diff options
| author | Anders Leino <aleino@nvidia.com> | 2024-12-30 11:42:25 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-30 11:42:25 +0200 |
| commit | 89dd2b1278f9e457d9d343742a51de27502ebca1 (patch) | |
| tree | 22899e27c4131d86f395d2c501d92a694eb6a137 /examples/wgpu-html5 | |
| parent | b2d51adc4b433c44298d35bb19637496c99ce44b (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/wgpu-html5')
| -rw-r--r-- | examples/wgpu-html5/README.md | 25 | ||||
| -rwxr-xr-x | examples/wgpu-html5/build.py | 19 | ||||
| -rw-r--r-- | examples/wgpu-html5/example.js | 89 | ||||
| -rw-r--r-- | examples/wgpu-html5/index.html | 15 | ||||
| -rw-r--r-- | examples/wgpu-html5/shader.slang | 28 |
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; +} |
