Skip to content
Open
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
f1c8645
Allow SV_PrimitiveID in ray tracing hit stages
jkwak-work May 28, 2026
9a617f4
Support SV_PrimitiveID for DXIL ray tracing
jkwak-work May 28, 2026
70debcd
Fix HLSL legalization formatting
jkwak-work May 28, 2026
85865f8
Address HLSL legalization review feedback
jkwak-work May 29, 2026
9425abd
Support SV_PrimitiveID via GLSL SPIR-V
jkwak-work May 29, 2026
b85c65b
Address ray tracing primitive ID review
jkwak-work Jun 2, 2026
240a14a
Address upstream primitive ID review
jkwak-work Jun 2, 2026
9d2552d
Add primitive ID coverage tests
jkwak-work Jun 2, 2026
a61125a
Add primitive ID non-input diagnostics
jkwak-work Jun 2, 2026
d95badf
Preserve ray tracing GLSL parameter legalization
jkwak-work Jun 2, 2026
b5275bc
Support primitive ID hit params on CUDA
jkwak-work Jun 2, 2026
9da9ce2
Preserve primitive ID borrow replacements
jkwak-work Jun 2, 2026
7b64847
Address primitive ID test review
jkwak-work Jun 2, 2026
8d2b981
Add primitive ID coverage tests
jkwak-work Jun 2, 2026
213e0f7
Consolidate primitive ID lowering
jkwak-work Jun 4, 2026
cfeda0b
Canonicalize ray tracing primitive ID for GLSL
jkwak-work Jun 4, 2026
49e16ab
Address primitive ID legalization review
jkwak-work Jun 4, 2026
1225131
Guard primitive ID type conversion
jkwak-work Jun 4, 2026
9fa9f48
Handle pointer primitive ID params
jkwak-work Jun 4, 2026
ad3c6fa
Guard CUDA primitive ID conversion
jkwak-work Jun 4, 2026
cf1ac42
Export language server methods for wasm
jkwak-work Jun 4, 2026
7466afd
Build wasm compiler statically
jkwak-work Jun 4, 2026
33f4896
Test multi-entry primitive ID lowering
jkwak-work Jun 4, 2026
21f415a
Clarify HLSL parameter legalization name
jkwak-work Jun 6, 2026
e8ab0d6
Assert primitive ID legalization preconditions
jkwak-work Jun 6, 2026
de1f740
Reuse varying param traversal for primitive ID
jkwak-work Jun 6, 2026
701c4d4
Fix merge preset and formatting checks
jkwak-work Jun 6, 2026
bd32d06
Stabilize varying parameter entry traversal
jkwak-work Jun 6, 2026
2c5092b
Address ray tracing cleanup feedback
jkwak-work Jun 6, 2026
0035f14
Apply GLSL legalization formatting
jkwak-work Jun 6, 2026
ccc10a4
Legalize PrimitiveID before GLSL consolidation
jkwak-work Jun 6, 2026
742e7a9
Cover GLSL PrimitiveID legalization
jkwak-work Jun 6, 2026
e12ab61
Simplify GLSL entry parameter legalization
jkwak-work Jun 6, 2026
16a2d99
Apply GLSL parameter call formatting
jkwak-work Jun 6, 2026
92b53ac
Add PrimitiveID forwarding coverage
jkwak-work Jun 6, 2026
4f4b6c9
Cache GLSL SPIR-V emission mode
jkwak-work Jun 6, 2026
c18e089
Restore GLSL parameter return style
jkwak-work Jun 6, 2026
5190000
Keep GLSL entry point legalization signature
jkwak-work Jun 7, 2026
34b60d4
Address PrimitiveID review feedback
jkwak-work Jun 9, 2026
2b3d705
Validate PrimitiveID helper lookup
jkwak-work Jun 9, 2026
0de9b7e
Add CUDA PrimitiveID multi-entry test
jkwak-work Jun 9, 2026
5e6c3eb
Add PrimitiveID name-collision target tests
jkwak-work Jun 9, 2026
e398ceb
Add unused PrimitiveID hit-stage tests
jkwak-work Jun 9, 2026
0fc1868
Simplify PrimitiveID legalization helper
jkwak-work Jun 10, 2026
60bd954
Format PrimitiveID helper signature
jkwak-work Jun 10, 2026
ffbf01a
Address GLSL review feedback
jkwak-work Jun 11, 2026
8f222ec
Format GLSL legalize call
jkwak-work Jun 11, 2026
403c538
Fix GLSL ray tracing parameter consolidation
jkwak-work Jun 11, 2026
9c96cb0
Address PrimitiveID review feedback
jkwak-work Jun 12, 2026
403863d
Handle mixed PrimitiveID hit inputs
jkwak-work Jun 12, 2026
87b80de
Address mixed PrimitiveID review nits
jkwak-work Jun 12, 2026
fbc972a
Handle mixed PrimitiveID struct forwarding
jkwak-work Jun 12, 2026
479bc64
Cover nested PrimitiveID hit inputs
jkwak-work Jun 12, 2026
968a0e6
Repair CUDA entry point type after PrimitiveID removal
jkwak-work Jun 12, 2026
f0e623f
Handle nested mixed PrimitiveID hit inputs
jkwak-work Jun 12, 2026
16800db
Preflight PrimitiveID struct replacements
jkwak-work Jun 12, 2026
e185f30
Format PrimitiveID legalization
jkwak-work Jun 12, 2026
5d33c5c
Handle PrimitiveID struct inputs for SPIR-V
jkwak-work Jun 12, 2026
dcd0acd
Assert direct SPIR-V PrimitiveID metadata
jkwak-work Jun 12, 2026
bd7a400
Reuse direct SPIR-V PrimitiveID global
jkwak-work Jun 12, 2026
894214b
Clarify PrimitiveID emitter failure contract
jkwak-work Jun 12, 2026
f9f998c
Apply PrimitiveID legalization formatting
jkwak-work Jun 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions source/slang/core.meta.slang
Original file line number Diff line number Diff line change
Expand Up @@ -5050,6 +5050,9 @@ semantic sv_primitiveid
[require(geometry)]
[require(hull)]
[require(domain)]
[require(intersection)]
[require(anyhit)]
[require(closesthit)]
get : uint;

