summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/wgpu-slang-wasm/README.md16
-rw-r--r--examples/wgpu-slang-wasm/example.js158
-rw-r--r--examples/wgpu-slang-wasm/index.html12
-rw-r--r--examples/wgpu-slang-wasm/shader.slang28
4 files changed, 214 insertions, 0 deletions
diff --git a/examples/wgpu-slang-wasm/README.md b/examples/wgpu-slang-wasm/README.md
new file mode 100644
index 000000000..f0dd5d6a6
--- /dev/null
+++ b/examples/wgpu-slang-wasm/README.md
@@ -0,0 +1,16 @@
+# Simple WebGPU example using Slang WebAssembly library
+
+## Description
+
+This is a simple example showing how WebGPU applications can use the slang-wasm library to compile slang shaders at runtime to WGSL.
+The resulting application shows a green triangle rendered on a black background.
+
+## Instructions
+
+Follow the WebAssembly build instructions in `docs/building.md` to produce `slang-wasm.js` and `slang-wasm.wasm`, and place these files in this directory.
+
+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-slang-wasm/example.js b/examples/wgpu-slang-wasm/example.js
new file mode 100644
index 000000000..9e554bb44
--- /dev/null
+++ b/examples/wgpu-slang-wasm/example.js
@@ -0,0 +1,158 @@
+"use strict";
+
+let Example = {
+ initialize: async function (slang, 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]);
+ }
+
+ const slangCode = await fetch("shader.slang").then(r => r.text());
+
+ var wasmCompileTarget = null;
+ var compileTargetMap = slang.module.getCompileTargets();
+ for (var i = 0; i < compileTargetMap.length; i++) {
+ var target = compileTargetMap[i];
+ if(target.name == "WGSL") {
+ wasmCompileTarget = target.value;
+ }
+ }
+ if (wasmCompileTarget === null) {
+ throw new Error("Slang/WASM module doesn't support WGSL compile target.");
+ }
+
+ var slangSession = slang.globalSession.createSession(wasmCompileTarget);
+ if (!slangSession) {
+ throw new Error("Failed to create global Slang session.");
+ }
+
+ var wgslShaders = null;
+ try {
+ var module = slangSession.loadModuleFromSource(
+ slangCode, "shader", '/shader.slang'
+ );
+ var vertexEntryPoint = module.findAndCheckEntryPoint(
+ "vertexMain", slang.constants.STAGE_VERTEX
+ );
+ var fragmentEntryPoint = module.findAndCheckEntryPoint(
+ "fragmentMain", slang.constants.STAGE_FRAGMENT
+ );
+ var linkedProgram = slangSession.createCompositeComponentType([
+ module, vertexEntryPoint, fragmentEntryPoint
+ ]).link();
+ wgslShaders = {
+ vertex: linkedProgram.getEntryPointCode(
+ 0 /* entryPointIndex */, 0 /* targetIndex */
+ ),
+ fragment: linkedProgram.getEntryPointCode(
+ 1 /* entryPointIndex */, 0 /* targetIndex */
+ ),
+ };
+ } finally {
+ if (slangSession) {
+ slangSession.delete();
+ }
+ }
+
+ if (!wgslShaders) {
+ throw new Error("Failed to compile WGSL shaders.");
+ }
+
+ render(wgslShaders);
+ }
+}
+
+var Module = {
+ onRuntimeInitialized: function() {
+ const canvas = document.querySelector("canvas");
+
+ var globalSlangSession = Module.createGlobalSession();
+ if (!globalSlangSession) {
+ throw new Error("Failed to create global Slang session.");
+ }
+
+ const slang = {
+ module: Module,
+ globalSession: globalSlangSession,
+ constants: {
+ STAGE_VERTEX: 1,
+ STAGE_FRAGMENT: 5,
+ },
+ };
+ Example.initialize(slang, canvas);
+ },
+};
diff --git a/examples/wgpu-slang-wasm/index.html b/examples/wgpu-slang-wasm/index.html
new file mode 100644
index 000000000..b03d8366b
--- /dev/null
+++ b/examples/wgpu-slang-wasm/index.html
@@ -0,0 +1,12 @@
+<html>
+ <head>
+ <title>WebGPU Triangle using Slang WASM</title>
+ <script src="example.js"></script>
+ </head>
+ <body>
+ <center>
+ <canvas width="512" height="512"></canvas>
+ </center>
+ <script src="slang-wasm.js"></script>
+ </body>
+</html>
diff --git a/examples/wgpu-slang-wasm/shader.slang b/examples/wgpu-slang-wasm/shader.slang
new file mode 100644
index 000000000..0721a1fff
--- /dev/null
+++ b/examples/wgpu-slang-wasm/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;
+}