Skip to content

Latest commit

 

History

History
1596 lines (1286 loc) · 40.6 KB

File metadata and controls

1596 lines (1286 loc) · 40.6 KB

Vulkan High Level Shader Language Comparison

While Vulkan itself consumes shaders in a binary format called SPIR-V, shaders are usually written in a high level language. This section provides a mapping between shader functionality for the most common ones used with Vulkan: GLSL, HLSL, and Slang. This is mostly aimed at people wanting to migrate from one high level shader language to another. It’s meant as a starting point and not as a complete porting guide to one language from another.

Tip

For more details on using HLSL with Vulkan, visit this chapter.

For more details on using Slang with Vulkan, visit this chapter.

Note

The following listings are by no means complete, and mappings for newer extensions may be missing. Also note that concepts do not always map 1:1 between languages. E.g. there are no semantics in GLSL, while some newer GLSL functionality may not (yet) be available in HLSL or Slang.

Slang is largely compatible with HLSL 2018 but adds several advanced features like generics, interfaces, and modules. In most cases where HLSL syntax is shown, Slang supports the same syntax, but may offer additional options.

Extensions

In GLSL extensions need to be explicitly enabled using the #extension directive. This is not necessary in HLSL or Slang. Both compilers will implicitly select suitable SPIR-V extensions based on the shader.

For HLSL, if required one can use -fspv-extension arguments to explicitly select extensions.

For Slang, extensions can be explicitly enabled using command-line options like -D or through the Slang API. Slang also supports SPIR-V intrinsics similar to HLSL.

Data types

Note

Types work similarly across GLSL, HLSL, and Slang. GLSL has explicit vector or matrix types, while HLSL and Slang use basic types with numeric suffixes. HLSL offers some advanced type features like C++ templates, and Slang extends this further with full generic support. This section contains a basic summary with examples to show type differences between the languages.

GLSL HLSL Example Slang-specific

vecn

floatn

vec4 → float4

ivecn

intn

ivec3 → int3

matnxm or shorthand matn

floatnxm

mat4 → float4x4

n/a

n/a

interface types

n/a

limited templates

full generics support

Note

Slang uses the same basic data types as HLSL (floatn, intn, floatnxm, etc.) but adds several advanced features like interfaces and full generic support.

The syntax for casting types differs:

GLSL:

mat4x3 mat = mat4x3(ubo.view);

HLSL:

float4x3 mat = (float4x3)(ubo.view);

Slang:

float4x3 mat = (float4x3)(ubo.view);
// Or using generics
let mat = cast<float4x3>(ubo.view);

Matrix Layout

It is important to be mindful that matrices in GLSL are column-major, while matrices in HLSL and Slang are row-major by default. This affects things like matrix construction.

high_level_shader_language_comparison_matrix.png

Slang-specific Types

Slang extends HLSL’s type system with several advanced features:

Interfaces

Slang supports interfaces similar to C# or Java:

interface IMaterial {
    float3 evaluateBRDF(float3 viewDir, float3 lightDir, float3 normal);
}

struct LambertianMaterial : IMaterial {
    float3 albedo;

    float3 evaluateBRDF(float3 viewDir, float3 lightDir, float3 normal) {
        return albedo * max(0, dot(normal, lightDir)) / 3.14159;
    }
}

Generics

Slang provides full generic support:

// Generic function
T min<T>(T a, T b) {
    return a < b ? a : b;
}

// Generic struct
struct Array<T, let N : int> {
    T data[N];

    T getElement(int index) { return data[index]; }
}

// Usage
float result = min<float>(1.0, 2.0);
Array<float, 4> myArray;

Implicit vk Namespace

For Vulkan concepts that are not available in DirectX, an implicit namespace has been added that marks Vulkan specific features.

Slang supports the same vk namespace as HLSL for Vulkan-specific functionality. This allows Slang code to use the same Vulkan-specific attributes and functions as HLSL:

// Binding a resource in Slang using vk namespace
[[vk::binding(0, 0)]]
Texture2D albedoMap;

// Push constants in Slang
struct PushConstants {
    float4x4 transform;
};
[[vk::push_constant]]
PushConstants pushConstants;

In addition to supporting the HLSL vk namespace, Slang also provides its own parameter block system that can be used alongside the vk namespace for more organized resource binding (see the Parameter Blocks section below).

SPIR-V macro

When using DXC to compile HLSL to SPIR-V you can use the __spirv__ macro for Vulkan specific code. This is useful if HLSL shaders need to work with both Vulkan and D3D:

#ifdef __spirv__
[[vk::binding(0, 1)]]
#endif
ConstantBuffer<Node> node : register(b0, space1);

Slang provides similar conditional compilation capabilities, but with more options for cross-API development. Slang does not automatically define target-specific preprocessor macros (preprocessing runs once, regardless of how many targets you compile for), so any target macro must be passed on the command line with -D:

