Skip to content

Commit 734833e

Browse files
authored
Document "Migrating from HLSL" (#4)
* Document "Migrating from HLSL"
1 parent d0ed383 commit 734833e

File tree

1 file changed

+304
-1
lines changed

1 file changed

+304
-1
lines changed

docs/coming-from-hlsl.md

Lines changed: 304 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,306 @@
11
# Migrating to Slang from HLSL
22

3-
[//]: # (TODO: write documentation on HLSL onramp here, and update link in docs/index.md)
3+
## Overview
4+
This guide provides a comprehensive overview of the primary syntax and feature differences between HLSL and Slang. It offers essential adjustments needed for a smooth transition and tips to enhance debugging and performance.
5+
6+
While the languages share similarities, paying close attention to specific syntax and function conventions will ensure a seamless migration of HLSL shaders to Slang.
7+
8+
9+
## Key Syntax and Feature Differences
10+
11+
### `enum` is scoped in Slang
12+
In HLSL, `enum` is unscoped, which means the enum values can be referred to without the enum's name.
13+
In Slang, `enum` is scoped, requiring explicit reference to the enum's name along with its values.
14+
15+
<table>
16+
<tr><td>HLSL shader</td><td>Slang shader</td></tr>
17+
<tr><td>
18+
19+
```hlsl
20+
enum MyEnum
21+
{
22+
MyEnum_A,
23+
MyEnum_B,
24+
MyEnum_C
25+
};
26+
27+
int MyFunc()
28+
{
29+
return int(MyEnum_A);
30+
}
31+
```
32+
</td><td>
33+
34+
```hlsl
35+
enum MyEnum
36+
{
37+
A,
38+
B,
39+
C
40+
};
41+
42+
int MyFunc()
43+
{
44+
return int(MyEnum::A);
45+
}
46+
```
47+
</td></tr></table>
48+
49+
To make `enum` unscoped in Slang, use the `-unscoped-enums` option or add the `[UnscopedEnum]` attribute to explicitly declare an enum as unscoped.
50+
```hlsl
51+
// Slang shader
52+
[UnscopedEnum]
53+
enum MyEnum
54+
{
55+
MyEnum_A,
56+
MyEnum_B,
57+
MyEnum_C
58+
};
59+
60+
int MyFunc()
61+
{
62+
return int(MyEnum_A);
63+
}
64+
```
65+
66+
67+
### Member functions are immutable by default
68+
By default, Slang member functions do not allow mutations to `this`. It is as if the member function has the `const` keyword in C/C++.
69+
70+
To mutate `this`, you must explicitly use the `[mutating]` attribute on each function.
71+
72+
This is a significant departure from HLSL, but the compiler will flag any missing keywords with an error.
73+
<table>
74+
<tr><td>HLSL shader</td><td>Slang shader</td></tr>
75+
<tr><td>
76+
77+
```hlsl
78+
struct Counter
79+
{
80+
int count;
81+
void increment() { count++; }
82+
};
83+
```
84+
</td><td>
85+
86+
```hlsl
87+
struct Counter
88+
{
89+
int count;
90+
91+
[mutating]
92+
void increment() { count++; }
93+
};
94+
```
95+
</td></tr></table>
96+
97+
98+
### Forward declaration is not needed and not supported
99+
In Slang, function declarations do not need to precede their usage.
100+
101+
Furthermore, Slang does not support separating the declaration from its definition for member functions. Member functions must be fully defined at the point of declaration.
102+
103+
104+
### Generics Instead of Templates
105+
Slang does not support a "template" feature like HLSL does.
106+
If your HLSL shaders use templates, they will need to be converted to use generics.
107+
Depending on the complexity, this conversion process is generally straightforward.
108+
109+
When the template argument is a constant integer value, use the `let XX : uint` or `uint XX` syntax as shown below,
110+
<table>
111+
<tr><td>HLSL shader</td><td>Slang shader</td></tr>
112+
<tr><td>
113+
114+
```hlsl
115+
template<uint strideX, uint strideY>
116+
uint GetValue(const uint index)
117+
{
118+
return index % strideX, index / strideY;
119+
}
120+
```
121+
</td><td>
122+
123+
```hlsl
124+
__generic<uint strideX, uint strideY>
125+
uint GetValue(const uint index)
126+
{
127+
return index % strideX, index / strideY;
128+
}
129+
```
130+
</td></tr></table>
131+
132+
When the template argument is a typename, you must use a built-in interface or a custom interface you define.
133+
<table>
134+
<tr><td>HLSL shader</td><td>Slang shader</td></tr>
135+
<tr><td>
136+
137+
```hlsl
138+
template<typename T>
139+
T max4(T x, T y, T z, T w)
140+
{
141+
return max(max(x, y), max(z, w));
142+
}
143+
```
144+
</td><td>
145+
146+
```hlsl
147+
__generic<typename T> // `typename` can be omitted
148+
T max4(T x, T y, T z, T w)
149+
where T : __BuiltinFloatingPointType
150+
{
151+
return max(max(x, y), max(z, w));
152+
}
153+
```
154+
</td></tr></table>
155+
156+
For more detailed explanations on defining a custom interface, please refer to the [Slang User's Guide](https://shader-slang.com/slang/user-guide/interfaces-generics.html).
157+
158+
159+
### `#pragma` for DXC wouldn't work for Slang
160+
161+
If your HLSL shaders use `#pragma` for the DXC compiler, these will not be compatible with Slang.
162+
You will need to remove these lines or encapsulate them as follows,
163+
```hlsl
164+
#if !COMPILER_SLANG
165+
// Generate vector truncation warnings to errors.
166+
#pragma warning(error: 3206)
167+
#endif // #if !COMPILER_SLANG
168+
```
169+
170+
171+
### Operator Overloading
172+
Slang supports operator overloading, but it cannot be defined as a member method.
173+
174+
<table>
175+
<tr><td>HLSL shader</td><td>Slang shader</td></tr>
176+
<tr><td>
177+
178+
```hlsl
179+
struct MyStruct
180+
{
181+
MyStruct operator+(MyStruct rhs)
182+
{
183+
MyStruct tmp;
184+
tmp.value = value + rhs.value;
185+
return tmp;
186+
}
187+
188+
float value;
189+
};
190+
```
191+
</td><td>
192+
193+
```hlsl
194+
struct MyStruct
195+
{
196+
MyStruct operator_ADD(MyStruct rhs)
197+
{
198+
MyStruct tmp;
199+
tmp.value = value + rhs.value;
200+
return tmp;
201+
}
202+
203+
float value;
204+
};
205+
206+
MyStruct operator+(MyStruct lhs, MyStruct rhs)
207+
{
208+
return lhs.operator_ADD(rhs);
209+
}
210+
```
211+
</td></tr></table>
212+
213+
For more details, please consult the [Slang User's Guide](https://shader-slang.com/slang/user-guide/convenience-features.html#operator-overloading)
214+
215+
### Subscript Operator
216+
Slang uses a different syntax for overloading subscript operator so both reads and writes to a subscript location can be defined.
217+
218+
<table>
219+
<tr><td>HLSL shader</td><td>Slang shader</td></tr>
220+
<tr><td>
221+
222+
```hlsl
223+
struct MyType
224+
{
225+
float values[12];
226+
227+
float operator[](int index)
228+
{
229+
return values[index];
230+
}
231+
}
232+
```
233+
</td><td>
234+
235+
```hlsl
236+
struct MyType
237+
{
238+
float values[12];
239+
240+
__subscript(int index) -> float
241+
{
242+
get { return val[index]; }
243+
set { val[index] = newValue; }
244+
}
245+
}
246+
```
247+
</td></tr></table>
248+
249+
For more details, please consult the [Slang User's Guide](https://shader-slang.com/slang/user-guide/convenience-features.html#subscript-operator).
250+
251+
252+
### Implicit parameter binding
253+
Slang binds resources based on the shader before Dead-Code-Elimination. This means that even if a uniform parameter is not utilized by the shader, Slang will still assign a resource binding index to it.
254+
255+
This differs from HLSL's behavior, where the DXC compiler removes unused parameters from the compiled shaders. The DXC reflection API then provides information on which parameters are actually being used.
256+
257+
This approach ensures consistent behavior across different variations of the same shader, as Slang does not remove unused parameters during its linking process, maintaining a consistent set of parameters regardless of how the shader was used or linked.
258+
259+
260+
### `unsigned int` is not supported
261+
Slang does not support type names that use more than one token. As a result, `unsigned int` or `signed int` are not supported. These should be renamed to `uint` or `int` respectively.
262+
263+
264+
### Buffer Layouts
265+
Slang defaults to std140 for constant buffers and std430 for structured buffers and related.
266+
- Use `-fvk-use-scalar-layout` to set buffer layout to scalar block layout.
267+
- Use `-fvk-use-gl-layout` to set std430 layout for raw buffer load/stores
268+
269+
StructuredBuffer and related objects can also take a per resource layout
270+
```hlsl
271+
StructuredBuffer<MyStruct, Std140DataLayout>
272+
StructuredBuffer<MyStruct, Std430DataLayout>
273+
StructuredBuffer<MyStruct, ScalarDataLayout>
274+
```
275+
276+
277+
### `#pragma pack_matrix()` is not supported
278+
HLSL provides a way to change the matrix packing order with a pragma.
279+
280+
While Slang doesn't support the same pragma syntax, you can achieve the same functionality with `-matrix-layout-column-major` or `-matrix-layout-row-major`.
281+
282+
283+
### Slang requires more strict type casting
284+
Slang demands more strict type casting, but you can add your own casting functions if needed.
285+
Due to this difference, some casting issues may appear as errors while migrating your HLSL shaders to Slang.
286+
287+
288+
289+
## Debugging and Performance Tips
290+
291+
When debugging Slang shaders, disabling optimizations can simplify the debugging process. Use -O0 during development, and consider experimenting with [ForceInline] to reduce compile times for performance-critical shaders.
292+
293+
294+
### `#line` directives
295+
Slang offers a command-line option to emit or suppress `#line` directives when targeting C-like text formats such as HLSL, GLSL, Metal, and WGSL.
296+
When `#line` directives are emitted, they can assist in debugging with shader debugging tools, as these tools can correlate back to the original Slang shader.
297+
298+
For more information, please refer to `LineDirectiveMode` in the [Slang User's Guide](https://shader-slang.com/slang/user-guide/compiling.html).
299+
300+
301+
### Experiment with [ForceInline]
302+
`[ForceInline]` is akin to the `inline` keyword in C++, where the body of a function is inlined at the call locations. This typically does not make any observable differences, but we have noticed that using `[ForceInline]` can reduce compile times compared to HLSL compilation with DXC.
303+
304+
This is because the shader optimization step inside DXC involves additional processes to optimize out the `out` or `inout` modifiers. When the function is inlined, these modifiers become redundant, thus reducing compile times.
305+
306+
This characteristic may change in the future, but it is worth experimenting with `[ForceInline]`.

0 commit comments

Comments
 (0)