[require(geometry)]
Expand Down
7 changes: 4 additions & 3 deletions source/slang/slang-emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1638,11 +1638,12 @@ Result linkAndOptimizeIR(
SLANG_PASS(legalizeEmptyRayPayloadsForHLSL);
}

// For DXIL only: unwrap ForceVarIntoRayPayloadStructTemporarily instructions
// (must run before legalizeExistentialTypeLayout removes empty struct parameters)
// For DXIL/HLSL: rewrite parameters that need backend intrinsics or
// temporary structs before existential layout removes empty struct
// parameters.
if (isD3DTarget(targetRequest))
{
SLANG_PASS(legalizeNonStructParameterToStructForHLSL);
SLANG_PASS(legalizeParametersForHLSL);
}

if (requiredLoweringPassSet.existentialTypeLayout)
Expand Down
154 changes: 150 additions & 4 deletions source/slang/slang-ir-glsl-legalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "slang-ir-clone.h"
#include "slang-ir-inst-pass-base.h"
#include "slang-ir-insts.h"
#include "slang-ir-legalize-varying-params.h"
#include "slang-ir-single-return.h"
#include "slang-ir-specialize-function-call.h"
#include "slang-ir-util.h"
Expand Down Expand Up @@ -2999,6 +3000,71 @@ void handleSingleParam(
builder->addDependsOnDecoration(func, globalParam);
}

struct DirectSPIRVPrimitiveIDEmitterContext
{
IRFunc* func = nullptr;
IRType* canonicalValueType = nullptr;
IRGlobalParam* globalParam = nullptr;
};

static IRType* getPrimitiveIDValueTypeForDirectSPIRV(IRType* type)
{
if (auto borrowInParamType = as<IRBorrowInParamType>(type))
return borrowInParamType->getValueType();
if (auto ptrType = as<IRPtrTypeBase>(type))
return ptrType->getValueType();
return type;
}