// TARGET_VULKAN is defined on the command line,
// e.g. `slangc -target spirv -DTARGET_VULKAN=1 ...`
#ifdef TARGET_VULKAN
[[vk::binding(0, 1)]]
#endif
ConstantBuffer<Node> node : register(b0, space1);

// Using Slang's target-agnostic parameter blocks instead avoids
// the need for conditional compilation altogether
ParameterBlock<Resources> resources;

Slang’s multi-target compilation system allows you to write shaders that can be compiled for multiple graphics APIs from a single source file, with conditional compilation to handle API-specific differences where parameter blocks aren’t a good fit.

SPIR-V intrinsics

DXC supports SPIR-V intrinsics with the GL_EXT_spirv_intrinsics extension. This adds support for embedding arbitrary SPIR-V in the middle of GLSL for features not available in DirectX. For this new keywords are added to the vk namespace that map SPIR-V opcodes, incl. vk::ext_extension, vk::ext_capability, vk::ext_builtin_input, vk::ext_execution_mode and vk::ext_instruction.

Example for using the stencil export SPIR-V extension in HLSL:

[[vk::ext_capability(/* StencilExportEXT */ 5013)]]
[[vk::ext_extension("SPV_EXT_shader_stencil_export")]]
vk::ext_execution_mode(/* StencilRefReplacingEXT */ 5027);

Example for setting up the built-in to access vertex positions in ray tracing:

[[vk::ext_extension("SPV_KHR_ray_tracing_position_fetch")]]
[[vk::ext_capability(RayTracingPositionFetchKHR)]]
[[vk::ext_builtin_input(HitTriangleVertexPositionsKHR)]]
const static float3 gl_HitTriangleVertexPositions[3];

Slang supports the same SPIR-V intrinsics capabilities as HLSL, allowing you to access Vulkan-specific features that don’t have direct mappings in the language. The syntax is identical to HLSL:

[[vk::ext_capability(/* StencilExportEXT */ 5013)]]
[[vk::ext_extension("SPV_EXT_shader_stencil_export")]]
vk::ext_execution_mode(/* StencilRefReplacingEXT */ 5027);

In addition, Slang provides a more structured approach to extension capabilities through its module system, allowing you to encapsulate extension-specific code in dedicated modules:

// In a dedicated module for stencil export functionality
module Extensions.StencilExport;

[[vk::ext_capability(/* StencilExportEXT */ 5013)]]
[[vk::ext_extension("SPV_EXT_shader_stencil_export")]]
vk::ext_execution_mode(/* StencilRefReplacingEXT */ 5027);

public void writeStencil(uint value) {
    // Implementation using stencil export
}

// In main shader code
import Extensions.StencilExport;

void main() {
    // Use the extension functionality
    writeStencil(42);
}

Built-ins vs. Semantics

Note

While GLSL makes heavy use of input and output variables built into the languages called "built-ins", there is no such concept in HLSL or Slang. HLSL and Slang instead use semantics, strings that are attached to inputs or outputs that contain information about the intended use of that variable. They are prefixed with SV_. For HLSL and Slang, input values are explicit arguments for the main entry point and the shader needs to explicitly return an output.

Slang follows HLSL’s semantic-based approach but provides additional features through its interface system that can abstract away some of the semantic details.

Slang Interface Abstractions

Slang allows you to define interfaces that can abstract away some of the semantic details:

// Define a vertex shader interface
interface IVertexShader
{
    // Input structure with semantics
    struct Input
    {
        float3 position;
        float3 normal;
        float2 texCoord;
    };

    // Output structure with semantics
    struct Output
    {
        float4 position : SV_POSITION;
        float3 worldPos : POSITION0;
        float3 normal : NORMAL0;
        float2 texCoord : TEXCOORD0;
    };

    // Vertex shader function
    Output computeVertex(Input input);
}

// Implement the interface
struct StandardVertexShader : IVertexShader
{
    // Implementation of the vertex shader function
    IVertexShader.Output computeVertex(IVertexShader.Input input)
    {
        IVertexShader.Output output;
        // Implementation...
        return output;
    }
}

// Use the interface in a shader entry point
[shader("vertex")]
IVertexShader.Output vertexMain(IVertexShader.Input input)
{
    StandardVertexShader vertexShader;
    return vertexShader.computeVertex(input);
}

This approach allows for more modular and reusable shader code while still leveraging the semantic system.

Examples

Writing positions from the vertex shader:

GLSL:

layout (location = 0) in vec4 inPos;

void main() {
    // The vertex output position is written to the gl_Position built-in
    gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * inPos.xyz;
}

HLSL:

struct VSOutput
{
    // The SV_POSITION semantic declares the Pos member as the vertex output position
    float4 Pos : SV_POSITION;
};

VSOutput main(VSInput input)
{
    VSOutput output = (VSOutput)0;
    output.Pos = mul(ubo.projectionMatrix, mul(ubo.viewMatrix, mul(ubo.modelMatrix, input.Pos)));
    return output;
}

Slang:

