Skip to content

Commit 38b813c

Browse files
authored
Merge pull request #53 from Devon7925/update-playground
Update slang playground version
2 parents 00bff9f + 8058a83 commit 38b813c

14 files changed

Lines changed: 1562 additions & 1205 deletions

.github/workflows/build-vsix.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ name: Build VSIX
33
on:
44
push:
55
branches: [ main ]
6+
# Allows you to run this workflow manually from the Actions tab
7+
workflow_dispatch:
68

79
jobs:
810
build:

client/src/nativeClientMain.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,25 @@ export async function activate(context: ExtensionContext) {
8787
workspace.registerTextDocumentContentProvider('slang-synth', synthCodeProvider)
8888
);
8989

90-
// Initialize language server options, including the implicit playground.slang file.
91-
const playgroundUri = vscode.Uri.file(path.join(context.extensionPath, 'external', 'slang-playground', 'engine', 'slang-compilation-engine', 'src', 'slang', 'playground.slang'));
92-
const playgroundDocument = await vscode.workspace.openTextDocument(playgroundUri);
90+
// Initialize language server options, including the implicit playground.slang file and other embedded slang files.
91+
const slangDir = path.join(context.extensionPath, 'external', 'slang-playground', 'engine', 'slang-compilation-engine', 'src', 'slang');
92+
let embeddedSlangFiles: { uri: string, content: string }[] = [];
93+
if (fs.existsSync(slangDir)) {
94+
const names = fs.readdirSync(slangDir);
95+
for (const name of names) {
96+
if(!name.endsWith(".slang")) continue;
97+
const fileUri = vscode.Uri.file(path.join(slangDir, name));
98+
const fileDocument = await vscode.workspace.openTextDocument(fileUri);
99+
embeddedSlangFiles.push({ uri: fileUri.toString(), content: fileDocument.getText() });
100+
}
101+
}
93102
const initializationOptions: ServerInitializationOptions = {
94103
extensionUri: context.extensionUri.toString(true),
95104
workspaceUris: vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders.map(folder => folder.uri.fsPath) : [],
96-
files: [... await getSlangFilesWithContents(), {uri: playgroundUri.toString(), content: playgroundDocument.getText() }]
105+
files: [
106+
... await getSlangFilesWithContents(),
107+
...embeddedSlangFiles
108+
]
97109
}
98110
worker = new Worker(path.join(context.extensionPath, 'server', 'dist', 'nativeServerMain.js'), {
99111
workerData: initializationOptions

client/src/sharedClient.ts

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ function getSlangLogChannel(): vscode.OutputChannel {
99
return slangLogChannel;
1010
}
1111

12-
import type { CompiledPlayground, CompileRequest, EntrypointsRequest, EntrypointsResult, PlaygroundMessage, Result, Shader, UniformController } from 'slang-playground-shared';
13-
import { isControllerRendered, RUNNABLE_ENTRY_POINT_NAMES } from "slang-playground-shared";
12+
import type { CompiledPlayground, CompileRequest, EntrypointsRequest, EntrypointsResult, PlaygroundMessage, Result, Shader } from 'slang-playground-shared';
13+
import { isControllerRendered } from "slang-playground-shared";
1414

1515
// Maps to track open panels by file URI and command type
1616
const playgroundPanels = new Map<string, vscode.WebviewPanel>();
@@ -82,17 +82,11 @@ export async function sharedActivate(context: ExtensionContext, slangHandler: Sl
8282
commands.registerCommand('slang.playgroundRun', async () => {
8383
const userSource = window.activeTextEditor.document.getText();
8484
const userURI = window.activeTextEditor.document.uri;
85-
const shaderType = checkShaderType(userSource);
86-
if (shaderType == null) {
87-
vscode.window.showErrorMessage("Error: In order to run the shader, please define either imageMain or printMain function in the shader code.");
88-
return;
89-
}
9085
const compileResult = await slangHandler.compilePlayground({
9186
target: "WGSL",
92-
entrypoint: shaderType,
87+
entrypoint: null,
9388
sourceCode: userSource,
9489
shaderPath: window.activeTextEditor.document.uri.toString(false),
95-
noWebGPU: false,
9690
uri: userURI.toString(),
9791
});
9892
if (compileResult.succ == false) {
@@ -126,7 +120,7 @@ export async function sharedActivate(context: ExtensionContext, slangHandler: Sl
126120
panel.onDidDispose(() => playgroundPanels.delete(playgroundKey));
127121
panel.webview.html = getWebviewContent(context, panel, 'client/dist/webviewBundle.js', 'client/dist/webviewBundle.css');
128122

129-
if (shaderType === 'printMain') {
123+
if (compilation.outputTypes.includes("printing")) {
130124
const outputKey = userURI.toString() + ':output';
131125
if (outputPanels.has(outputKey)) {
132126
try { outputPanels.get(outputKey)!.dispose(); } catch { }
@@ -210,7 +204,7 @@ export async function sharedActivate(context: ExtensionContext, slangHandler: Sl
210204

211205
function updateContext(editor: vscode.TextEditor | undefined) {
212206
const text = editor?.document.getText() || "";
213-
const shouldShow = text.match("void\\s+printMain\\s*\\(") != null || text.match("float4\\s+imageMain\\s*\\(") != null;
207+
const shouldShow = text.match("import playground;") != null;
214208
vscode.commands.executeCommand('setContext', 'isPlaygroundFile', shouldShow);
215209
}
216210

@@ -257,10 +251,14 @@ export async function sharedActivate(context: ExtensionContext, slangHandler: Sl
257251
entrypoint: selectedEntrypoint,
258252
sourceCode: userSource,
259253
shaderPath: window.activeTextEditor.document.uri.toString(false),
260-
noWebGPU: true,
261254
});
262255
if (compilationResult.succ == false) {
256+
const logChannel = getSlangLogChannel();
263257
vscode.window.showErrorMessage(compilationResult.message);
258+
if (compilationResult.log) {
259+
logChannel.appendLine(compilationResult.log);
260+
logChannel.show(true);
261+
}
264262
return;
265263
}
266264
const shader = compilationResult.result;
@@ -282,10 +280,14 @@ export async function sharedActivate(context: ExtensionContext, slangHandler: Sl
282280
entrypoint: "",
283281
sourceCode: userSource,
284282
shaderPath: window.activeTextEditor.document.uri.toString(false),
285-
noWebGPU: true,
286283
});
287284
if (compilationResult.succ == false) {
285+
const logChannel = getSlangLogChannel();
288286
vscode.window.showErrorMessage(compilationResult.message);
287+
if (compilationResult.log) {
288+
logChannel.appendLine(compilationResult.log);
289+
logChannel.show(true);
290+
}
289291
return;
290292
}
291293
const shader = compilationResult.result;
@@ -345,15 +347,3 @@ export async function sharedActivate(context: ExtensionContext, slangHandler: Sl
345347
</html>
346348
`;
347349
}
348-
349-
export function checkShaderType(userSource: string) {
350-
// we did a pre-filter on the user input source code.
351-
let shaderTypes = RUNNABLE_ENTRY_POINT_NAMES.filter((entryPoint) => userSource.includes(entryPoint));
352-
353-
// Only one of the main function should be defined.
354-
// In this case, we will know that the shader is not runnable, so we can only compile it.
355-
if (shaderTypes.length !== 1)
356-
return null;
357-
358-
return shaderTypes[0];
359-
}

external/slang-playground

Submodule slang-playground updated 38 files

media/playgroundDocumentation.md

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ No data is sent to any server.
55

66
## Compiling Shaders (the "Compile" button)
77

8-
You can compile shaders to many targets supported by Slang here, including SPIR-V, HLSL, GLSL, Metal, and
8+
You can compile shaders to many targets supported by Slang here, including SPIR-V, HLSL, GLSL, Metal, CUDA, and
99
WGSL.
1010
Generating DXIL requires DXC, which doesn't run in the browser so it's not supported here. You will have the opportunuty to select entrypoints for targets that don't support multiple entrypoints.
1111
You can compile shaders with either the button in the top right of the editor, or the command `Slang Compile`.
@@ -14,13 +14,36 @@ You can compile shaders with either the button in the top right of the editor, o
1414

1515
In addition to compiling shaders, this playground extension can also run simple shaders via WebGPU.
1616
You can run shaders with either the button in the top right of the editor, or the command `Run Playground`.
17-
The playground supports running two types of shaders:
17+
The playground supports running compute shaders that produce an image, text output, or both. All runnable shaders must be compute shaders marked with the `[shader("compute")]` attribute. You can use `import rendering` or `import printing` to display shader results.
1818

19-
* **Image Compute Shader**: This is a compute shader that returns a pixel value at a given image
20-
coordinate, similar to ShaderToys.
21-
An image compute shader must define a `float4 imageMain(uint2 dispatchThreadID, int2 screenSize)` function.
22-
* **Print Shader**: This is a shader that prints text to the output pane.
23-
A print shader must define a `void printMain()` function.
19+
### Rendering
20+
21+
The `rendering` module provides the following function:
22+
23+
`void drawPixel(uint2 location, IFunc<float4, int2> renderFunction)`
24+
25+
This will render to the output at the provided location based on the result of the provided function. The provided function takes in the screen size as an `int2`. It also creates an `outputTexture` texture, so you can create an entrypoint that executes for every pixel like so:
26+
27+
```slang
28+
import playground;
29+
import rendering;
30+
31+
[shader("compute")]
32+
[numthreads(16, 16, 1)]
33+
[playground::CALL::SIZE_OF("outputTexture")]
34+
void imageMain(uint2 dispatchThreadID: SV_DispatchThreadID)
35+
{
36+
drawPixel(dispatchThreadID, (int2 _) => float4(0.3, 0.7, 0.55, 1.0));
37+
}
38+
```
39+
40+
### Printing
41+
42+
The `printing` module provides the following function:
43+
44+
### `void printf<T>(String format, expand each T values) where T : IPrintf`
45+
46+
Prints the values formatted according to the format.
2447

2548
## Shader Commands
2649

@@ -54,6 +77,10 @@ Initialize a `float` buffer with uniform random floats between 0 and 1.
5477

5578
Gives a `float` uniform the current time in milliseconds.
5679

80+
### `[playground::FRAME_ID]`
81+
82+
Gives a `float` uniform the current frame index (starting from 0).
83+
5784
### `[playground::MOUSE_POSITION]`
5885

5986
Gives a `float4` uniform mouse data.
@@ -85,17 +112,13 @@ Dispatch a compute pass using the resource size to determine the work-group size
85112
Dispatch a compute pass with the given grid of threads.
86113
The number of work-groups will be determined by dividing by the number of threads per work-group and rounding up.
87114

88-
### `[playground::CALL::ONCE]`
89-
90-
Only dispatch the compute pass once at the start of rendering. Should be used in addition to another CALL command.
115+
### `[playground::CALL_INDIRECT("BUFFER-NAME", 0)]`
91116

92-
## Playground functions
117+
Dispatch a compute pass with an indirect command buffer and an offset in bytes.
93118

94-
The playground shader also provides the following functions:
95-
96-
### `void printf<T>(String format, expand each T values) where T : IPrintf`
119+
### `[playground::CALL::ONCE]`
97120

98-
Prints the values formatted according to the format. Only available in print shaders.
121+
Only dispatch the compute pass once at the start of rendering. Should be used in addition to another CALL command.
99122

100123
## Shader Reflection
101124

0 commit comments

Comments
 (0)