static IRInst* emitDirectSPIRVPrimitiveIDValue(
IRModule* module,
IRBuilder& builder,
IRType* type,
IRStructField* field,
IRVarLayout* layout,
void* userData)
{
SLANG_RELEASE_ASSERT(layout);

auto semanticDecor = field ? field->getKey()->findDecoration<IRSemanticDecoration>() : nullptr;
auto systemValueAttr = layout->findSystemValueSemanticAttr();
SLANG_RELEASE_ASSERT(
(systemValueAttr &&
systemValueAttr->getName().caseInsensitiveEquals(toSlice("sv_primitiveid"))) ||
(semanticDecor &&
semanticDecor->getSemanticName().caseInsensitiveEquals(toSlice("sv_primitiveid"))));

auto context = static_cast<DirectSPIRVPrimitiveIDEmitterContext*>(userData);
auto valueType = getPrimitiveIDValueTypeForDirectSPIRV(type);
if (context->globalParam)
{
auto value = builder.emitLoad(context->globalParam);
if (isTypeEqual(context->canonicalValueType, valueType))
return value;
return builder.emitCast(valueType, value);
}

auto globalParam = addGlobalParam(
module,
builder.getPtrType(
valueType,
AccessQualifier::Immutable,
AddressSpace::BuiltinInput,
builder.getDefaultBufferLayoutType()));

IRVarLayout::Builder varLayoutBuilder(&builder, layout->getTypeLayout());
varLayoutBuilder.cloneEverythingButOffsetsFrom(layout);
if (!systemValueAttr && semanticDecor)
varLayoutBuilder.setSystemValueSemantic(semanticDecor->getSemanticName(), 0);
builder.addLayoutDecoration(globalParam, varLayoutBuilder.build());
moveValueBefore(globalParam, context->func);
builder.addDependsOnDecoration(context->func, globalParam);

context->canonicalValueType = valueType;
context->globalParam = globalParam;
return builder.emitLoad(globalParam);
Comment thread
jkwak-work marked this conversation as resolved.
}