struct VSOutput
{
    // Same as HLSL, using SV_POSITION semantic
    float4 Pos : SV_POSITION;
};

// Standard approach (identical to HLSL)
VSOutput main(VSInput input)
{
    VSOutput output = (VSOutput)0;
    output.Pos = mul(ubo.projectionMatrix, mul(ubo.viewMatrix, mul(ubo.modelMatrix, input.Pos)));
    return output;
}

// Alternative using interfaces
interface IVertexTransform
{
    float4x4 getTransform();
}

struct StandardTransform : IVertexTransform
{
    float4x4 projectionMatrix;
    float4x4 viewMatrix;
    float4x4 modelMatrix;

    float4x4 getTransform()
    {
        return mul(projectionMatrix, mul(viewMatrix, modelMatrix));
    }
}

VSOutput transformedMain(VSInput input, IVertexTransform transform)
{
    VSOutput output = (VSOutput)0;
    output.Pos = mul(transform.getTransform(), float4(input.Pos, 1.0));
    return output;
}

Reading the vertex index:

GLSL:

void main()
{
    // The vertex index is stored in the gl_VertexIndex built-in
    outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
}

HLSL:

struct VSInput
{
    // The SV_VertexID semantic declares the VertexIndex member as the vertex index input
    uint VertexIndex : SV_VertexID
};

VSOutput main(VSInput input)
{
    VSOutput output = (VSOutput)0;
    output.UV = float2((input.VertexIndex << 1) & 2, input.VertexIndex & 2);
    return output;
}

Slang:

struct VSInput
{
    // Same as HLSL, using SV_VertexID semantic
    uint VertexIndex : SV_VertexID
};

VSOutput main(VSInput input)
{
    VSOutput output = (VSOutput)0;
    output.UV = float2((input.VertexIndex << 1) & 2, input.VertexIndex & 2);
    return output;
}

// Alternative using a utility function
float2 calculateUVFromVertexID(uint vertexID)
{
    return float2((vertexID << 1) & 2, vertexID & 2);
}

VSOutput alternativeMain(VSInput input)
{
    VSOutput output = (VSOutput)0;
    output.UV = calculateUVFromVertexID(input.VertexIndex);
    return output;
}

Shader interface

Note

Shader interfaces greatly differ between GLSL, HLSL, and Slang. GLSL uses a more procedural approach with global variables, while HLSL uses a more object-oriented approach with explicit structures and semantics. Slang extends HLSL’s approach with additional features like parameter blocks, interfaces, and modules.

Slang Parameter Blocks

Slang introduces a powerful concept called parameter blocks that provides a more structured way to organize shader resources:

// Define a parameter block type
struct MaterialResources
{
    Texture2D albedoMap;
    SamplerState samplerState;

    struct Constants {
        float4 baseColor;
        float roughness;
        float metallic;
    } constants;
};

// Declare a parameter block - Slang assigns its descriptor set and
// binding automatically, no explicit binding is required
ParameterBlock<MaterialResources> material;

// Usage in shader code
float4 sampleAlbedo(float2 uv)
{
    // Access resources through the parameter block
    return material.albedoMap.Sample(material.samplerState, uv);
}

float getRoughness()
{
    // Access constants through the parameter block
    return material.constants.roughness;
}

Parameter blocks offer several advantages: * Logical grouping of related resources * Cleaner shader code with hierarchical access * Better compatibility across different graphics APIs * Support for nested resources and constants * Improved reflection capabilities

Descriptor bindings

GLSL

layout (set = <set-index>, binding = <binding-index>) uniform <type> <name>

There are two options for defining descriptor set and binding indices in HLSL when using Vulkan.

HLSL way

<type> <name> : register(<register-type><binding-index>, space<set-index>)

Using this syntax, descriptor set and binding indices will be implicitly assigned from the set and binding index.

Vulkan namespace

[[vk::binding(binding-index, set-index)]]
<type> <name>

With this option, descriptor set and binding indices are explicitly set using vk::binding.

Note

It’s possible to use both the vk::binding[] and register() syntax for one descriptor. This can be useful if a shader is used for both Vulkan and DirectX.

Examples

GLSL
layout (set = 1, binding = 0) uniform Node {
    mat4 matrix;
} node;
HLSL
struct Node {
    float4x4 transform;
};

// HLSL style
ConstantBuffer<Node> node : register(b0, space1);

// Vulkan style
[[vk::binding(0, 1)]]
ConstantBuffer<Node> node;

// Combined
[[vk::binding(0, 1)]]
ConstantBuffer<Node> node : register(b0, space1);

Uniforms

GLSL

layout (set = <set-index>, binding = <binding-index>) uniform <type> <name>

Examples:

// Uniform buffer
layout (set = 0, binding = 0) uniform UBO
{
    mat4 projection;
} ubo;

// Combined image sampler
layout (set = 0, binding = 1) uniform sampler2D samplerColor;

HLSL

<type> <name> : register(<register-type><binding-index>, space<set-index>)

or

