diff --git a/chapters/high_level_shader_language_comparison.adoc b/chapters/high_level_shader_language_comparison.adoc new file mode 100644 index 00000000..aa41a76e --- /dev/null +++ b/chapters/high_level_shader_language_comparison.adoc @@ -0,0 +1,971 @@ +// Copyright 2024 Sascha Willems +// SPDX-License-Identifier: CC-BY-4.0 + +ifndef::chapters[:chapters:] +ifndef::images[:images: images/] + +[[shader-decoder-ring]] += Vulkan High Level Shader Language Comparison +:toc: + +While Vulkan itself consumes shaders in a binary format called xref:{chapters}what_is_spirv.adoc[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 and HLSL. 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 xref:{chapters}hlsl.adoc[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 GLSL and HLSL. E.g. there are no semantic in GLSL while some newer GLSL functionality may not (yet) be available in HLSL. +==== + +== Extensions + +In GLSL extensions need to be explicitly enabled using the `#extension` directive. This is **not** necessary in HLSL. The compiler will implicitly select suitable SPIR-V extensions based on the shader. If required one can use `-fspv-extension` arguments to explicitly select extensions. + +== Data types + +[NOTE] +==== +Types work similar in GLSL and HLSL. But where GLSL e.g. has explicit vector or matrix types, HLSL uses basic types. On the other hand HLSL offers advanced type features like C++ templates. This paragraph contains a basic summary with some examples to show type differences between the two languages. +==== + +[options="header"] +|==== +| *GLSL* | *HLSL* | *Example* +| vec__n__ | float__n__ | vec4 -> float4 +| ivec__n__ | int__n__ | ivec3 --> int3 +| mat__nxm__ or shorthand mat__n__ | float__nxm__ | mat4 -> float4x4 +|==== + +* link:https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-data-types[HLSL data types (Microsoft)] +* link:https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)[GLSL data types (OpenGL wiki)] + +The syntax for casting types also differs: + +GLSL: +[source,glsl] +---- +mat4x3 mat = mat4x3(ubo.view); +---- + +HLSL: +[source,hlsl] +---- +float4x3 mat = (float4x3)(ubo.view); +---- + +[NOTE] +==== +An imporant difference: Matrices in GLSL are column-major, while matrices in HLSL are row-major. This affects things like matrix construction. +==== + +== 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. HLSL instead uses link:https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics[semantics], strings that are attached to inputs or inputs that contain information about the intended use of that variable. They are prefixed with `SV_`. For HLSL input values are explicit arguments for the main entry point and the shader needs to explicitly return an output. +==== + +=== Examples + +Writing positions from the vertex shader: + +GLSL: +[source,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 +[source,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; +} +---- + +Reading the vertex index: + +GLSL: +[source,glsl] +---- +void main() +{ + // The vertex index is stored in the gl_VertexIndex built-in + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); +} +---- + +HLSL +[source,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; +} +---- + +== Shader interface + +[NOTE] +==== +Shader interfaces greatly differ between GLSL and HLSL. Vulkan concepts not directly available in HLSL use the link:https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#the-implicit-vk-namespace)[implicit vk namespace]. +==== + +=== Descriptor bindings + +==== GLSL + +[source,glsl] +---- +layout (set = , binding = ) uniform +---- + +There are two options for defining descriptor set and binding indices in HLSL when using Vulkan. + +==== HLSL way + +[source,hlsl] +---- + : register(, space) +---- + +Using this syntax, descriptor set and binding indices will be implicitly assigned from the set and binding index. + +==== Vulkan namespace + +[source,hlsl] +---- +[[vk::binding(binding-index, set-index)]] + +---- + +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 + +[source, glsl] +---- +layout (set = 1, binding = 0) uniform Node { + mat4 matrix; +} node; +---- + +===== HLSL +---- +struct Node { + float4x4 transform; +}; + +// HLSL style +ConstantBuffer node : register(b0, space1); + +// Vulkan style +[[vk::binding(0, 1)]] +ConstantBuffer node; + +// Combined +[[vk::binding(0, 1)]] +ConstantBuffer node : register(b0, space1); +---- + +=== Uniforms + +==== GLSL + +[source,glsl] +---- +layout (set = , binding = ) uniform +---- + +Examples: +[source,glsl] +---- +// Uniform buffer +layout (set = 0, binding = 0) uniform UBO +{ + mat4 projection; +} ubo; + +// Combined image sampler +layout (set = 0, binding = 1) uniform sampler2D samplerColor; +---- + +==== HLSL + +[source,hlsl] +---- + : register(, space) +---- +or +[source,hlsl] +---- +[[vk::binding(binding-index, set-index)]] + +---- + +Examples: +[source,hlsl] +---- +// Uniform buffer +struct UBO +{ + float4x4 projection; +}; +ConstantBuffer ubo : register(b0, space0); + +// Combined image sampler +Texture2D textureColor : register(t1); +SamplerState samplerColor : register(s1); +---- + +If using the HLSL descriptor binding syntax `++` can be: + +[options="header"] +|==== +| *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 + +[source,glsl] +---- +layout (location = ) in ; +---- + +Example: +[source,glsl] +---- +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 + +[source,hlsl] +---- +[[vk::location()]] : ; +---- + +Example: +[source,hlsl] +---- +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) { +} +---- + +// @todo: add general note on input semantics, e.g. for other stuff like compute where you need to use input semantics instead of built-ins + +`++` can be +[options="header"] +|==== +| *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. + +link:https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics[source] + +=== Shader outputs + +==== Passing data between stages + +E.g. for vertex and tessellations shaders. + +===== GLSL + +[source,glsl] +---- +layout (location = ) out/in ; +---- + +Example: +[source,glsl] +---- +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 + +[source,hlsl] +---- +[[vk::location()]] : ; +---- + +Example: +[source,hlsl] +---- +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 + +[source,glsl] +---- +layout (location = ) out/in ; +---- + +Example: +[source,glsl] +---- +layout (location = 0) out vec4 outPosition; +layout (location = 1) out vec4 outNormal; +layout (location = 2) out vec4 outAlbedo; + +void main() { + outPosition = ... + outNormal = ... + outAlbedo = ... +} +---- + +===== HLSL + +[source,hlsl] +---- + : SV_TARGET; +---- + +Example: +[source,hlsl] +---- +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 + +[source,glsl] +---- +layout (push_constant) uniform { } +---- + +Example: +[source,glsl] +---- +layout (push_constant) uniform PushConsts { + mat4 matrix; +} pushConsts; +---- + +==== HLSL + +[source,hlsl] +---- +[[vk::push_constant]] ; +---- + +[source,hlsl] +---- +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 + +[source,glsl] +---- +layout (constant_id = ) const int = ; +---- + +Example: +[source,glsl] +---- +layout (constant_id = 0) const int SPEC_CONST = 0; +---- + +==== HLSL + +[source,hlsl] +---- +[[vk::constant_id()]] const int = ; +---- + +Example: +[source,hlsl] +---- +[[vk::constant_id(0)]] const int SPEC_CONST = 0; +---- + +=== Sub passes + +==== GLSL + +[source,glsl] +---- +layout (input_attachment_index = , binding = ) uniform subpassInput ; +---- + +Example: +[source,glsl] +---- +layout (input_attachment_index = 0, binding = 0) uniform subpassInput input0; +---- + +==== HLSL + +[source,hlsl] +---- +[[vk::input_attachment_index()]][[vk::binding()]] SubpassInput ; +---- + +Example: +[source,hlsl] +---- +[[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: +[source,glsl] +---- +layout (binding = 0, set = 0) uniform sampler2D sampler0; + +void main() { + vec4 color = texture(sampler0, inUV); +} +---- + +HLSL: +[source,hlsl] +---- +Texture2D texture0 : register(t0, space0); +SamplerState sampler0 : register(s0, space0); + +float4 main(VSOutput input) : SV_TARGET { + float4 color = texture0.Sample(sampler0, input.UV); +} +---- + +[options="header"] +|==== +| *GLSL* | *HLSL* +| texture | Sample +| textureGrad | SampleGrad +| textureLod | SampleLevel +| textureSize | GetDimensions +| textureProj | n.a., requires manual perspective divide +| texelFetch | Load +| sparseTexelsResidentARB | SampleLevel +|==== + +=== Image formats + +==== GLSL + +[source,glsl] +---- +layout (set = , binding = , ) uniform ; +---- + +Example: +[source,glsl] +---- +layout (set = 0, binding = 0, rgba8) uniform writeonly image2D outputImage; +---- + +==== HLSL + +[source,hlsl] +---- +[[vk::image_format()]] +RWTexture2D : register(, space); +---- + +Example: +[source,hlsl] +---- +[[vk::image_format("rgba8")]] +RWTexture2D resultImage : register(u0, space0); +---- + +== Built-ins and functions mapping +// @todo: change caption or maybe remove completely + +=== Buffer device address + +[NOTE] +==== +Currently, HLSL only supports a link:https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#rawbufferload-and-rawbufferstore[subset] of VK_KHR_buffer_device_address. +==== + +==== GLSL + +Example: +[source,glsl] +---- +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: +[source,hlsl] +---- +struct PushConstants { + uint64_t bufferAddress; +}; +[[vk::push_constant]] PushConstants pushConstants; + +void main() { + float4 data = vk::RawBufferLoad(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: +[source,hlsl] +---- +[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: +[source,glsl] +---- +layout(shaderRecordEXT, std430) buffer SBT { + float data; +}; +---- + +==== HLSL + +Example: +[source,hlsl] +---- +struct SBT { + float data; +}; +[[vk::shader_record_ext]] +ConstantBuffer sbt; +---- + +==== Built-Ins + +// @todo: some of the stuff in here is used across different stages (e.g. gl_PrimitiveID) +[options="header"] +|==== +| *GLSL* | *HLSL* +| 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 | AY_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 +| @todo | RAY_FLAG_SKIP_TRIANGLES +| @todo | RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES +| gl_HitKindFrontFacingTriangleEXT | HIT_KIND_TRIANGLE_FRONT_FACE +| gl_HitKindBackFacingTriangleEXT | HIT_KIND_TRIANGLE_BACK_FACE +| shadercallcoherent | @todo +|==== + +=== Compute + +==== Local workgroup size + +===== GLSL + +[source,glsl] +---- +layout (local_size_x = , local_size_y = , local_size_z = ) in; +---- + +Example: +[source,glsl] +---- +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +---- + +===== HLSL + +[source,hlsl] +---- +[numthreads(, , )] +---- + +Example: +[source,hlsl] +---- +[numthreads(1, 1, 1)] +void main() {} +---- + +==== Shared memory + +===== GLSL + +Example: +[source,glsl] +---- +shared vec4 sharedData[1024]; +---- + +===== HLSL + +Example: +[source,hlsl] +---- +groupshared float4 sharedData[1024]; +---- + +==== Built-Ins + +[options="header"] +|==== +| *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. +|==== + +==== Barriers + +[NOTE] +==== +Barriers heavily differ between GLSL and HLSL. With one exception there is no direct mapping. To match HLSL in GLSL you often need to call multiple different barrier types in glsl. +==== + +Example: + +GLSL: +[source,glsl] +---- +groupMemoryBarrier; +barrier; +for (int j = 0; j < 256; j++) { + doSomething; +} +groupMemoryBarrier; +barrier; +---- + +HLSL: +[source,hlsl] +---- +GroupMemoryBarrierWithGroupSync; +for (int j = 0; j < 256; j++) { + doSomething; +} +GroupMemoryBarrierWithGroupSync; +---- + +|==== +| *GLSL* | *HLSL* +| groupMemoryBarrier | GroupMemoryBarrier +| groupMemoryBarrier + barrier | GroupMemoryBarrierWithGroupSync +| memoryBarrier + memoryBarrierImage + memoryBarrierImage | DeviceMemoryBarrier +| memoryBarrier + memoryBarrierImage + memoryBarrierImage + barrier | DeviceMemoryBarrierWithGroupSync +| All above barriers + barrier | AllMemoryBarrierWithGroupSync +| All above barriers | AllMemoryBarrier +| memoryBarrierShared (only) | n.a. +|==== + +=== Mesh, task (amplification) and geometry shaders + +These shader stages share several functions and built-ins + +[options="header"] +|==== +| *GLSL* | *HLSL* +| EmitMeshTasksEXT | DispatchMesh +| SetMeshOutputsEXT | SetMeshOutputCounts +| EmitVertex | __StreamType__<__Name__>.Append (e.g. +{TriangleStream}+) +| EndPrimitive | __StreamType__<__Name__>.RestartStrip +// @todo: check these +| gl_PrimitiveShadingRateEXT | SV_ShadingRate +| gl_CullPrimitiveEXT | SV_CullPrimitive +| gl_in | Array argument for main entry (e.g. +{triangle VSInput input[3]}+) +|==== + +=== Tessellation shaders + +[options="header"] +|==== +| *GLSL* | *HLSL* +| gl_InvocationID | SV_OutputControlPointID +| gl_TessLevelInner | SV_InsideTessFactor +| gl_TessLevelOuter | SV_TessFactor +| gl_TessCoord | SV_DomainLocation +|==== + +=== Subgroups +// @todo: not sure, maybe rename or split into others +[options="header"] +|==== +| *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 OpVariablen.a. +| WaveIsFirstLane | subgroupElect +| WaveActiveAnyTrue | subgroupAny +| WaveActiveAllTrue | subgroupAll +| WaveActiveBallot | subgroupBallot +| WaveActiveAllEqual | subgroupAllEqual +| WaveActiveCountBits | subgroupBallotBitCount +| WaveActiveBitAdd | subgroupAnd +| WaveActiveBitOr | subgroupOr +| WaveActiveBitXor | subgroupXor +| WaveActiveSum | subgroupAdd +| WaveActiveProduct | subgroupMul +| WaveActiveMin | subgroupMin +| WaveActiveMax | subgroupMax +| WavePrefixSum | subgroupExclusiveAdd +| WavePrefixProduct | subgroupExclusiveMul +| WavePrefixCountBits | subgroupBallotExclusiveBitCount +| WaveReadLaneAt | subgroupBroadcast +| WaveReadLaneFirst | subgroupBroadcastFirst +| QuadReadAcrossX | subgroupQuadSwapHorizontal +| QuadReadAcrossY | subgroupQuadSwapVertical +| QuadReadAcrossDiagonal | subgroupQuadSwapDiagonal +| QuadReadLaneAt | subgroupQuadBroadcast +|==== + +=== Misc +// @todo: rename, split +[options="header"] +|==== +| *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("PoBaseInstanceintSize")]] | 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_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 | .SubpassLoad | +| imageLoad | RWTexture1D/2D/3D[] | +| imageStore | RWTexture1D/2D/3D[] | +| atomicAdd | InterlockedAdd | +| atomicCompSwap | InterlockedCompareExchange | +| imageAtomicExchange | InterlockedExchange | +| nonuniformEXT | NonUniformResourceIndex | +| gl_BaryCoordEXT | SV_Barycentrics | +| gl_BaryCoordNoPerspEXT | SV_Barycentrics with noperspective | +|==== + +== 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. +==== + +[options="header"] +|==== +| *GLSL* | *HLSL* +| clamp | saturate +| dFdx | ddx +| dFdxCoarse | ddx_coarse +| DFdxFine | ddx_fine +| dFdy | ddy +| dFdyCoarse | ddy_coarse +| DFdyFine | ddy_fine +| fma | mad +| fract | frac +| mix | lerp +|==== + +* link:https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions[HLSL intrinsic function (Microsoft)] +* link:https://registry.khronos.org/OpenGL-Refpages/gl4/index.php[OpenGL reference pages]