static void consolidateParameters(GLSLLegalizationContext* context, List<IRParam*>& params)
{
auto builder = context->getBuilder();
Expand Down Expand Up @@ -3077,7 +3143,12 @@ static void consolidateParameters(GLSLLegalizationContext* context, List<IRParam
}

// Consolidate ray tracing parameters for an entry point function
void consolidateRayTracingParameters(GLSLLegalizationContext* context, IRFunc* func)
void consolidateRayTracingParameters(

@csyonghe csyonghe Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I am not sure why we need so many special handling of primitive ID across different places in this file. What makes PrimitiveID so special compared to other builtins, e.g. InstanceID? Why don't we need the special case for InstanceID, DispatchId etc.? The changes seems more like fixes to paper issues rather than principled way to implement the new feature.

We should have some legalization/canonicalization step to turn all uses of primitiveID into a canonical form, so no special handling is needed throughout different places.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Agent] Addressed in cfeda0b. The GLSL/glslang path now canonicalizes ray-tracing hit-stage SV_PrimitiveID parameters to the shared PrimitiveIndex helper before ray-tracing payload/hit-attribute consolidation. That removes the GLSL-only "skip PrimitiveID from consolidation" branch and the later PrimitiveID fall-through in legalizeEntryPointParameterForGLSL; the shared helper now has a GLSL target intrinsic spelling of (gl_PrimitiveID) alongside HLSL/CUDA.

Validation: cmake.exe --build --preset debug --target slangc slang-test; focused slang-test.exe set passed 38/38; manual -emit-spirv-via-glsl compiles for intersection/any-hit/closest-hit all emitted BuiltIn PrimitiveId; git.exe diff --check.

GLSLLegalizationContext* context,
IRFunc* func,
bool legalizePrimitiveIDBeforeConsolidation,
bool legalizePrimitiveIDStructBeforeConsolidation,
RayTracingPrimitiveIDValueEmitter const* primitiveIDStructValueEmitter)
{
auto builder = context->getBuilder();
auto firstBlock = func->getFirstBlock();
Expand All @@ -3088,18 +3159,73 @@ void consolidateRayTracingParameters(GLSLLegalizationContext* context, IRFunc* f
List<IRParam*> outParams;
List<IRParam*> params;

for (auto param = firstBlock->getFirstParam(); param; param = param->getNextParam())
// When requested for SPIR-V via GLSL, canonicalize hit-stage SV_PrimitiveID before
// ray-tracing parameter consolidation rewrites entry-point parameters. This changes
// a Slang entry point parameter:
//
// void main_chit(int primitiveID : SV_PrimitiveID) { use(primitiveID); }
//
// into GLSL-style body code that reads the builtin:
//
// int primitiveID = int(gl_PrimitiveID);
// use(primitiveID);
//
// and removes the original parameter before consolidation sees payload/attributes.
for (auto param = firstBlock->getFirstParam(); param;)
{
auto nextParam = param->getNextParam();
auto paramLayout = findVarLayout(param);
if (!isVaryingParameter(paramLayout))
{
param = nextParam;
continue;
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not make unnecessary change.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Agent] Addressed in ffbf01ab0. I removed the unnecessary standalone insert-position change from the ray-tracing parameter collection path while folding the PrimitiveID handling into the existing consolidation scan.

Validation: git diff --check; cmake --build --preset debug --target slangc; direct slangc checks for main_isect, main_ahit, and main_chit with -target spirv-assembly -emit-spirv-via-glsl verified BuiltIn PrimitiveId, and -target glsl verified gl_PrimitiveID for all three hit stages.

I left this thread unresolved for reviewer confirmation.

builder->setInsertBefore(firstBlock->getFirstOrdinaryInst());

if (legalizePrimitiveIDBeforeConsolidation)
{
bool paramRemoved = false;
if (tryLegalizeRayTracingPrimitiveIDParam(
builder->getModule(),
*builder,
param,
&paramRemoved))
{
if (paramRemoved)
{
param = nextParam;
continue;
}
}
}

if (legalizePrimitiveIDStructBeforeConsolidation)
{
bool paramRemoved = false;
if (tryLegalizeRayTracingPrimitiveIDStructParam(
builder->getModule(),
*builder,
param,
&paramRemoved,
primitiveIDStructValueEmitter))
{
if (paramRemoved)
{
param = nextParam;
continue;
}
}
}

if (as<IROutParamType>(param->getDataType()) ||
as<IRBorrowInOutParamType>(param->getDataType()))
{
outParams.add(param);
}
params.add(param);

param = nextParam;
}

// We don't need consolidation here.
Expand Down Expand Up @@ -4820,6 +4946,7 @@ void legalizeEntryPointForGLSL(
SLANG_ASSERT(entryPointDecor);

auto stage = entryPointDecor->getProfile().getStage();
const bool isViaGLSL = !codeGenContext->getTargetProgram()->shouldEmitSPIRVDirectly();

auto layoutDecoration = func->findDecoration<IRLayoutDecoration>();
SLANG_ASSERT(layoutDecoration);
Expand Down Expand Up @@ -4918,8 +5045,27 @@ void legalizeEntryPointForGLSL(
case Stage::Intersection:
case Stage::Miss:
case Stage::RayGeneration:
consolidateRayTracingParameters(&context, func);
break;
{
DirectSPIRVPrimitiveIDEmitterContext directSPIRVPrimitiveIDEmitterContext;
directSPIRVPrimitiveIDEmitterContext.func = func;

RayTracingPrimitiveIDValueEmitter directSPIRVPrimitiveIDEmitter;
RayTracingPrimitiveIDValueEmitter const* primitiveIDStructValueEmitter = nullptr;
if (isRayTracingHitStage(stage) && !isViaGLSL)
{
directSPIRVPrimitiveIDEmitter.func = emitDirectSPIRVPrimitiveIDValue;
directSPIRVPrimitiveIDEmitter.userData = &directSPIRVPrimitiveIDEmitterContext;
primitiveIDStructValueEmitter = &directSPIRVPrimitiveIDEmitter;
}

consolidateRayTracingParameters(
&context,
func,
isRayTracingHitStage(stage) && isViaGLSL,
isRayTracingHitStage(stage),
primitiveIDStructValueEmitter);
break;
}
default:
break;
}
Expand Down
7 changes: 4 additions & 3 deletions source/slang/slang-ir-hlsl-legalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@

#include "slang-ir-inst-pass-base.h"
#include "slang-ir-insts.h"
#include "slang-ir-legalize-varying-params.h"
#include "slang-ir-specialize-function-call.h"
#include "slang-ir-util.h"
#include "slang-ir.h"

#include <functional>

namespace Slang
{

Expand Down Expand Up @@ -160,7 +159,7 @@ void searchChildrenForForceVarIntoStructTemporarily(IRModule* module, IRInst* in
}
}

void legalizeNonStructParameterToStructForHLSL(IRModule* module)
void legalizeParametersForHLSL(IRModule* module)
{
for (auto globalInst : module->getGlobalInsts())
{
Expand All @@ -170,6 +169,8 @@ void legalizeNonStructParameterToStructForHLSL(IRModule* module)
continue;
searchChildrenForForceVarIntoStructTemporarily(module, globalInst);
}
Comment thread
jkwak-work marked this conversation as resolved.
Comment thread
jkwak-work marked this conversation as resolved.

legalizeRayTracingPrimitiveIDParamsForHLSL(module);
}

void legalizeEmptyRayPayloadsForHLSL(IRModule* module)
Expand Down
2 changes: 1 addition & 1 deletion source/slang/slang-ir-hlsl-legalize.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Session;
struct IRFunc;
struct IRModule;

void legalizeNonStructParameterToStructForHLSL(IRModule* module);
void legalizeParametersForHLSL(IRModule* module);

void legalizeEmptyRayPayloadsForHLSL(IRModule* module);

Expand Down
Loading
Loading