[[vk::binding(binding-index, set-index)]]
<type> <name>

Examples:

// Uniform buffer
struct UBO
{
    float4x4 projection;
};
ConstantBuffer<UBO> ubo : register(b0, space0);

// Combined image sampler
Texture2D textureColor : register(t1);
SamplerState samplerColor : register(s1);

If using the HLSL descriptor binding syntax <register type> can be:

Type Register Description Vulkan resource

b

Constant buffer

Uniform buffer

t

Texture and texture buffer

Uniform texel buffer and read-only shader storage buffer

c

Buffer offset

layout(offset = N)

s

Sampler

same

u

Unordered Access View

Shader storage buffer, storage image and storage texel buffer

Shader inputs

GLSL

layout (location = <location-index>) in <type> <name>;

Example:

layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec2 inUV0;
layout (location = 3) in vec2 inUV1;

HLSL

[[vk::location(<location-index>)]] <type> <name> : <semantic-type>;

Example:

struct VSInput
{
[[vk::location(0)]] float3 Pos : POSITION;
[[vk::location(1)]] float3 Normal : NORMAL;
[[vk::location(2)]] float2 UV0 : TEXCOORD0;
[[vk::location(3)]] float2 UV1 : TEXCOORD1;
};

VSOutput main(VSInput input) {
}

<semantic type> can be

Semantic Description Type

BINORMAL[n]

Binormal

float4

BLENDINDICES[n]

Blend indices

uint

BLENDWEIGHT[n]

Blend weights

float

COLOR[n]

Diffuse and specular color

float4

NORMAL[n]

Normal vector

float4

POSITION[n]

Vertex position in object space.

float4

POSITIONT

Transformed vertex position

float4

PSIZE[n]

Point size

float

TANGENT[n]

Tangent

float4

TEXCOORD[n]

Texture coordinates

float4

n is an optional integer between 0 and the number of resources supported. (source)

Shader outputs

Passing data between stages

E.g. for vertex and tessellations shaders.

GLSL
layout (location = <location-index>) out/in <type> <name>;

Example:

layout (location = 0) out vec3 outNormal;
layout (location = 1) out vec3 outColor;
layout (location = 2) out vec2 outUV;
layout (location = 3) out vec3 outViewVec;

void main() {
    gl_Position = vec4(inPos, 1.0);
    outNormal = inNormal;
}
HLSL
[[vk::location(<location-index>)]] <type> <name> : <semantic-type>;

Example:

struct VSOutput
{
                    float4 Pos : SV_POSITION;
[[vk::location(0)]] float3 Normal : NORMAL;
[[vk::location(1)]] float3 Color : COLOR;
[[vk::location(2)]] float2 UV : TEXCOORD0;
[[vk::location(3)]] float3 ViewVec : TEXCOORD1;
}

VSOutput main(VSInput input) {
    VSOutput output = (VSOutput)0;
    output.Pos = float4(input.Pos.xyz, 1.0);
    output.Normal = input.Normal;
    return output;
}

Writing attachments

For fragment shaders.

GLSL
layout (location = <attachment-index>) out/in <type> <name>;

Example:

layout (location = 0) out vec4 outPosition;
layout (location = 1) out vec4 outNormal;
layout (location = 2) out vec4 outAlbedo;

void main() {
    outPosition = ...
    outNormal = ...
    outAlbedo = ...
}
HLSL
<type> <name> : SV_TARGET<attachment-index>;

Example:

struct FSOutput
{
    float4 Position : SV_TARGET0;
    float4 Normal : SV_TARGET1;
    float4 Albedo : SV_TARGET2;
};

FSOutput main(VSOutput input) {
    output.Position = ...
    output.Normal = ...
    output.Albedo = ...
    return output;
}

Push constants

Note

Push constants must be handled through a root signature in D3D.

GLSL

layout (push_constant) uniform <structure-type> { <members> } <name>

Example:

layout (push_constant) uniform PushConsts {
    mat4 matrix;
} pushConsts;

HLSL

[[vk::push_constant]] <structure-type> <name>;
struct PushConsts {
    float4x4 matrix;
};
[[vk::push_constant]] PushConsts pushConsts;

Specialization constants

Note

Specialization constants are only available in Vulkan, D3D doesn’t offer anything similar.

GLSL

layout (constant_id = <specialization-constant-index>) const int <name> = <default-value>;

Example:

layout (constant_id = 0) const int SPEC_CONST = 0;

HLSL

[[vk::constant_id(<specialization-constant-index>)]] const int <name> = <default-value>;

Example:

[[vk::constant_id(0)]] const int SPEC_CONST = 0;

Sub passes

GLSL

layout (input_attachment_index = <input-attachment-index>, binding = <binding-index>) uniform subpassInput <name>;

Example:

layout (input_attachment_index = 0, binding = 0) uniform subpassInput input0;

HLSL

[[vk::input_attachment_index(<input-attachment-index>)]][[vk::binding(<binding-index>)]] SubpassInput <name>;

Example:

[[vk::input_attachment_index(0)]][[vk::binding(0)]] SubpassInput input0;

Texture reads

Note

Where GLSL uses global functions to access images, HLSL uses member functions of the texture object.

Example:

GLSL:

layout (binding = 0, set = 0) uniform sampler2D sampler0;

void main() {
    vec4 color = texture(sampler0, inUV);
}

HLSL:

Texture2D texture0 : register(t0, space0);
SamplerState sampler0 : register(s0, space0);

float4 main(VSOutput input) : SV_TARGET {
    float4 color = texture0.Sample(sampler0, input.UV);
}
GLSL HLSL

texture

Sample

textureGrad

SampleGrad

textureLod

SampleLevel

textureSize

GetDimensions

textureProj

n.a., requires manual perspective divide

texelFetch

Load

sparseTexelsResidentARB

CheckAccessFullyMapped

Note

Slang supports the same texture operations as HLSL, with identical function names and behavior.

Slang also supports more advanced texture operations through its parameter block system:

// Define a parameter block with textures
struct TextureResources
{
    Texture2D albedoMap;
    Texture2D normalMap;
    Texture2D roughnessMap;
    SamplerState samplerState;
}

// Declare a parameter block
[[vk::binding(0, 0)]]
ParameterBlock<TextureResources> textures;

// Sample textures through the parameter block
float4 sampleAlbedo(float2 uv)
{
    return textures.albedoMap.Sample(textures.samplerState, uv);
}

Image formats

GLSL

layout (set = <set-index>, binding = <image-binding-index>, <image-format>) uniform <memory-qualifier> <image-type> <name>;

Example:

layout (set = 0, binding = 0, rgba8) uniform writeonly image2D outputImage;

HLSL

[[vk::image_format(<image-format>)]]
RWTexture2D<image-components> <name> : register(<register-type><binding-index>, space<set-index>);

Example:

[[vk::image_format("rgba8")]]
RWTexture2D<float4> resultImage : register(u0, space0);

Built-ins and functions mapping

Buffer device address

Note

Currently, HLSL only supports a subset of VK_KHR_buffer_device_address.

GLSL

Example:

layout(push_constant) uniform PushConstants {
    uint64_t bufferAddress;
} pushConstants;

layout(buffer_reference, scalar) buffer Data {vec4 f[]; };

void main() {
    Data data = Data(pushConstants.bufferAddress);
}

HLSL

Example:

struct PushConstants {
    uint64_t bufferAddress;
};
[[vk::push_constant]] PushConstants pushConstants;

void main() {
    float4 data = vk::RawBufferLoad<float4>(pushConstants.bufferAddress);
}

Raytracing

Shader stage selection

While GLSL implicitly detects the shader stage (for raytracing) via file extension (or explicitly via compiler arguments), for HLSL raytracing shaders need to be marked by the [shader("stage")] semantic:

Example:

[shader("closesthit")]
void main(inout RayPayload rayPayload, in float2 attribs) {
}

Stage names match GLSL: raygeneration, intersection, anyhit, closesthit, miss, callable

Shader record buffer

GLSL

Example:

layout(shaderRecordEXT, std430) buffer SBT {
    float data;
};

HLSL

Example:

struct SBT {
    float data;
};
[[vk::shader_record_ext]]
ConstantBuffer<SBT> sbt;

Built-Ins

GLSL HLSL Note

accelerationStructureEXT

RaytracingAccelerationStructure

executeCallableEXT

CallShader

ignoreIntersectionEXT

IgnoreHit

reportIntersectionEXT

ReportHit

terminateRayEXT

AcceptHitAndEndSearch

traceRayEXT

TraceRay

rayPayloadEXT (storage qualifier)

Last argument of TraceRay

rayPayloadInEXT (storage qualifier)

First argument for main entry of any hit, closest hit and miss stage

hitAttributeEXT (storage qualifier)

Last argument of ReportHit

callableDataEXT (storage qualifier)

Last argument of CallShader

callableDataInEXT (storage qualifier)

First argument for main entry of callabe stage

gl_LaunchIDEXT

DispatchRaysIndex

gl_LaunchSizeEXT

DispatchRaysDimensions

gl_PrimitiveID

PrimitiveIndex

gl_InstanceID

InstanceIndex

gl_InstanceCustomIndexEXT

InstanceID

gl_GeometryIndexEXT

GeometryIndex

gl_VertexIndex

SV_VertexID

gl_WorldRayOriginEXT

WorldRayOrigin

gl_WorldRayDirectionEXT

WorldRayDirection

gl_ObjectRayOriginEXT

ObjectRayOrigin

gl_ObjectRayDirectionEXT

ObjectRayDirection

gl_RayTminEXT

RayTMin

gl_RayTmaxEXT

RayTCurrent

gl_IncomingRayFlagsEXT

RayFlags

gl_HitTEXT

RayTCurrent

gl_HitKindEXT

HitKind

gl_ObjectToWorldEXT

ObjectToWorld4x3

gl_WorldToObjectEXT

WorldToObject4x3

gl_WorldToObject3x4EXT

WorldToObject3x4

gl_ObjectToWorld3x4EXT

ObjectToWorld3x4

gl_RayFlagsNoneEXT

RAY_FLAG_NONE

gl_RayFlagsOpaqueEXT

RAY_FLAG_FORCE_OPAQUE

gl_RayFlagsNoOpaqueEXT

RAY_FLAG_FORCE_NON_OPAQUE

gl_RayFlagsTerminateOnFirstHitEXT

RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH

gl_RayFlagsSkipClosestHitShaderEXT

RAY_FLAG_SKIP_CLOSEST_HIT_SHADER

gl_RayFlagsCullBackFacingTrianglesEXT

RAY_FLAG_CULL_BACK_FACING_TRIANGLES

gl_RayFlagsCullFrontFacingTrianglesEXT

RAY_FLAG_CULL_FRONT_FACING_TRIANGLES

gl_RayFlagsCullOpaqueEXT

RAY_FLAG_CULL_OPAQUE

gl_RayFlagsCullNoOpaqueEXT

RAY_FLAG_CULL_NON_OPAQUE

requires GL_EXT_ray_flags_primitive_culling

gl_RayFlagsSkipTrianglesEXT

RAY_FLAG_SKIP_TRIANGLES

requires GL_EXT_ray_flags_primitive_culling

gl_RayFlagsSkipAABBEXT

RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES

gl_HitKindFrontFacingTriangleEXT

HIT_KIND_TRIANGLE_FRONT_FACE

gl_HitKindBackFacingTriangleEXT

HIT_KIND_TRIANGLE_BACK_FACE

gl_HitTriangleVertexPositionsEXT

Requires SPIR-V intrinsics:

[[vk::ext_extension("SPV_KHR_ray_tracing_position_fetch")]]
[[vk::ext_capability(RayTracingPositionFetchKHR)]]
[[vk::ext_builtin_input(HitTriangleVertexPositionsKHR)]]

Requires GL_EXT_ray_tracing_position_fetch

shadercallcoherent

n.a.

Note

Slang supports the same raytracing built-ins as HLSL, with identical function names and behavior. Additionally, Slang’s interface and module system can be used to create more modular and reusable raytracing code.

// Define a ray tracing interface
interface IRayTracer
{
    struct RayPayload
    {
        float3 color;
        float distance;
        int materialID;
    };

    void traceScene(inout RayPayload payload, float3 origin, float3 direction);
}

// Implement the interface
struct StandardRayTracer : IRayTracer
{
    RaytracingAccelerationStructure accelerationStructure;

    void traceScene(inout IRayTracer.RayPayload payload, float3 origin, float3 direction)
    {
        // Configure ray
        uint rayFlags = RAY_FLAG_NONE;
        uint instanceMask = 0xFF;
        uint rayContributionToHitGroupIndex = 0;
        uint multiplierForGeometryContributionToHitGroupIndex = 1;
        uint missShaderIndex = 0;

        // Trace ray
        TraceRay(
            accelerationStructure,
            rayFlags,
            instanceMask,
            rayContributionToHitGroupIndex,
            multiplierForGeometryContributionToHitGroupIndex,
            missShaderIndex,
            origin,
            0.001f,    // Min t
            direction,
            10000.0f,  // Max t
            payload);
    }
}

Compute

Local workgroup size

GLSL
layout (local_size_x = <local-size-x>, local_size_y = <local-size-y>, local_size_z = <local-size-z>) in;

Example:

layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
HLSL
[numthreads(<local-size-x>, <local-size-y>, <local-size-z>)]

Example:

[numthreads(1, 1, 1)]
void main() {}

Shared memory

GLSL

Example:

shared vec4 sharedData[1024];
HLSL

Example:

groupshared float4 sharedData[1024];

Built-Ins

GLSL HLSL

gl_GlobalInvocationID

SV_DispatchThreadID

gl_LocalInvocationID

SV_GroupThreadID

gl_WorkGroupID

SV_GroupID

gl_LocalInvocationIndex

SV_GroupIndex

gl_NumWorkGroups

n.a.

gl_WorkGroupSize

n.a.

Note

Slang supports the same compute shader semantics as HLSL, with identical names and behavior. Additionally, Slang’s interface and module system can be used to create more modular and reusable compute shader code.

// Define a compute kernel interface
interface IComputeKernel
{
    void execute(uint3 globalID, uint3 groupID, uint3 localID);
}

// Implement a specific compute kernel
struct ImageProcessingKernel : IComputeKernel
{
    RWTexture2D<float4> outputImage;
    Texture2D<float4> inputImage;
    SamplerState samplerState;

    void execute(uint3 globalID, uint3 groupID, uint3 localID)
    {
        // Process image at the current pixel
        uint2 pixelCoord = globalID.xy;
        float2 uv = float2(pixelCoord) / float2(1920, 1080); // Example resolution

        // Sample input and write to output
        float4 color = inputImage.Sample(samplerState, uv);
        outputImage[pixelCoord] = color;
    }
}

// Main compute shader entry point
[numthreads(16, 16, 1)]
void main(
    uint3 dispatchThreadID : SV_DispatchThreadID,
    uint3 groupID : SV_GroupID,
    uint3 groupThreadID : SV_GroupThreadID)
{
    // Create and use the kernel
    ImageProcessingKernel kernel;
    kernel.execute(dispatchThreadID, groupID, groupThreadID);
}

Barriers

Example:

GLSL:

barrier();
for (int j = 0; j < 256; j++) {
    doSomething;
}
barrier();

HLSL:

GroupMemoryBarrierWithGroupSync();
for (int j = 0; j < 256; j++) {
    doSomething;
}
GroupMemoryBarrierWithGroupSync();
Note

Barriers heavily differ between GLSL and HLSL. Some HLSL barriers don’t have direct mapping to GLSL (such functions are in italics, and GLSL barriers have been used for them as accurately as possible).

GLSL

HLSL

memoryBarrierShared

GroupMemoryBarrier

barrier

GroupMemoryBarrierWithGroupSync

memoryBarrierImage + memoryBarrierBuffer

DeviceMemoryBarrier

memoryBarrierImage + memoryBarrierBuffer + barrier

DeviceMemoryBarrierWithGroupSync

memoryBarrier + barrier

AllMemoryBarrierWithGroupSync

memoryBarrier

AllMemoryBarrier

Note

barrier implicitly sets a memory barrier for shared/groupshared memory. Roughly speaking, the barrier contains the memoryBarrierShared/GroupMemoryBarrier.

Mesh, task (amplification) and geometry shaders

These shader stages share several functions and built-ins

GLSL HLSL

EmitMeshTasksEXT

DispatchMesh

SetMeshOutputsEXT

SetMeshOutputCounts

EmitVertex

StreamType<Name>.Append (e.g. {TriangleStream<MSOutput>})

EndPrimitive

StreamType<Name>.RestartStrip

gl_PrimitiveShadingRateEXT

SV_ShadingRate

gl_CullPrimitiveEXT

SV_CullPrimitive

gl_in

Array argument for main entry (e.g. {triangle VSInput input[3]})

Tessellation shaders

GLSL HLSL

gl_InvocationID

SV_OutputControlPointID

gl_TessLevelInner

SV_InsideTessFactor

gl_TessLevelOuter

SV_TessFactor

gl_TessCoord

SV_DomainLocation

Subgroups

GLSL HLSL

gl_HelperInvocation

WaveIsHelperLane

n.a.

WaveOnce

readFirstInvocationARB

WaveReadFirstLane

readInvocationARB

WaveReadLaneAt

anyInvocationARB

WaveAnyTrue

allInvocationsARB

WaveAllTrue

allInvocationsEqualARB

WaveAllEqual

ballotARB

WaveBallot

gl_NumSubgroups

NumSubgroups decorated OpVariable

gl_SubgroupID

SubgroupId decorated OpVariable

gl_SubgroupSize

WaveGetLaneCount

gl_SubgroupInvocationID

WaveGetLaneIndex

gl_SubgroupEqMask

n.a.

gl_SubgroupGeMask

n.a.

gl_SubgroupGtMask

n.a.

gl_SubgroupLeMask

n.a.

gl_SubgroupLtMask

SubgroupLtMask decorated OpVariable

subgroupElect

WaveIsFirstLane

subgroupAny

WaveActiveAnyTrue

subgroupAll

WaveActiveAllTrue

subgroupBallot

WaveActiveBallot

subgroupAllEqual

WaveActiveAllEqual

subgroupBallotBitCount

WaveActiveCountBits

subgroupAnd

WaveActiveBitAdd

subgroupOr

WaveActiveBitOr

subgroupXor

WaveActiveBitXor

subgroupAdd

WaveActiveSum

subgroupMul

WaveActiveProduct

subgroupMin

WaveActiveMin

subgroupMax

WaveActiveMax

subgroupExclusiveAdd

WavePrefixSum

subgroupExclusiveMul

WavePrefixProduct

subgroupBallotExclusiveBitCount

WavePrefixCountBits

subgroupBroadcast

WaveReadLaneAt

subgroupBroadcastFirst

WaveReadLaneFirst

subgroupQuadSwapHorizontal

QuadReadAcrossX

subgroupQuadSwapVertical

QuadReadAcrossY

subgroupQuadSwapDiagonal

QuadReadAcrossDiagonal

subgroupQuadBroadcast

QuadReadLaneAt

Misc

GLSL HLSL Note

gl_PointSize

[[vk::builtin("PointSize")]]

Vulkan only, no direct HLSL equivalent

gl_BaseVertexARB

[[vk::builtin("BaseVertex")]]

Vulkan only, no direct HLSL equivalent

gl_BaseInstanceARB

[[vk::builtin("BaseInstance")]]

Vulkan only, no direct HLSL equivalent

gl_DrawID

[[vk::builtin("DrawIndex")]]

Vulkan only, no direct HLSL equivalent

gl_DeviceIndex

[[vk::builtin("DeviceIndex")]]

Vulkan only, no direct HLSL equivalent

gl_ViewportMask

[[vk::builtin("ViewportMaskNV")]]

Vulkan only, no direct HLSL equivalent

gl_FragCoord

SV_Position

gl_FragDepth

SV_Depth

gl_FrontFacing

SV_IsFrontFace

gl_InstanceIndex

SV_InstanceID

gl_ViewIndex

SV_ViewID

gl_ClipDistance

SV_ClipDistance

gl_CullDistance

SV_CullDistance

gl_PointCoord

SV_Position

gl_Position

SV_Position

gl_PrimitiveID

SV_PrimitiveID

gl_ViewportIndex

SV_ViewportArrayIndex

gl_Layer

SV_RenderTargetArrayIndex

gl_SampleID

SV_SampleIndex

gl_SamplePosition

EvaluateAttributeAtSample

subpassLoad

<SubPassInput>.SubpassLoad

imageLoad

RWTexture1D/2D/3D<T>[]

imageStore

RWTexture1D/2D/3D<T>[]

atomicAdd

InterlockedAdd

atomicCompSwap

InterlockedCompareExchange

imageAtomicExchange

InterlockedExchange

nonuniformEXT

NonUniformResourceIndex

gl_BaryCoordEXT

SV_Barycentrics

gl_BaryCoordNoPerspEXT

SV_Barycentrics with noperspective

Slang Modules and Imports

One of Slang’s most distinctive features is its module system, which allows for better code organization and reuse. This feature is not available in either GLSL or standard HLSL.

Module Declaration

In Slang, you can organize code into modules:

// File: Lighting.slang
module Lighting;

// Public functions must be marked with 'public'
public float3 calculateDirectLighting(float3 normal, float3 lightDir, float3 color)
{
    float NdotL = max(0, dot(normal, lightDir));
    return color * NdotL;
}

// Private function (not exported)
float calculateAttenuation(float distance)
{
    return 1.0 / (distance * distance);
}

Importing Modules

You can import modules to use their exported functionality:

// File: Fragment.slang
module Fragment;

// Import another module
import Lighting;

[shader("fragment")]
float4 fragmentMain(float3 normal : NORMAL, float3 worldPos : POSITION) : SV_TARGET
{
    float3 lightDir = normalize(float3(1, 1, 1));
    float3 lightColor = float3(1, 1, 1);

    // Use function from imported module - modules don't introduce namespaces
    float3 directLighting = calculateDirectLighting(normal, lightDir, lightColor);

    return float4(directLighting, 1.0);
}

Hierarchical Modules

Slang supports hierarchical module organization:

// File: Rendering/Materials/PBR.slang
module Rendering.Materials.PBR;

public interface IMaterial { ... }
public struct PBRMaterial : IMaterial { ... }

// File: Main.slang
module Main;

// Import specific module
import Rendering.Materials.PBR;

// Use imported types
PBR::PBRMaterial material;

Module Benefits

The module system offers several advantages:

  • Better code organization

  • Encapsulation of implementation details

  • Reduced name conflicts

  • Explicit dependencies between components

  • Improved compilation times through separate compilation

Functions

Note

Most GLSL functions are also available in HLSL and vice-versa. This chapter lists functions with divergent names. Functions that have a 1:1 counterpart (e.g. isNan) aren’t listed.

GLSL HLSL

dFdx

ddx

dFdxCoarse

ddx_coarse

dFdxFine

ddx_fine

dFdy

ddy

dFdyCoarse

ddy_coarse

dFdyFine

ddy_fine

fma

mad

fract

frac

mix

lerp

Note

Slang supports all the same intrinsic functions as HLSL, with identical names. Additionally, Slang allows you to define your own generic functions that can work with multiple types and supports operator overloading and custom function definitions within interfaces.

Here’s an example of Slang’s generic functions:

// Generic interpolation function
T interpolate<T>(T a, T b, float t)
{
    return lerp(a, b, t);
}

// Usage with different types
float result1 = interpolate(1.0f, 2.0f, 0.5f);          // Returns 1.5
float3 result2 = interpolate(float3(1,0,0), float3(0,1,0), 0.5f); // Returns (0.5, 0.5, 0)

Slang also supports operator overloading and custom function definitions within interfaces, allowing for more expressive and reusable code:

// Define an interface with operations
interface IVector<T>
{
    T dot(T other);
    T normalize();
    T scale(float factor);
}

// Implement for float3
struct Float3Vector : IVector<float3>
{
    float3 value;

    float3 dot(float3 other)
    {
        return dot(value, other);
    }

    float3 normalize()
    {
        return normalize(value);
    }

    float3 scale(float factor)
    {
        return value * factor;
    }
}