diff options
Diffstat (limited to 'source')
26 files changed, 1501 insertions, 422 deletions
diff --git a/source/slang-glslang/slang-glslang.cpp b/source/slang-glslang/slang-glslang.cpp index 2f5f17e42..ba39d6a12 100644 --- a/source/slang-glslang/slang-glslang.cpp +++ b/source/slang-glslang/slang-glslang.cpp @@ -97,9 +97,16 @@ static int glslang_compileGLSLToSPIRV(glslang_CompileRequest* request) CASE(DOMAIN, TessEvaluation); CASE(COMPUTE, Compute); + CASE(RAY_GENERATION, RayGenNV); + CASE(INTERSECTION, IntersectNV); + CASE(ANY_HIT, AnyHitNV); + CASE(CLOSEST_HIT, ClosestHitNV); + CASE(MISS, MissNV); + #undef CASE default: + dumpDiagnostics(request, "internal error: stage unsupported by glslang\n"); return 1; } diff --git a/source/slang/check.cpp b/source/slang/check.cpp index bddf813f2..f0e7be32b 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -1782,34 +1782,6 @@ namespace Slang } } - Stage findStageByName(String const& name) - { - static const struct - { - char const* name; - Stage stage; - } kStages[] = - { - #define PROFILE_STAGE(ID, NAME, ENUM) \ - { #NAME, Stage::ID }, - - #define PROFILE_STAGE_ALIAS(ID, NAME, VAL) \ - { #NAME, Stage::ID }, - - #include "profile-defs.h" - }; - - for(auto entry : kStages) - { - if(name == entry.name) - { - return entry.stage; - } - } - - return Stage::Unknown; - } - bool hasIntArgs(Attribute* attr, int numArgs) { if (int(attr->args.Count()) != numArgs) @@ -3746,6 +3718,7 @@ namespace Slang // and trying to special case `DeclGroup*` here feels silly. // dispatchDecl(stmt->decl); + checkModifiers(stmt->decl); } void visitBlockStmt(BlockStmt* stmt) diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp index 9c1823f91..7a5501a23 100644 --- a/source/slang/compiler.cpp +++ b/source/slang/compiler.cpp @@ -137,6 +137,35 @@ namespace Slang return Profile::Unknown; } + Stage findStageByName(String const& name) + { + static const struct + { + char const* name; + Stage stage; + } kStages[] = + { + #define PROFILE_STAGE(ID, NAME, ENUM) \ + { #NAME, Stage::ID }, + + #define PROFILE_STAGE_ALIAS(ID, NAME, VAL) \ + { #NAME, Stage::ID }, + + #include "profile-defs.h" + }; + + for(auto entry : kStages) + { + if(name == entry.name) + { + return entry.stage; + } + } + + return Stage::Unknown; + } + + // String emitHLSLForEntryPoint( diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 05ecd5b37..8626a832a 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -935,7 +935,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) // `SampleLevel` - sb << "__target_intrinsic(glsl, \"textureLod($p, $2, $3)\")\n"; + sb << "__target_intrinsic(glsl, \"textureLod($p, $2, $3)$z\")\n"; sb << "T SampleLevel(SamplerState s, "; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "float level);\n"; @@ -1229,3 +1229,9 @@ attribute_syntax [earlydepthstencil] : EarlyDepthStencilAttribute; __attributeTarget(FuncDecl) attribute_syntax [numthreads(x: int, y: int = 1, z: int = 1)] : NumThreadsAttribute; +// +__attributeTarget(VarDeclBase) +attribute_syntax [__vulkanRayPayload] : VulkanRayPayloadAttribute; + +__attributeTarget(VarDeclBase) +attribute_syntax [__vulkanHitAttributes] : VulkanHitAttributesAttribute; diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h index 07d997a6a..854d114bf 100644 --- a/source/slang/core.meta.slang.h +++ b/source/slang/core.meta.slang.h @@ -950,7 +950,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) // `SampleLevel` - sb << "__target_intrinsic(glsl, \"textureLod($p, $2, $3)\")\n"; + sb << "__target_intrinsic(glsl, \"textureLod($p, $2, $3)$z\")\n"; sb << "T SampleLevel(SamplerState s, "; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "float level);\n"; @@ -1247,3 +1247,9 @@ SLANG_RAW("// Compute Shader\n") SLANG_RAW("__attributeTarget(FuncDecl)\n") SLANG_RAW("attribute_syntax [numthreads(x: int, y: int = 1, z: int = 1)] : NumThreadsAttribute;\n") SLANG_RAW("\n") +SLANG_RAW("//\n") +SLANG_RAW("__attributeTarget(VarDeclBase)\n") +SLANG_RAW("attribute_syntax [__vulkanRayPayload] : VulkanRayPayloadAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW("__attributeTarget(VarDeclBase)\n") +SLANG_RAW("attribute_syntax [__vulkanHitAttributes] : VulkanHitAttributesAttribute;\n") diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h index 02edb6521..8a9773ba9 100644 --- a/source/slang/diagnostic-defs.h +++ b/source/slang/diagnostic-defs.h @@ -71,9 +71,12 @@ DIAGNOSTIC( 15, Error, unknownStage, "unknown stage '$0'"); DIAGNOSTIC( 16, Error, unknownPassThroughTarget, "unknown pass-through target '$0'"); DIAGNOSTIC( 17, Error, unknownCommandLineOption, "unknown command-line option '$0'"); DIAGNOSTIC( 18, Error, noProfileSpecified, "no profile specified; use the '-profile <profile name>' option"); -DIAGNOSTIC( 19, Error, multipleEntryPointsNeedMulitpleProfiles, "when multiple entry points are specified, each must have a profile given (with '-profile') before the '-entry' option"); +DIAGNOSTIC( 19, Error, multipleEntryPointsNeedMulitpleProfiles, "when specifying multiple profiles on the command line, each '-entry' option must be preceded by a '-profile' option"); DIAGNOSTIC( 20, Error, multipleTranslationUnitsNeedEntryPoints, "when using multiple translation units, entry points must be specified after their translation unit file(s)"); DIAGNOSTIC( 21, Error, expectedArgumentForOption, "expected an argument for command-line option '$0'"); +DIAGNOSTIC( 22, Error, noStageSpecified, "no stage specified; use the '-stage <stage name>' option"); +DIAGNOSTIC( 23, Error, multipleEntryPointsNeedMulitpleStages, "when multiple entry points are specified on the command line, each '-entry' point must be given a stage by a preceding '-stage' option"); +DIAGNOSTIC( 24, Error, unknownLineDirectiveMode, "unknown '#line' directive mode '$0'"); // // 1xxxx - Lexical anaylsis @@ -336,6 +339,9 @@ DIAGNOSTIC(39010, Error, expectedSpaceIndex, "expected a register space index af DIAGNOSTIC(39011, Error, componentMaskNotSupported, "explicit register component masks are not yet supported in Slang") DIAGNOSTIC(39012, Error, packOffsetNotSupported, "explicit 'packoffset' bindings are not yet supported in Slang") +DIAGNOSTIC(39013, Error, dontExpectOutParametersForStage, "the '$0' stage does not support `out` or `inout` entry point parameters") +DIAGNOSTIC(39014, Error, dontExpectInParametersForStage, "the '$0' stage does not support `in` entry point parameters") + // // 4xxxx - IL code generation. // diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 521a9fe1f..312e8c0b3 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -146,6 +146,8 @@ struct SharedEmitContext Dictionary<IRInst*, String> mapInstToName; DiagnosticSink* getSink() { return &entryPoint->compileRequest->mSink; } + + Dictionary<IRInst*, UInt> mapIRValueToRayPayloadLocation; }; struct EmitContext @@ -1162,13 +1164,17 @@ struct EmitVisitor case CodeGenTarget::GLSL: { - // TODO: This "translation" is obviously wrong for GLSL. switch (type->op) { + case kIROp_RaytracingAccelerationStructureType: + requireGLSLExtension("GL_NVX_raytracing"); + Emit("accelerationStructureNVX"); + break; + + // TODO: These "translations" are obviously wrong for GLSL. case kIROp_HLSLByteAddressBufferType: Emit("ByteAddressBuffer"); break; case kIROp_HLSLRWByteAddressBufferType: Emit("RWByteAddressBuffer"); break; case kIROp_HLSLRasterizerOrderedByteAddressBufferType: Emit("RasterizerOrderedByteAddressBuffer"); break; - case kIROp_RaytracingAccelerationStructureType: Emit("RaytracingAccelerationStructure"); break; default: SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled buffer type"); @@ -1886,6 +1892,7 @@ struct EmitVisitor CASE(GLSL_430, 430); CASE(GLSL_440, 440); CASE(GLSL_450, 450); + CASE(GLSL_460, 460); #undef CASE default: @@ -2444,6 +2451,10 @@ struct EmitVisitor { return true; } + else if(as<IRUntypedBufferResourceType>(type)) + { + return true; + } else if(as<IRSamplerStateTypeBase>(type)) { return true; @@ -3236,6 +3247,57 @@ struct EmitVisitor } break; + // We will use the `$X` case as a prefix for + // special logic needed when cross-compiling ray-tracing + // shaders. + case 'X': + { + SLANG_RELEASE_ASSERT(*cursor); + switch(*cursor++) + { + case 'P': + { + // The `$XP` case handles looking up + // the assocaited `location` for a variable + // used as the argument ray payload at a + // trace call site. + + UInt argIndex = 0; + SLANG_RELEASE_ASSERT(argCount > argIndex); + auto arg = args[argIndex].get(); + auto argLoad = as<IRLoad>(arg); + SLANG_RELEASE_ASSERT(argLoad); + auto argVar = argLoad->getOperand(0); + Emit(getRayPayloadLocation(ctx, argVar)); + } + break; + + case 'T': + { + // The `$XT` case handles selecting between + // the `gl_HitTNVX` and `gl_RayTmaxNVX` builtins, + // based on what stage we are using: + switch( ctx->shared->entryPoint->getStage() ) + { + default: + Emit("gl_RayTmaxNVX"); + break; + + case Stage::AnyHit: + case Stage::ClosestHit: + Emit("gl_HitTNVX"); + break; + } + } + break; + + default: + SLANG_RELEASE_ASSERT(false); + break; + } + } + break; + default: SLANG_UNEXPECTED("bad format in intrinsic definition"); break; @@ -3910,7 +3972,10 @@ struct EmitVisitor } else if (auto entryPointLayout = layout->dynamicCast<EntryPointLayout>()) { - emitIRSemantics(ctx, entryPointLayout->resultLayout); + if(auto resultLayout = entryPointLayout->resultLayout) + { + emitIRSemantics(ctx, resultLayout); + } } } } @@ -4353,18 +4418,7 @@ struct EmitVisitor { if(profile.GetVersion() >= ProfileVersion::DX_6_1 ) { - char const* stageName = nullptr; - switch(stage) - { - #define PROFILE_STAGE(ID, NAME, ENUM) \ - case Stage::ID: stageName = #NAME; break; - - #include "profile-defs.h" - - default: - break; - } - + char const* stageName = getStageName(stage); if(stageName) { emit("[shader(\""); @@ -4917,6 +4971,13 @@ struct EmitVisitor EmitContext* ctx, IRStructType* structType) { + // If the selected `struct` type is actually an intrinsic + // on our target, then we don't want to emit anything at all. + if(auto intrinsicDecoration = findTargetIntrinsicDecoration(ctx, structType)) + { + return; + } + emit("struct "); emit(getIRName(structType)); emit("\n{\n"); @@ -5102,12 +5163,43 @@ struct EmitVisitor } } + UInt getRayPayloadLocation( + EmitContext* ctx, + IRInst* inst) + { + auto& map = ctx->shared->mapIRValueToRayPayloadLocation; + UInt value = 0; + if(map.TryGetValue(inst, value)) + return value; + + value = map.Count(); + map.Add(inst, value); + return value; + } + void emitIRVarModifiers( EmitContext* ctx, VarLayout* layout, IRInst* varDecl, IRType* varType) { + // Deal with Vulkan raytracing layout stuff *before* we + // do the check for whether `layout` is null, because + // the payload won't automatically get a layout applied + // (it isn't part of the user-visible interface...) + // + if(varDecl->findDecoration<IRVulkanRayPayloadDecoration>()) + { + emit("layout(location = "); + Emit(getRayPayloadLocation(ctx, varDecl)); + emit(")\n"); + emit("rayPayloadNVX\n"); + } + if(varDecl->findDecoration<IRVulkanHitAttributesDecoration>()) + { + emit("hitAttributeNVX\n"); + } + if (!layout) return; @@ -5252,6 +5344,18 @@ struct EmitVisitor } break; + case LayoutResourceKind::RayPayload: + { + emit("rayPayloadInNVX "); + } + break; + + case LayoutResourceKind::HitAttributes: + { + emit("hitAttributeNVX "); + } + break; + default: continue; } @@ -6239,6 +6343,27 @@ String emitEntryPoint( } destroyIRSpecializationState(irSpecializationState); + // Deal with cases where a particular stage requires certain GLSL versions + // and/or extensions. + switch( entryPoint->getStage() ) + { + default: + break; + + case Stage::AnyHit: + case Stage::Callable: + case Stage::ClosestHit: + case Stage::Intersection: + case Stage::Miss: + case Stage::RayGeneration: + if( target == CodeGenTarget::GLSL ) + { + requireGLSLExtension(&context.shared->extensionUsageTracker, "GL_NVX_raytracing"); + requireGLSLVersionImpl(&context.shared->extensionUsageTracker, ProfileVersion::GLSL_460); + } + break; + } + String code = sharedContext.sb.ProduceString(); sharedContext.sb.Clear(); diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 5bcff1762..b2b2c5fc5 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -1303,9 +1303,7 @@ static const RAY_FLAG RAY_FLAG_CULL_NON_OPAQUE = 0x80; // 10.1.2 - Ray Description Structure
-__builtin
-__magic_type(RayDescType)
-__intrinsic_type($(kIROp_RayDescType))
+__target_intrinsic(hlsl, RayDesc)
struct RayDesc
{
__target_intrinsic(hlsl, Origin)
@@ -1337,9 +1335,7 @@ struct RaytracingAccelerationStructure {}; // 10.1.5 - Intersection Attributes Structure
-__builtin
-__magic_type(BuiltInTriangleIntersectionAttributesType)
-__intrinsic_type($(kIROp_BuiltInTriangleIntersectionAttributesType))
+__target_intrinsic(hlsl, BuiltInTriangleIntersectionAttributes)
struct BuiltInTriangleIntersectionAttributes
{
__target_intrinsic(hlsl, barycentrics)
@@ -1354,6 +1350,7 @@ struct BuiltInTriangleIntersectionAttributes // 10.3 - Intrinsics
// 10.3.1
+__target_intrinsic(glsl, "callableShadersAreNotYetAvailableInVulkan")
void CallShader<param_t>(uint ShaderIndex, inout param_t Parameter);
// 10.3.2
@@ -1367,13 +1364,84 @@ void TraceRay<payload_t>( RayDesc Ray,
inout payload_t Payload);
+__target_intrinsic(glsl, "traceNVX")
+void __traceNVX(
+ RaytracingAccelerationStructure AccelerationStructure,
+ uint RayFlags,
+ uint InstanceInclusionMask,
+ uint RayContributionToHitGroupIndex,
+ uint MultiplierForGeometryContributionToHitGroupIndex,
+ uint MissShaderIndex,
+ float3 Origin,
+ float TMin,
+ float3 Direction,
+ float TMax,
+ int PayloadLocation);
+
+// TODO: Slang's parsing logic currently puts modifiers on
+// the `GenericDecl` rather than the inner decl when
+// using our default syntax, which seems wrong. We need
+// to fix this, but for now using the expanded `__generic`
+// syntax works in a pinch.
+//
+__generic<Payload>
+__target_intrinsic(glsl, "$XP")
+int __rayPayloadLocation(Payload payload);
+
+__generic<payload_t>
+__specialized_for_target(glsl)
+void TraceRay(
+ RaytracingAccelerationStructure AccelerationStructure,
+ uint RayFlags,
+ uint InstanceInclusionMask,
+ uint RayContributionToHitGroupIndex,
+ uint MultiplierForGeometryContributionToHitGroupIndex,
+ uint MissShaderIndex,
+ RayDesc Ray,
+ inout payload_t Payload)
+{
+ [__vulkanRayPayload]
+ static payload_t p;
+
+ p = Payload;
+ __traceNVX(
+ AccelerationStructure,
+ RayFlags,
+ InstanceInclusionMask,
+ RayContributionToHitGroupIndex,
+ MultiplierForGeometryContributionToHitGroupIndex,
+ MissShaderIndex,
+ Ray.Origin,
+ Ray.TMin,
+ Ray.Direction,
+ Ray.TMax,
+ __rayPayloadLocation(p));
+ Payload = p;
+}
+
// 10.3.3
-bool ReportHit<attr_t>(float THit, uint HitKind, attr_t Attributes);
+bool ReportHit<A>(float tHit, uint hitKind, A attributes);
+
+__target_intrinsic(glsl, "reportIntersectionNVX")
+bool __reportIntersectionNVX(float tHit, uint hitKind);
+
+__generic<A>
+__specialized_for_target(glsl)
+bool ReportHit(float tHit, uint hitKind, A attributes)
+{
+ [__vulkanHitAttributes]
+ static A a;
+
+ a = attributes;
+ return __reportIntersectionNVX(tHit, hitKind);
+}
// 10.3.4
+__target_intrinsic(glsl, ignoreIntersectionNVX)
void IgnoreHit();
// 10.3.5
+__target_intrinsic(glsl, terminateRayNVX)
void AcceptHitAndEndSearch();
// 10.4 - System Values and Special Semantics
@@ -1382,27 +1450,69 @@ void AcceptHitAndEndSearch(); // they can only be accessed from specific stages.
// 10.4.1 - Ray Dispatch System Values
+
+__target_intrinsic(glsl, "uvec3(gl_LaunchIDNVX, 0)")
uint3 DispatchRaysIndex();
+
+__target_intrinsic(glsl, "uvec3(gl_LaunchSizeNVX, 0)")
uint3 DispatchRaysDimensions();
// 10.4.2 - Ray System Values
+
+__target_intrinsic(glsl, "(gl_WorldRayOriginNVX)")
float3 WorldRayOrigin();
+
+__target_intrinsic(glsl, "(gl_WorldRayDirectionNVX)")
float3 WorldRayDirection();
+
+__target_intrinsic(glsl, "(gl_RayTminNVX)")
float RayTMin();
+
+// Note: The `RayTCurrent()` intrinsic should translate to
+// either `gl_HitTNVX` (for hit shaders) or `gl_RayTmaxNVX`
+// (for intersection shaders). Right now we are handling this
+// during code emission, for simplicity.
+//
+// TODO: Once the compiler supports a more refined concept
+// of profiles/capabilities and overloading based on them,
+// we should simply provide two overloads here, specialized
+// to the appropriate Vulkan stages.
+//
+__target_intrinsic(glsl, "$XT")
float RayTCurrent();
+
+__target_intrinsic(glsl, "(gl_RayFlagsNVX)")
uint RayFlags();
// 10.4.3 - Primitive/Object Space System Values
+
+__target_intrinsic(glsl, "(gl_InstanceCustomIndexNVX)")
uint InstanceIndex();
+
+__target_intrinsic(glsl, "(gl_InstanceID)")
uint InstanceID();
+
+__target_intrinsic(glsl, "(gl_PrimitiveID)")
uint PrimitiveIndex();
+
+__target_intrinsic(glsl, "(gl_ObjectRayOriginNVX)")
float3 ObjectRayOrigin();
+
+__target_intrinsic(glsl, "(gl_ObjectRayDirectionNVX)")
float3 ObjectRayDirection();
+__target_intrinsic(glsl, "transpose(gl_ObjectToWorldNVX)")
float3x4 ObjectToWorld3x4();
-float4x3 ObjectToWorld4x3();
+
+__target_intrinsic(glsl, "transpose(gl_WorldToObjectNVX)")
float3x4 WorldToObject3x4();
-float4x3 WorldToObject4x3();
+
+__target_intrinsic(glsl, "(gl_ObjectToWorldNVX)")
+float4x3 ObjectToWorld4x3();
+
+__target_intrinsic(glsl, "(gl_WorldToObjectNVX)")
+float4x3 WorldToObject4x3();
+
// Note: The provisional DXR spec included these unadorned
// `ObjectToWorld()` and `WorldToObject()` functions, so
// we will forward them to the new names as a convience
@@ -1416,4 +1526,5 @@ float3x4 ObjectToWorld() { return ObjectToWorld3x4(); } float3x4 WorldToObject() { return WorldToObject3x4(); }
// 10.4.4 - Hit Specific System values
+__target_intrinsic(glsl, "(gl_HitKindNVX)")
uint HitKind();
diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h index 21a9305f8..073302b0b 100644 --- a/source/slang/hlsl.meta.slang.h +++ b/source/slang/hlsl.meta.slang.h @@ -1348,12 +1348,7 @@ SLANG_RAW("static const RAY_FLAG RAY_FLAG_CULL_NON_OPAQUE = 0x8 SLANG_RAW("\n") SLANG_RAW("// 10.1.2 - Ray Description Structure\n") SLANG_RAW("\n") -SLANG_RAW("__builtin\n") -SLANG_RAW("__magic_type(RayDescType)\n") -SLANG_RAW("__intrinsic_type(") -SLANG_SPLICE(kIROp_RayDescType -) -SLANG_RAW(")\n") +SLANG_RAW("__target_intrinsic(hlsl, RayDesc)\n") SLANG_RAW("struct RayDesc\n") SLANG_RAW("{\n") SLANG_RAW(" __target_intrinsic(hlsl, Origin)\n") @@ -1388,12 +1383,7 @@ SLANG_RAW("// for this stuff comes across as a kludge rather than the best possi SLANG_RAW("\n") SLANG_RAW("// 10.1.5 - Intersection Attributes Structure\n") SLANG_RAW("\n") -SLANG_RAW("__builtin\n") -SLANG_RAW("__magic_type(BuiltInTriangleIntersectionAttributesType)\n") -SLANG_RAW("__intrinsic_type(") -SLANG_SPLICE(kIROp_BuiltInTriangleIntersectionAttributesType -) -SLANG_RAW(")\n") +SLANG_RAW("__target_intrinsic(hlsl, BuiltInTriangleIntersectionAttributes)\n") SLANG_RAW("struct BuiltInTriangleIntersectionAttributes\n") SLANG_RAW("{\n") SLANG_RAW(" __target_intrinsic(hlsl, barycentrics)\n") @@ -1408,6 +1398,7 @@ SLANG_RAW("\n") SLANG_RAW("// 10.3 - Intrinsics\n") SLANG_RAW("\n") SLANG_RAW("// 10.3.1\n") +SLANG_RAW("__target_intrinsic(glsl, \"callableShadersAreNotYetAvailableInVulkan\")\n") SLANG_RAW("void CallShader<param_t>(uint ShaderIndex, inout param_t Parameter);\n") SLANG_RAW("\n") SLANG_RAW("// 10.3.2\n") @@ -1421,13 +1412,84 @@ SLANG_RAW(" uint MissShaderIndex,\n") SLANG_RAW(" RayDesc Ray,\n") SLANG_RAW(" inout payload_t Payload);\n") SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"traceNVX\")\n") +SLANG_RAW("void __traceNVX(\n") +SLANG_RAW(" RaytracingAccelerationStructure AccelerationStructure,\n") +SLANG_RAW(" uint RayFlags,\n") +SLANG_RAW(" uint InstanceInclusionMask,\n") +SLANG_RAW(" uint RayContributionToHitGroupIndex,\n") +SLANG_RAW(" uint MultiplierForGeometryContributionToHitGroupIndex,\n") +SLANG_RAW(" uint MissShaderIndex,\n") +SLANG_RAW(" float3 Origin,\n") +SLANG_RAW(" float TMin,\n") +SLANG_RAW(" float3 Direction,\n") +SLANG_RAW(" float TMax,\n") +SLANG_RAW(" int PayloadLocation);\n") +SLANG_RAW("\n") +SLANG_RAW("// TODO: Slang's parsing logic currently puts modifiers on\n") +SLANG_RAW("// the `GenericDecl` rather than the inner decl when\n") +SLANG_RAW("// using our default syntax, which seems wrong. We need\n") +SLANG_RAW("// to fix this, but for now using the expanded `__generic`\n") +SLANG_RAW("// syntax works in a pinch.\n") +SLANG_RAW("//\n") +SLANG_RAW("__generic<Payload>\n") +SLANG_RAW("__target_intrinsic(glsl, \"$XP\")\n") +SLANG_RAW("int __rayPayloadLocation(Payload payload);\n") +SLANG_RAW("\n") +SLANG_RAW("__generic<payload_t>\n") +SLANG_RAW("__specialized_for_target(glsl)\n") +SLANG_RAW("void TraceRay(\n") +SLANG_RAW(" RaytracingAccelerationStructure AccelerationStructure,\n") +SLANG_RAW(" uint RayFlags,\n") +SLANG_RAW(" uint InstanceInclusionMask,\n") +SLANG_RAW(" uint RayContributionToHitGroupIndex,\n") +SLANG_RAW(" uint MultiplierForGeometryContributionToHitGroupIndex,\n") +SLANG_RAW(" uint MissShaderIndex,\n") +SLANG_RAW(" RayDesc Ray,\n") +SLANG_RAW(" inout payload_t Payload)\n") +SLANG_RAW("{\n") +SLANG_RAW(" [__vulkanRayPayload]\n") +SLANG_RAW(" static payload_t p;\n") +SLANG_RAW("\n") +SLANG_RAW(" p = Payload;\n") +SLANG_RAW(" __traceNVX(\n") +SLANG_RAW(" AccelerationStructure,\n") +SLANG_RAW(" RayFlags,\n") +SLANG_RAW(" InstanceInclusionMask,\n") +SLANG_RAW(" RayContributionToHitGroupIndex,\n") +SLANG_RAW(" MultiplierForGeometryContributionToHitGroupIndex,\n") +SLANG_RAW(" MissShaderIndex,\n") +SLANG_RAW(" Ray.Origin,\n") +SLANG_RAW(" Ray.TMin,\n") +SLANG_RAW(" Ray.Direction,\n") +SLANG_RAW(" Ray.TMax,\n") +SLANG_RAW(" __rayPayloadLocation(p));\n") +SLANG_RAW(" Payload = p;\n") +SLANG_RAW("}\n") +SLANG_RAW("\n") SLANG_RAW("// 10.3.3\n") -SLANG_RAW("bool ReportHit<attr_t>(float THit, uint HitKind, attr_t Attributes);\n") +SLANG_RAW("bool ReportHit<A>(float tHit, uint hitKind, A attributes);\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"reportIntersectionNVX\")\n") +SLANG_RAW("bool __reportIntersectionNVX(float tHit, uint hitKind);\n") +SLANG_RAW("\n") +SLANG_RAW("__generic<A>\n") +SLANG_RAW("__specialized_for_target(glsl)\n") +SLANG_RAW("bool ReportHit(float tHit, uint hitKind, A attributes)\n") +SLANG_RAW("{\n") +SLANG_RAW(" [__vulkanHitAttributes]\n") +SLANG_RAW(" static A a;\n") +SLANG_RAW("\n") +SLANG_RAW(" a = attributes;\n") +SLANG_RAW(" return __reportIntersectionNVX(tHit, hitKind);\n") +SLANG_RAW("}\n") SLANG_RAW("\n") SLANG_RAW("// 10.3.4\n") +SLANG_RAW("__target_intrinsic(glsl, ignoreIntersectionNVX)\n") SLANG_RAW("void IgnoreHit();\n") SLANG_RAW("\n") SLANG_RAW("// 10.3.5\n") +SLANG_RAW("__target_intrinsic(glsl, terminateRayNVX)\n") SLANG_RAW("void AcceptHitAndEndSearch();\n") SLANG_RAW("\n") SLANG_RAW("// 10.4 - System Values and Special Semantics\n") @@ -1436,26 +1498,67 @@ SLANG_RAW("// TODO: Many of these functions need to be restricted so that\n") SLANG_RAW("// they can only be accessed from specific stages.\n") SLANG_RAW("\n") SLANG_RAW("// 10.4.1 - Ray Dispatch System Values\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"uvec3(gl_LaunchIDNVX, 0)\")\n") SLANG_RAW("uint3 DispatchRaysIndex();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"uvec3(gl_LaunchSizeNVX, 0)\")\n") SLANG_RAW("uint3 DispatchRaysDimensions();\n") SLANG_RAW("\n") SLANG_RAW("// 10.4.2 - Ray System Values\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldRayOriginNVX)\")\n") SLANG_RAW("float3 WorldRayOrigin();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldRayDirectionNVX)\")\n") SLANG_RAW("float3 WorldRayDirection();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_RayTminNVX)\")\n") SLANG_RAW("float RayTMin();\n") +SLANG_RAW("\n") +SLANG_RAW("// Note: The `RayTCurrent()` intrinsic should translate to\n") +SLANG_RAW("// either `gl_HitTNVX` (for hit shaders) or `gl_RayTmaxNVX`\n") +SLANG_RAW("// (for intersection shaders). Right now we are handling this\n") +SLANG_RAW("// during code emission, for simplicity.\n") +SLANG_RAW("//\n") +SLANG_RAW("// TODO: Once the compiler supports a more refined concept\n") +SLANG_RAW("// of profiles/capabilities and overloading based on them,\n") +SLANG_RAW("// we should simply provide two overloads here, specialized\n") +SLANG_RAW("// to the appropriate Vulkan stages.\n") +SLANG_RAW("//\n") +SLANG_RAW("__target_intrinsic(glsl, \"$XT\")\n") SLANG_RAW("float RayTCurrent();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_RayFlagsNVX)\")\n") SLANG_RAW("uint RayFlags();\n") SLANG_RAW("\n") SLANG_RAW("// 10.4.3 - Primitive/Object Space System Values\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_InstanceCustomIndexNVX)\")\n") SLANG_RAW("uint InstanceIndex();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_InstanceID)\")\n") SLANG_RAW("uint InstanceID();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_PrimitiveID)\")\n") SLANG_RAW("uint PrimitiveIndex();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectRayOriginNVX)\")\n") SLANG_RAW("float3 ObjectRayOrigin();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectRayDirectionNVX)\")\n") SLANG_RAW("float3 ObjectRayDirection();\n") SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"transpose(gl_ObjectToWorldNVX)\")\n") SLANG_RAW("float3x4 ObjectToWorld3x4();\n") -SLANG_RAW("float4x3 ObjectToWorld4x3();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"transpose(gl_WorldToObjectNVX)\")\n") SLANG_RAW("float3x4 WorldToObject3x4();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectToWorldNVX)\")\n") +SLANG_RAW("float4x3 ObjectToWorld4x3();\n") +SLANG_RAW("\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldToObjectNVX)\")\n") SLANG_RAW("float4x3 WorldToObject4x3();\n") SLANG_RAW("\n") SLANG_RAW("// Note: The provisional DXR spec included these unadorned\n") @@ -1471,4 +1574,5 @@ SLANG_RAW("float3x4 ObjectToWorld() { return ObjectToWorld3x4(); }\n") SLANG_RAW("float3x4 WorldToObject() { return WorldToObject3x4(); }\n") SLANG_RAW("\n") SLANG_RAW("// 10.4.4 - Hit Specific System values\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_HitKindNVX)\")\n") SLANG_RAW("uint HitKind();\n") diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h index 661626dd4..b8e4a979f 100644 --- a/source/slang/ir-inst-defs.h +++ b/source/slang/ir-inst-defs.h @@ -29,8 +29,6 @@ INST(Nop, nop, 0, 0) INST_RANGE(BasicType, VoidType, AfterBaseType) INST(StringType, String, 0, 0) - INST(RayDescType, RayDesc, 0, 0) - INST(BuiltInTriangleIntersectionAttributesType, BuiltInTriangleIntersectionAttributes, 0, 0) /* ArrayTypeBase */ INST(ArrayType, Array, 2, 0) diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h index 69b8a6bf2..d38dec0f5 100644 --- a/source/slang/ir-insts.h +++ b/source/slang/ir-insts.h @@ -117,6 +117,22 @@ struct IRNameHintDecoration : IRDecoration Name* name; }; +/// A decoration that indicates that a variable represents +/// a vulkan ray payload, and should have a location assigned +/// to it. +struct IRVulkanRayPayloadDecoration : IRDecoration +{ + enum { kDecorationOp = kIRDecorationOp_VulkanRayPayload }; +}; + +/// A decoration that indicates that a variable represents +/// vulkan hit attributes, and should have a location assigned +/// to it. +struct IRVulkanHitAttributesDecoration : IRDecoration +{ + enum { kDecorationOp = kIRDecorationOp_VulkanHitAttributes }; +}; + // An instruction that specializes another IR value // (representing a generic) to a particular set of generic arguments // (instructions representing types, witness tables, etc.) diff --git a/source/slang/ir-serialize.cpp b/source/slang/ir-serialize.cpp index 10caaeb5b..6def3d9e1 100644 --- a/source/slang/ir-serialize.cpp +++ b/source/slang/ir-serialize.cpp @@ -480,6 +480,12 @@ Result IRSerialWriter::write(IRModule* module, SourceManager* sourceManager, Opt dstInst.m_payload.m_stringIndices[0] = getStringIndex(nameDecor->name); break; } + case kIRDecorationOp_VulkanRayPayload: + case kIRDecorationOp_VulkanHitAttributes: + { + dstInst.m_payloadType = PayloadType::Empty; + break; + } default: { SLANG_ASSERT(!"Unhandled decoration type"); @@ -973,6 +979,18 @@ IRDecoration* IRSerialReader::_createDecoration(const Ser::Inst& srcInst) decor->name = getName(srcInst.m_payload.m_stringIndices[0]); return decor; } + case kIRDecorationOp_VulkanRayPayload: + { + auto decor = createEmptyDecoration<IRVulkanRayPayloadDecoration>(m_module); + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Empty); + return decor; + } + case kIRDecorationOp_VulkanHitAttributes: + { + auto decor = createEmptyDecoration<IRVulkanHitAttributesDecoration>(m_module); + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Empty); + return decor; + } default: { SLANG_ASSERT(!"Unhandled decoration type"); diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index 72eea2e7c..0b4427f35 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -2849,6 +2849,30 @@ namespace Slang } break; + case kIRDecorationOp_TargetIntrinsic: + { + auto decoration = (IRTargetIntrinsicDecoration*) dd; + + dump(context, "\n"); + dumpIndent(context); + dump(context, "[targetIntrinsic("); + dump(context, StringRepresentation::getData(decoration->targetName)); + dump(context, ", "); + dump(context, StringRepresentation::getData(decoration->definition)); + dump(context, ")]"); + } + break; + + case kIRDecorationOp_VulkanRayPayload: + { + dump(context, "\n[__vulkanRayPayload]"); + } + break; + case kIRDecorationOp_VulkanHitAttributes: + { + dump(context, "\n[__vulkanHitAttributes]"); + } + break; } } } @@ -2857,9 +2881,6 @@ namespace Slang IRDumpContext* context, IRGlobalValueWithCode* code) { - // TODO: should apply this to all instructions - dumpIRDecorations(context, code); - auto opInfo = getIROpInfo(code->op); dump(context, "\n"); @@ -2920,9 +2941,6 @@ namespace Slang IRDumpContext* context, IRParentInst* inst) { - // TODO: should apply this to all instructions - dumpIRDecorations(context, inst); - auto opInfo = getIROpInfo(inst->op); dump(context, "\n"); @@ -2988,6 +3006,8 @@ namespace Slang auto op = inst->op; + dumpIRDecorations(context, inst); + // There are several ops we want to special-case here, // so that they will be more pleasant to look at. // @@ -4188,11 +4208,17 @@ namespace Slang fieldKey)); case ScalarizedVal::Flavor::address: - return ScalarizedVal::address( - builder->emitFieldAddress( - getFieldType(val.irValue->getDataType(), fieldKey), - val.irValue, - fieldKey)); + { + auto ptrType = as<IRPtrTypeBase>(val.irValue->getDataType()); + auto valType = ptrType->getValueType(); + auto fieldType = getFieldType(valType, fieldKey); + auto fieldPtrType = builder->getPtrType(ptrType->op, fieldType); + return ScalarizedVal::address( + builder->emitFieldAddress( + fieldPtrType, + val.irValue, + fieldKey)); + } case ScalarizedVal::Flavor::tuple: { @@ -4536,6 +4562,308 @@ namespace Slang return nullptr; } + void legalizeRayTracingEntryPointParameterForGLSL( + GLSLLegalizationContext* context, + IRParam* pp, + VarLayout* paramLayout) + { + auto builder = context->getBuilder(); + auto paramType = pp->getDataType(); + + if(auto paramPtrType = as<IROutTypeBase>(paramType) ) + { + // This is either an `out` or `in out` parameter. + // We want to treat `out` parameters the same + // as `in out` for our purposes, since there are + // no pure `out` parameters defined for the ray + // tracing stages. + + // Unlike the default legalization strategy for + // `out` and `in out` entry point parameters, + // we will not introduce an intermediate temporary. + // + // Instead we will simply create a global variable + // and replace uses of the parameter with uses + // of that global variable. + + auto valueType = paramPtrType->getValueType(); + + auto globalVariable = addGlobalVariable(builder->getModule(), valueType); + builder->addLayoutDecoration(globalVariable, paramLayout); + moveValueBefore(globalVariable, builder->getFunc()); + + pp->replaceUsesWith(globalVariable); + } + else + { + // This is the `in` parameter case, so that the parameter + // was not a pointer. We will allocate a global variable + // to represent the parameter, and then perform a load + // form it at the start of the function. + // + auto valueType = paramType; + auto globalVariable = addGlobalVariable(builder->getModule(), valueType); + builder->addLayoutDecoration(globalVariable, paramLayout); + moveValueBefore(globalVariable, builder->getFunc()); + + auto irLoad = builder->emitLoad(globalVariable); + pp->replaceUsesWith(irLoad); + } + + } + + void legalizeEntryPointParameterForGLSL( + GLSLLegalizationContext* context, + IRFunc* func, + IRParam* pp, + VarLayout* paramLayout) + { + auto builder = context->getBuilder(); + auto stage = context->getStage(); + + // We need to create a global variable that will replace the parameter. + // It seems superficially obvious that the variable should have + // the same type as the parameter. + // However, if the parameter was a pointer, in order to + // support `out` or `in out` parameter passing, we need + // to be sure to allocate a variable of the pointed-to + // type instead. + // + // We also need to replace uses of the parameter with + // uses of the variable, and the exact logic there + // will differ a bit between the pointer and non-pointer + // cases. + auto paramType = pp->getDataType(); + + // First we will special-case stage input/outputs that + // don't fit into the standard varying model. + // For right now we are only doing special-case handling + // of geometry shader output streams. + if( auto paramPtrType = as<IROutTypeBase>(paramType) ) + { + auto valueType = paramPtrType->getValueType(); + if( auto gsStreamType = as<IRHLSLStreamOutputType>(valueType) ) + { + // An output stream type like `TriangleStream<Foo>` should + // more or less translate into `out Foo` (plus scalarization). + + auto globalOutputVal = createGLSLGlobalVaryings( + context, + builder, + valueType, + paramLayout, + LayoutResourceKind::VaryingOutput, + stage); + + // TODO: a GS output stream might be passed into other + // functions, so that we should really be modifying + // any function that has one of these in its parameter + // list (and in the limit we should be leagalizing any + // type that nests these...). + // + // For now we will just try to deal with `Append` calls + // directly in this function. + + + + for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() ) + { + for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() ) + { + // Is it a call? + if(ii->op != kIROp_Call) + continue; + + // Is it calling the append operation? + auto callee = ii->getOperand(0); + for(;;) + { + // If the instruction is `specialize(X,...)` then + // we want to look at `X`, and if it is `generic { ... return R; }` + // then we want to look at `R`. We handle this + // iteratively here. + // + // TODO: This idiom seems to come up enough that we + // should probably have a dedicated convenience routine + // for this. + // + // Alternatively, we could switch the IR encoding so + // that decorations are added to the generic instead of the + // value it returns. + // + switch(callee->op) + { + case kIROp_Specialize: + { + callee = cast<IRSpecialize>(callee)->getOperand(0); + continue; + } + + case kIROp_Generic: + { + auto genericResult = findGenericReturnVal(cast<IRGeneric>(callee)); + if(genericResult) + { + callee = genericResult; + continue; + } + } + + default: + break; + } + break; + } + if(callee->op != kIROp_Func) + continue; + + // HACK: we will identify the operation based + // on the target-intrinsic definition that was + // given to it. + auto decoration = findTargetIntrinsicDecoration(callee, "glsl"); + if(!decoration) + continue; + + if(StringRepresentation::asSlice(decoration->definition) != UnownedStringSlice::fromLiteral("EmitVertex()")) + { + continue; + } + + // Okay, we have a declaration, and we want to modify it! + + builder->setInsertBefore(ii); + + assign(builder, globalOutputVal, ScalarizedVal::value(ii->getOperand(2))); + } + } + + return; + } + } + + // When we have an HLSL ray tracing shader entry point, + // we don't want to translate the inputs/outputs for GLSL/SPIR-V + // according to our default rules, for two reasons: + // + // 1. The input and output for these stages are expected to + // be packaged into `struct` types rather than be scalarized, + // so the usual scalarization approach we take here should + // not be applied. + // + // 2. An `in out` parameter isn't just sugar for a combination + // of an `in` and an `out` parameter, and instead represents the + // read/write "payload" that was passed in. It should legalize + // to a single variable, and we can lower reads/writes of it + // directly, rather than introduce an intermediate temporary. + // + switch( stage ) + { + default: + break; + + case Stage::AnyHit: + case Stage::Callable: + case Stage::ClosestHit: + case Stage::Intersection: + case Stage::Miss: + case Stage::RayGeneration: + legalizeRayTracingEntryPointParameterForGLSL(context, pp, paramLayout); + return; + } + + // Is the parameter type a special pointer type + // that indicates the parameter is used for `out` + // or `inout` access? + if(auto paramPtrType = as<IROutTypeBase>(paramType) ) + { + // Okay, we have the more interesting case here, + // where the parameter was being passed by reference. + // We are going to create a local variable of the appropriate + // type, which will replace the parameter, along with + // one or more global variables for the actual input/output. + + auto valueType = paramPtrType->getValueType(); + + auto localVariable = builder->emitVar(valueType); + auto localVal = ScalarizedVal::address(localVariable); + + if( auto inOutType = as<IRInOutType>(paramPtrType) ) + { + // In the `in out` case we need to declare two + // sets of global variables: one for the `in` + // side and one for the `out` side. + auto globalInputVal = createGLSLGlobalVaryings( + context, + builder, valueType, paramLayout, LayoutResourceKind::VaryingInput, stage); + + assign(builder, localVal, globalInputVal); + } + + // Any places where the original parameter was used inside + // the function body should instead use the new local variable. + // Since the parameter was a pointer, we use the variable instruction + // itself (which is an `alloca`d pointer) directly: + pp->replaceUsesWith(localVariable); + + // We also need one or more global variables to write the output to + // when the function is done. We create them here. + auto globalOutputVal = createGLSLGlobalVaryings( + context, + builder, valueType, paramLayout, LayoutResourceKind::VaryingOutput, stage); + + // Now we need to iterate over all the blocks in the function looking + // for any `return*` instructions, so that we can write to the output variable + for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() ) + { + auto terminatorInst = bb->getLastInst(); + if(!terminatorInst) + continue; + + switch( terminatorInst->op ) + { + default: + continue; + + case kIROp_ReturnVal: + case kIROp_ReturnVoid: + break; + } + + // We dont' re-use `builder` here because we don't want to + // disrupt the source location it is using for inserting + // temporary variables at the top of the function. + // + IRBuilder terminatorBuilder; + terminatorBuilder.sharedBuilder = builder->sharedBuilder; + terminatorBuilder.setInsertBefore(terminatorInst); + + // Assign from the local variabel to the global output + // variable before the actual `return` takes place. + assign(&terminatorBuilder, globalOutputVal, localVal); + } + } + else + { + // This is the "easy" case where the parameter wasn't + // being passed by reference. We start by just creating + // one or more global variables to represent the parameter, + // and attach the required layout information to it along + // the way. + + auto globalValue = createGLSLGlobalVaryings( + context, + builder, paramType, paramLayout, LayoutResourceKind::VaryingInput, stage); + + // Next we need to replace uses of the parameter with + // references to the variable(s). We are going to do that + // somewhat naively, by simply materializing the + // variables at the start. + IRInst* materialized = materializeValue(builder, globalValue); + + pp->replaceUsesWith(materialized); + } + } + void legalizeEntryPointForGLSL( Session* session, IRModule* module, @@ -4672,218 +5000,11 @@ namespace Slang SLANG_ASSERT(entryPointLayout->fields.Count() > paramIndex); auto paramLayout = entryPointLayout->fields[paramIndex]; - // We need to create a global variable that will replace the parameter. - // It seems superficially obvious that the variable should have - // the same type as the parameter. - // However, if the parameter was a pointer, in order to - // support `out` or `in out` parameter passing, we need - // to be sure to allocate a variable of the pointed-to - // type instead. - // - // We also need to replace uses of the parameter with - // uses of the variable, and the exact logic there - // will differ a bit between the pointer and non-pointer - // cases. - auto paramType = pp->getDataType(); - - // First we will special-case stage input/outputs that - // don't fit into the standard varying model. - // For right now we are only doing special-case handling - // of geometry shader output streams. - if( auto paramPtrType = as<IROutTypeBase>(paramType) ) - { - auto valueType = paramPtrType->getValueType(); - if( auto gsStreamType = as<IRHLSLStreamOutputType>(valueType) ) - { - // An output stream type like `TriangleStream<Foo>` should - // more or less translate into `out Foo` (plus scalarization). - - auto globalOutputVal = createGLSLGlobalVaryings( - &context, - &builder, - valueType, - paramLayout, - LayoutResourceKind::VaryingOutput, - stage); - - // TODO: a GS output stream might be passed into other - // functions, so that we should really be modifying - // any function that has one of these in its parameter - // list (and in the limit we should be leagalizing any - // type that nests these...). - // - // For now we will just try to deal with `Append` calls - // directly in this function. - - - - for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() ) - { - for( auto ii = bb->getFirstInst(); ii; ii = ii->getNextInst() ) - { - // Is it a call? - if(ii->op != kIROp_Call) - continue; - - // Is it calling the append operation? - auto callee = ii->getOperand(0); - for(;;) - { - // If the instruction is `specialize(X,...)` then - // we want to look at `X`, and if it is `generic { ... return R; }` - // then we want to look at `R`. We handle this - // iteratively here. - // - // TODO: This idiom seems to come up enough that we - // should probably have a dedicated convenience routine - // for this. - // - // Alternatively, we could switch the IR encoding so - // that decorations are added to the generic instead of the - // value it returns. - // - switch(callee->op) - { - case kIROp_Specialize: - { - callee = cast<IRSpecialize>(callee)->getOperand(0); - continue; - } - - case kIROp_Generic: - { - auto genericResult = findGenericReturnVal(cast<IRGeneric>(callee)); - if(genericResult) - { - callee = genericResult; - continue; - } - } - - default: - break; - } - break; - } - if(callee->op != kIROp_Func) - continue; - - // HACK: we will identify the operation based - // on the target-intrinsic definition that was - // given to it. - auto decoration = findTargetIntrinsicDecoration(callee, "glsl"); - if(!decoration) - continue; - - if(StringRepresentation::asSlice(decoration->definition) != UnownedStringSlice::fromLiteral("EmitVertex()")) - { - continue; - } - - // Okay, we have a declaration, and we want to modify it! - - builder.setInsertBefore(ii); - - assign(&builder, globalOutputVal, ScalarizedVal::value(ii->getOperand(2))); - } - } - - continue; - } - } - - - // Is the parameter type a special pointer type - // that indicates the parameter is used for `out` - // or `inout` access? - if(auto paramPtrType = as<IROutTypeBase>(paramType) ) - { - // Okay, we have the more interesting case here, - // where the parameter was being passed by reference. - // We are going to create a local variable of the appropriate - // type, which will replace the parameter, along with - // one or more global variables for the actual input/output. - - auto valueType = paramPtrType->getValueType(); - - auto localVariable = builder.emitVar(valueType); - auto localVal = ScalarizedVal::address(localVariable); - - if( auto inOutType = as<IRInOutType>(paramPtrType) ) - { - // In the `in out` case we need to declare two - // sets of global variables: one for the `in` - // side and one for the `out` side. - auto globalInputVal = createGLSLGlobalVaryings( - &context, - &builder, valueType, paramLayout, LayoutResourceKind::VaryingInput, stage); - - assign(&builder, localVal, globalInputVal); - } - - // Any places where the original parameter was used inside - // the function body should instead use the new local variable. - // Since the parameter was a pointer, we use the variable instruction - // itself (which is an `alloca`d pointer) directly: - pp->replaceUsesWith(localVariable); - - // We also need one or more global variabels to write the output to - // when the function is done. We create them here. - auto globalOutputVal = createGLSLGlobalVaryings( - &context, - &builder, valueType, paramLayout, LayoutResourceKind::VaryingOutput, stage); - - // Now we need to iterate over all the blocks in the function looking - // for any `return*` instructions, so that we can write to the output variable - for( auto bb = func->getFirstBlock(); bb; bb = bb->getNextBlock() ) - { - auto terminatorInst = bb->getLastInst(); - if(!terminatorInst) - continue; - - switch( terminatorInst->op ) - { - default: - continue; - - case kIROp_ReturnVal: - case kIROp_ReturnVoid: - break; - } - - // We dont' re-use `builder` here because we don't want to - // disrupt the source location it is using for inserting - // temporary variables at the top of the function. - // - IRBuilder terminatorBuilder; - terminatorBuilder.sharedBuilder = builder.sharedBuilder; - terminatorBuilder.setInsertBefore(terminatorInst); - - // Assign from the local variabel to the global output - // variable before the actual `return` takes place. - assign(&terminatorBuilder, globalOutputVal, localVal); - } - } - else - { - // This is the "easy" case where the parameter wasn't - // being passed by reference. We start by just creating - // one or more global variables to represent the parameter, - // and attach the required layout information to it along - // the way. - - auto globalValue = createGLSLGlobalVaryings( - &context, - &builder, paramType, paramLayout, LayoutResourceKind::VaryingInput, stage); - - // Next we need to replace uses of the parameter with - // references to the variable(s). We are going to do that - // somewhat naively, by simply materializing the - // variables at the start. - IRInst* materialized = materializeValue(&builder, globalValue); - - pp->replaceUsesWith(materialized); - } + legalizeEntryPointParameterForGLSL( + &context, + func, + pp, + paramLayout); } // At this point we should have eliminated all uses of the @@ -5137,6 +5258,18 @@ namespace Slang } break; + case kIRDecorationOp_VulkanRayPayload: + { + context->builder->addDecoration<IRVulkanRayPayloadDecoration>(clonedValue); + } + break; + + case kIRDecorationOp_VulkanHitAttributes: + { + context->builder->addDecoration<IRVulkanHitAttributesDecoration>(clonedValue); + } + break; + default: // Don't clone any decorations we don't understand. break; @@ -5721,9 +5854,26 @@ namespace Slang }; TargetSpecializationLevel getTargetSpecialiationLevel( - IRGlobalValue* val, + IRGlobalValue* inVal, String const& targetName) { + // HACK: Currently the front-end is placing modifiers related + // to target specialization on nodes like functions, even when + // those functions are being returned by a generic. This + // means that we need to try and inspect the value being + // returned by the generic if we are looking at a generic. + IRInst* val = inVal; + while( auto genericVal = as<IRGeneric>(val) ) + { + auto firstBlock = genericVal->getFirstBlock(); + if(!firstBlock) break; + + auto returnInst = as<IRReturnVal>(firstBlock->getLastInst()); + if(!returnInst) break; + + val = returnInst->getVal(); + } + TargetSpecializationLevel result = TargetSpecializationLevel::notSpecialized; for( auto dd = val->firstDecoration; dd; dd = dd->next ) { @@ -5776,13 +5926,13 @@ namespace Slang switch (val->op) { case kIROp_WitnessTable: - case kIROp_GlobalVar: case kIROp_GlobalConstant: case kIROp_Func: case kIROp_Generic: return ((IRParentInst*)val)->getFirstChild() != nullptr; case kIROp_StructType: + case kIROp_GlobalVar: return true; default: diff --git a/source/slang/ir.h b/source/slang/ir.h index 4f36b6e7f..2e3d61f8d 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -152,6 +152,8 @@ enum IRDecorationOp : uint16_t Typically used mark an instruction so can be specially handled - say when creating a IRConstant literal, and the payload of needs to be special cased for lookup. */ kIRDecorationOp_Transitory, + kIRDecorationOp_VulkanRayPayload, + kIRDecorationOp_VulkanHitAttributes, kIRDecorationOp_CountOf }; diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index b0ec6cbf0..7e22c4694 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -1247,6 +1247,14 @@ void addVarDecorations( { builder->addDecoration<IRInterpolationModeDecoration>(inst)->mode = IRInterpolationMode::Centroid; } + else if(mod.As<VulkanRayPayloadAttribute>()) + { + builder->addDecoration<IRVulkanRayPayloadDecoration>(inst); + } + else if(mod.As<VulkanHitAttributesAttribute>()) + { + builder->addDecoration<IRVulkanHitAttributesDecoration>(inst); + } // TODO: what are other modifiers we need to propagate through? } @@ -3419,13 +3427,10 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // A type alias declaration may be generic, if it is // nested under a generic type/function/etc. // - IRBuilder subBuilderStorage = *getBuilder(); - IRBuilder* subBuilder = &subBuilderStorage; - IRGeneric* outerGeneric = emitOuterGenerics(subBuilder, decl, decl); - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - subContext->irBuilder = subBuilder; + NestedContext nested(this); + auto subBuilder = nested.getBuilder(); + auto subContext = nested.getContet(); + IRGeneric* outerGeneric = emitOuterGenerics(subContext, decl, decl); // TODO: if a type alias declaration can have linkage, // we will need to lower it to some kind of global @@ -3624,13 +3629,10 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // declaration (either a type declaration or an `extension`) // is generic. // - IRBuilder subBuilderStorage = *getBuilder(); - IRBuilder* subBuilder = &subBuilderStorage; - emitOuterGenerics(subBuilder, inheritanceDecl, inheritanceDecl); - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - subContext->irBuilder = subBuilder; + NestedContext nested(this); + auto subBuilder = nested.getBuilder(); + auto subContext = nested.getContet(); + emitOuterGenerics(subContext, inheritanceDecl, inheritanceDecl); // Lower the super-type to force its declaration to be lowered. // @@ -3784,6 +3786,236 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> return globalVal; } + bool isFunctionStaticVarDecl(VarDeclBase* decl) + { + // Only a variable marked `static` can be static. + if(!decl->FindModifier<HLSLStaticModifier>()) + return false; + + // The immediate parent of a function-scope variable + // declaration will be a `ScopeDecl`. + // + // TODO: right now the parent links for scopes are *not* + // set correctly, so we can't just scan up and look + // for a function in the parent chain... + auto parent = decl->ParentDecl; + if( dynamic_cast<ScopeDecl*>(parent) ) + { + return true; + } + + return false; + } + + IRInst* defaultSpecializeOuterGeneric( + IRInst* outerVal, + IRType* type, + GenericDecl* genericDecl) + { + auto builder = getBuilder(); + + // We need to specialize any generics that are further out... + auto specialiedOuterVal = defaultSpecializeOuterGenerics( + outerVal, + builder->getGenericKind(), + genericDecl); + + List<IRInst*> genericArgs; + + // Walk the parameters of the generic, and emit an argument for each, + // which will be a reference to binding for that parameter in the + // current scope. + // + // First we start with type and value parameters, + // in the order they were declared. + for (auto member : genericDecl->Members) + { + if (auto typeParamDecl = member.As<GenericTypeParamDecl>()) + { + genericArgs.Add(getSimpleVal(context, ensureDecl(context, typeParamDecl))); + } + else if (auto valDecl = member.As<GenericValueParamDecl>()) + { + genericArgs.Add(getSimpleVal(context, ensureDecl(context, valDecl))); + } + } + // Then we emit constraint parameters, again in + // declaration order. + for (auto member : genericDecl->Members) + { + if (auto constraintDecl = member.As<GenericTypeConstraintDecl>()) + { + genericArgs.Add(getSimpleVal(context, ensureDecl(context, constraintDecl))); + } + } + + return builder->emitSpecializeInst(type, specialiedOuterVal, genericArgs.Count(), genericArgs.Buffer()); + } + + IRInst* defaultSpecializeOuterGenerics( + IRInst* val, + IRType* type, + Decl* decl) + { + if(!val) return nullptr; + + auto parentVal = val->getParent(); + while(parentVal) + { + if(as<IRGeneric>(parentVal)) + break; + parentVal = parentVal->getParent(); + } + if(!parentVal) + return val; + + for(auto pp = decl->ParentDecl; pp; pp = pp->ParentDecl) + { + if(auto genericAncestor = dynamic_cast<GenericDecl*>(pp)) + { + return defaultSpecializeOuterGeneric(parentVal, type, genericAncestor); + } + } + + return val; + } + + struct NestedContext + { + IRGenEnv subEnvStorage; + IRBuilder subBuilderStorage; + IRGenContext subContextStorage; + + NestedContext(DeclLoweringVisitor* outer) + : subBuilderStorage(*outer->getBuilder()) + , subContextStorage(*outer->context) + { + auto outerContext = outer->context; + + subEnvStorage.outer = outerContext->env; + + subContextStorage.irBuilder = &subBuilderStorage; + subContextStorage.env = &subEnvStorage; + } + + IRBuilder* getBuilder() { return &subBuilderStorage; } + IRGenContext* getContet() { return &subContextStorage; } + }; + + + LoweredValInfo lowerFunctionStaticVarDecl( + VarDeclBase* decl) + { + // A global variable may need to be generic, if one + // of the outer declarations is generic. + NestedContext nestedContext(this); + auto subBuilder = nestedContext.getBuilder(); + auto subContext = nestedContext.getContet(); + subBuilder->setInsertInto(subBuilder->getModule()->getModuleInst()); + emitOuterGenerics(subContext, decl, decl); + + IRType* subVarType = lowerType(subContext, decl->getType()); + + IRGlobalValueWithCode* irGlobal = subBuilder->createGlobalVar(subVarType); + addVarDecorations(subContext, irGlobal, decl); + + addNameHint(context, irGlobal, decl); + maybeSetRate(context, irGlobal, decl); + + subBuilder->addHighLevelDeclDecoration(irGlobal, decl); + + // We are inside of a function, and that function might be generic, + // in which case the `static` variable will be lowered to another + // generic. Let's start with a terrible example: + // + // interface IHasCount { int getCount(); } + // int incrementCounter<T : IHasCount >(T val) { + // static int counter = 0; + // counter += val.getCount(); + // return counter; + // } + // + // In this case, `incrementCounter` will lower to a function + // nested in a generic, while `counter` will be lowered to + // a global variable nested in a *different* generic. + // The net result is something like this: + // + // int counter<T:IHasCount> = 0; + // + // int incrementCounter<T:IHasCount>(T val) { + // counter<T> += val.getCount(); + // return counter<T>; + // + // The references to `counter` inside of `incrementCounter` + // become references to `counter<T>`. + // + // At the IR level, this means that the value we install + // for `decl` needs to be a specialized reference to `irGlobal`, + // for any outer generics. + // + IRType* varType = lowerType(context, decl->getType()); + IRType* varPtrType = getBuilder()->getPtrType(varType); + auto irSpecializedGlobal = defaultSpecializeOuterGenerics(irGlobal, varPtrType, decl); + LoweredValInfo globalVal = LoweredValInfo::ptr(irSpecializedGlobal); + setValue(context, decl, globalVal); + + // A `static` variable with an initializer needs special handling, + // at least if the initializer isn't a compile-time constant. + if( auto initExpr = decl->initExpr ) + { + // We must create an ordinary global `bool isInitialized = false` + // to represent whether we've initialized this before. + // Then emit code like: + // + // if(!isInitialized) { <globalVal> = <initExpr>; isInitialized = true; } + // + // TODO: we could conceivably optimize this by detecting + // when the `initExpr` lowers to just a reference to a constant, + // and then either deleting the extra code structure there, + // or not generating it in the first place. That is a bit + // more complexity than I'm ready for at the moment. + // + + // Of course, if we are under a generic, then the Boolean + // variable need to be generic as well! + NestedContext nestedBoolContext(this); + auto boolBuilder = nestedBoolContext.getBuilder(); + auto boolContext = nestedBoolContext.getContet(); + boolBuilder->setInsertInto(boolBuilder->getModule()->getModuleInst()); + emitOuterGenerics(boolContext, decl, decl); + + auto irBoolType = boolBuilder->getBoolType(); + auto irBool = boolBuilder->createGlobalVar(irBoolType); + boolBuilder->setInsertInto(irBool); + boolBuilder->setInsertInto(boolBuilder->createBlock()); + boolBuilder->emitReturn(boolBuilder->getBoolValue(false)); + + auto boolVal = LoweredValInfo::ptr(defaultSpecializeOuterGenerics(irBool, irBoolType, decl)); + + + // Okay, with our global Boolean created, we can move on to + // generating the code we actually care about, back in the original function. + + auto builder = getBuilder(); + auto initBlock = builder->createBlock(); + auto afterBlock = builder->createBlock(); + + builder->emitIfElse(getSimpleVal(context, boolVal), afterBlock, initBlock, afterBlock); + + builder->setInsertInto(initBlock); + LoweredValInfo initVal = lowerLValueExpr(context, initExpr); + assign(context, globalVal, initVal); + assign(context, boolVal, LoweredValInfo::simple(builder->getBoolValue(true))); + builder->emitBranch(afterBlock); + + builder->setInsertInto(afterBlock); + } + + irGlobal->moveToEnd(); + finishOuterGenerics(subBuilder, irGlobal); + return globalVal; + } + LoweredValInfo visitGenericValueParamDecl(GenericValueParamDecl* decl) { return emitDeclRef(context, makeDeclRef(decl), @@ -3799,6 +4031,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> return lowerGlobalVarDecl(decl); } + if(isFunctionStaticVarDecl(decl)) + { + return lowerFunctionStaticVarDecl(decl); + } + // A user-defined variable declaration will usually turn into // an `alloca` operation for the variable's storage, // plus some code to initialize it and then store to the variable. @@ -3913,16 +4150,12 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // TODO: a bit more work will be needed if we allow for // enum cases that have payloads, because then we need // a function that constructs the value given arguments. - - IRBuilder subBuilderStorage = *getBuilder(); - IRBuilder* subBuilder = &subBuilderStorage; + // + NestedContext nestedContext(this); + auto subContext = nestedContext.getContet(); // Emit any generics that should wrap the actual type. - emitOuterGenerics(subBuilder, decl, decl); - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - subContext->irBuilder = subBuilder; + emitOuterGenerics(subContext, decl, decl); return lowerRValueExpr(subContext, decl->initExpr); } @@ -3937,13 +4170,10 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> ensureDecl(context, inheritanceDecl); } - IRBuilder subBuilderStorage = *getBuilder(); - IRBuilder* subBuilder = &subBuilderStorage; - emitOuterGenerics(subBuilder, decl, decl); - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - subContext->irBuilder = subBuilder; + NestedContext nestedContext(this); + auto subBuilder = nestedContext.getBuilder(); + auto subContext = nestedContext.getContet(); + emitOuterGenerics(subContext, decl, decl); // An `enum` declaration will currently lower directly to its "tag" // type, so that any references to the `enum` become referenes to @@ -3977,15 +4207,12 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // We are going to create nested IR building state // to use when emitting the members of the type. // - IRBuilder subBuilderStorage = *getBuilder(); - IRBuilder* subBuilder = &subBuilderStorage; + NestedContext nestedContext(this); + auto subBuilder = nestedContext.getBuilder(); + auto subContext = nestedContext.getContet(); // Emit any generics that should wrap the actual type. - emitOuterGenerics(subBuilder, decl, decl); - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - subContext->irBuilder = subBuilder; + emitOuterGenerics(subContext, decl, decl); IRStructType* irStruct = subBuilder->createStructType(); addNameHint(context, irStruct, decl); @@ -4032,6 +4259,9 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> irStruct->moveToEnd(); + addTargetIntrinsicDecorations(irStruct, decl); + + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irStruct)); } @@ -4426,17 +4656,14 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> } IRGeneric* emitOuterGeneric( - IRBuilder* subBuilder, + IRGenContext* subContext, GenericDecl* genericDecl, Decl* leafDecl) { - // Of course, a generic might itself be nested inside of other generics... - auto nextOuterGeneric = emitOuterGenerics(subBuilder, genericDecl, leafDecl); - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - subContext->irBuilder = subBuilder; + auto subBuilder = subContext->irBuilder; + // Of course, a generic might itself be nested inside of other generics... + auto nextOuterGeneric = emitOuterGenerics(subContext, genericDecl, leafDecl); // We need to create an IR generic @@ -4498,13 +4725,13 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // The `leafDecl` represents the inner-most declaration we are actually // trying to emit, which is the one that should receive the mangled name. // - IRGeneric* emitOuterGenerics(IRBuilder* subBuilder, Decl* decl, Decl* leafDecl) + IRGeneric* emitOuterGenerics(IRGenContext* subContext, Decl* decl, Decl* leafDecl) { for(auto pp = decl->ParentDecl; pp; pp = pp->ParentDecl) { if(auto genericAncestor = dynamic_cast<GenericDecl*>(pp)) { - return emitOuterGeneric(subBuilder, genericAncestor, leafDecl); + return emitOuterGeneric(subContext, genericAncestor, leafDecl); } } @@ -4584,16 +4811,16 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { // We are going to use a nested builder, because we will // change the parent node that things get nested into. - - IRBuilder subBuilderStorage = *getBuilder(); - IRBuilder* subBuilder = &subBuilderStorage; - + // + NestedContext nestedContext(this); + auto subBuilder = nestedContext.getBuilder(); + auto subContext = nestedContext.getContet(); // The actual `IRFunction` that we emit needs to be nested // inside of one `IRGeneric` for every outer `GenericDecl` // in the declaration hierarchy. - emitOuterGenerics(subBuilder, decl, decl); + emitOuterGenerics(subContext, decl, decl); // Collect the parameter lists we will use for our new function. ParameterLists parameterLists; @@ -4621,11 +4848,6 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> } } - - IRGenContext subContextStorage = *context; - IRGenContext* subContext = &subContextStorage; - subContext->irBuilder = subBuilder; - // need to create an IR function here IRFunc* irFunc = subBuilder->createFunc(); @@ -4980,17 +5202,8 @@ LoweredValInfo lowerDecl( { IRBuilderSourceLocRAII sourceLocInfo(context->irBuilder, decl->loc); - IRGenEnv subEnv; - subEnv.outer = context->env; - - IRGenContext subContext = *context; - subContext.env = &subEnv; - - DeclLoweringVisitor visitor; - visitor.context = &subContext; - - + visitor.context = context; try { @@ -5030,9 +5243,12 @@ LoweredValInfo ensureDecl( subIRBuilder.sharedBuilder = context->irBuilder->sharedBuilder; subIRBuilder.setInsertInto(subIRBuilder.sharedBuilder->module->getModuleInst()); - IRGenContext subContext = *context; + IRGenEnv subEnv; + subEnv.outer = context->env; + IRGenContext subContext = *context; subContext.irBuilder = &subIRBuilder; + subContext.env = &subEnv; result = lowerDecl(&subContext, decl); diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h index d53f8bc6c..35bd38e2e 100644 --- a/source/slang/modifier-defs.h +++ b/source/slang/modifier-defs.h @@ -387,6 +387,18 @@ SYNTAX_CLASS(EntryPointAttribute, Attribute) FIELD(Stage, stage); END_SYNTAX_CLASS() +// A `[__vulkanRayPayload]` attribute, which is used in the +// standard library implementation to indicate that a variable +// actually represents the input/output interface for a Vulkan +// ray tracing shader to pass per-ray payload information. +SIMPLE_SYNTAX_CLASS(VulkanRayPayloadAttribute, Attribute) + +// A `[__vulkanHitAttributes]` attribute, which is used in the +// standard library implementation to indicate that a variable +// actually represents the output interface for a Vulkan +// intersection shader to pass hit attribute information. +SIMPLE_SYNTAX_CLASS(VulkanHitAttributesAttribute, Attribute) + // HLSL modifiers for geometry shader input topology SIMPLE_SYNTAX_CLASS(HLSLGeometryShaderInputPrimitiveTypeModifier, Modifier) SIMPLE_SYNTAX_CLASS(HLSLPointModifier , HLSLGeometryShaderInputPrimitiveTypeModifier) diff --git a/source/slang/options.cpp b/source/slang/options.cpp index d6310e8e1..4d4b61fc8 100644 --- a/source/slang/options.cpp +++ b/source/slang/options.cpp @@ -56,7 +56,7 @@ struct OptionsParser struct RawEntryPoint { String name; - SlangProfileID profileID = SLANG_PROFILE_UNKNOWN; + Profile profile; int translationUnitIndex = -1; int outputPathIndex = -1; }; @@ -75,10 +75,12 @@ struct OptionsParser int translationUnitCount = 0; int currentTranslationUnitIndex = -1; - SlangProfileID currentProfileID = SLANG_PROFILE_UNKNOWN; + // The currently specified '-profile' and/or '-stage' options. + Profile currentProfile = Profile::Unknown; - // How many times were `-profile` options given? + // How many times were `-profile` and '-stage' options given? int profileOptionCount = 0; + int stageOptionCount = 0; SlangCompileFlags flags = 0; SlangTargetFlags targetFlags = 0; @@ -400,8 +402,34 @@ struct OptionsParser } else { - currentProfileID = profileID; + auto newProfile = Profile(profileID); + currentProfile.setVersion(newProfile.GetVersion()); profileOptionCount++; + + // A `-profile` option that also specifies a stage (e.g., `-profile vs_5_0`) + // should be treated like a composite (e.g., `-profile sm_5_0 -stage vertex`) + if(newProfile.GetStage() != Stage::Unknown) + { + currentProfile.setStage(newProfile.GetStage()); + stageOptionCount++; + } + } + } + else if (argStr == "-stage") + { + String name; + SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); + + Stage stage = findStageByName(name); + if( stage == Stage::Unknown ) + { + sink->diagnose(SourceLoc(), Diagnostics::unknownStage, name); + return SLANG_FAIL; + } + else + { + currentProfile.setStage(stage); + stageOptionCount++; } } else if (argStr == "-entry") @@ -417,12 +445,7 @@ struct OptionsParser int currentOutputPathIndex = outputPathCount - 1; entry.outputPathIndex = currentOutputPathIndex; - // TODO(tfoley): Allow user to fold a specification of a profile into the entry-point name, - // for the case where they might be compiling multiple entry points in one invocation... - // - // For now, just use the last profile set on the command-line to specify this - - entry.profileID = currentProfileID; + entry.profile = currentProfile; rawEntryPoints.Add(entry); } @@ -548,6 +571,25 @@ struct OptionsParser { defaultMatrixLayoutMode = kMatrixLayoutMode_ColumnMajor; } + else if(argStr == "-line-directive-mode") + { + String name; + SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); + + SlangLineDirectiveMode mode = SLANG_LINE_DIRECTIVE_MODE_DEFAULT; + if(name == "none") + { + mode = SLANG_LINE_DIRECTIVE_MODE_NONE; + } + else + { + sink->diagnose(SourceLoc(), Diagnostics::unknownLineDirectiveMode, name); + return SLANG_FAIL; + } + + spSetLineDirectiveMode(compileRequest, mode); + + } else if (argStr == "--") { // The `--` option causes us to stop trying to parse options, @@ -605,25 +647,32 @@ struct OptionsParser // Use a default entry point name char const* entryPointName = "main"; - // Try to determine a profile - SlangProfileID entryPointProfile = SLANG_PROFILE_UNKNOWN; + // Try to determine a profile and stage + + // If a profile and/or stage was specified on the command line, then we use it + Profile entryPointProfile = currentProfile; - // If a profile was specified on the command line, then we use it - if(currentProfileID != SLANG_PROFILE_UNKNOWN) - { - entryPointProfile = currentProfileID; - } // Otherwise, check if the translation unit implied a profile // (e.g., a `*.vert` file implies the `GLSL_Vertex` profile) - else if(rawTranslationUnit.implicitProfile != SLANG_PROFILE_UNKNOWN) + // + // TODO: most of this is just there to support GLSL files + // as input, which doesn't make sense when we don't support + // GLSL at all other than for pass-through. We should ditch + // as much of this complexity as possible. + // + if(entryPointProfile.raw == SLANG_PROFILE_UNKNOWN) { - entryPointProfile = rawTranslationUnit.implicitProfile; + if(rawTranslationUnit.implicitProfile != SLANG_PROFILE_UNKNOWN) + { + entryPointProfile = rawTranslationUnit.implicitProfile; + } } + RawEntryPoint entry; entry.name = entryPointName; entry.translationUnitIndex = rawTranslationUnit.translationUnitIndex; - entry.profileID = entryPointProfile; + entry.profile = entryPointProfile; rawEntryPoints.Add(entry); } } @@ -633,46 +682,83 @@ struct OptionsParser if( rawEntryPoints.Count() != 0 ) { bool anyEntryPointWithoutProfile = false; + bool anyEntryPointWithoutStage = false; for( auto& entryPoint : rawEntryPoints ) { - // Skip entry points that are already associated with a translation unit... - if( entryPoint.profileID != SLANG_PROFILE_UNKNOWN ) - continue; + if(entryPoint.profile.GetStage() == Stage::Unknown) + { + anyEntryPointWithoutStage = true; + } - anyEntryPointWithoutProfile = true; - break; + if(entryPoint.profile.getFamily() == ProfileFamily::Unknown) + { + anyEntryPointWithoutProfile = true; + } } - // Issue an error if there are entry points without a profile, - // and no profile was specified. - if( anyEntryPointWithoutProfile - && currentProfileID == SLANG_PROFILE_UNKNOWN) - { - sink->diagnose(SourceLoc(), Diagnostics::noProfileSpecified); - return SLANG_E_INVALID_ARG; - } - // Issue an error if we have mulitple `-profile` options *and* - // there were entry points that didn't get a profile, *and* - // there we m - if (anyEntryPointWithoutProfile - && profileOptionCount > 1) + if(anyEntryPointWithoutStage) { - if (rawEntryPoints.Count() > 1) + // If there are entry points that never got a stage specified, and + // the user used multiple `-profile` and `-stage` options to try + // and establish stages, then that is an error, because we can't + // infer a stage for whatever is left. + if(stageOptionCount > 1) + { + if (rawEntryPoints.Count() > 1) + { + sink->diagnose(SourceLoc(), Diagnostics::multipleEntryPointsNeedMulitpleStages); + return SLANG_E_INVALID_ARG; + } + } + + // If a stage never got specified, then that is an error. + if(currentProfile.GetStage() == Stage::Unknown) { - sink->diagnose(SourceLoc(), Diagnostics::multipleEntryPointsNeedMulitpleProfiles); + sink->diagnose(SourceLoc(), Diagnostics::noStageSpecified); return SLANG_E_INVALID_ARG; } + + // Otherwise, exactly one stage was specified on the command line, + // and that should implicitly apply to all the entry points. + for( auto& e : rawEntryPoints ) + { + if(e.profile.GetStage() == Stage::Unknown) + { + e.profile.setStage(currentProfile.GetStage()); + } + } } - // TODO: need to issue an error on a `-profile` option that doesn't actually - // affect any entry point... - // Take the profile that was specified on the command line, and - // apply it to any entry points that don't already have a profile. - for( auto& e : rawEntryPoints ) + if(anyEntryPointWithoutProfile ) { - if( e.profileID == SLANG_PROFILE_UNKNOWN ) + // If there are entry points that never got a profile specified, and + // the user used multiple `-profile` options to try and establish + // different profiles, then that is an error, because we can't + // infer a stage for whatever is left. + if(profileOptionCount > 1) { - e.profileID = currentProfileID; + if (rawEntryPoints.Count() > 1) + { + sink->diagnose(SourceLoc(), Diagnostics::multipleEntryPointsNeedMulitpleProfiles); + return SLANG_E_INVALID_ARG; + } + } + + // If a profile never got specified, then that is an error. + if(currentProfile.getFamily() == ProfileFamily::Unknown) + { + sink->diagnose(SourceLoc(), Diagnostics::noProfileSpecified); + return SLANG_E_INVALID_ARG; + } + + // Otherwise, exactly one profile was specified on the command line, + // and that should implicitly apply to all the entry points. + for( auto& e : rawEntryPoints ) + { + if(e.profile.getFamily() == ProfileFamily::Unknown) + { + e.profile.setVersion(currentProfile.GetVersion()); + } } } } @@ -829,7 +915,7 @@ struct OptionsParser compileRequest, entryPoint.translationUnitIndex, entryPoint.name.begin(), - entryPoint.profileID); + entryPoint.profile.raw); // If an output path was specified for the entry point, // when we need to provide it here. diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp index 34b490c71..a4e9d6ca0 100644 --- a/source/slang/parameter-binding.cpp +++ b/source/slang/parameter-binding.cpp @@ -1112,6 +1112,7 @@ struct EntryPointParameterState int semanticSlotCount; Stage stage = Stage::Unknown; bool isSampleRate = false; + SourceLoc loc; }; @@ -1133,6 +1134,7 @@ static void collectGlobalScopeGLSLVaryingParameter( state.directionMask = direction; state.ioSemanticIndex = &defaultSemanticIndex; state.stage = context->stage; + state.loc = varDecl->loc; RefPtr<VarLayout> varLayout = new VarLayout(); varLayout->varDecl = makeDeclRef(varDecl.Ptr()); @@ -1842,7 +1844,6 @@ static RefPtr<TypeLayout> processEntryPointParameterDecl( return processEntryPointParameter(context, type, state, varLayout); } - static RefPtr<TypeLayout> processEntryPointParameter( ParameterBindingContext* context, RefPtr<Type> type, @@ -1871,6 +1872,7 @@ static RefPtr<TypeLayout> processEntryPointParameter( elementState.optSemanticName = nullptr; elementState.semanticSlotCount = 0; elementState.stage = state.stage; + elementState.loc = state.loc; auto elementTypeLayout = processEntryPointParameter(context, elementType, elementState, nullptr); @@ -1885,6 +1887,68 @@ static RefPtr<TypeLayout> processEntryPointParameter( return typeLayout; } + // Raytracing shaders have a slightly different interpretation of their + // "varying" input/output parameters, since they don't have the same + // idea of previous/next stage as the rasterization shader types. + // + if( state.directionMask & kEntryPointParameterDirection_Output ) + { + // Note: we are silently treating `out` parameters as if they + // were `in out` for this test, under the assumption that + // an `out` parameter represents a write-only payload. + + switch(state.stage) + { + default: + // Not a raytracing shader. + break; + + case Stage::Intersection: + case Stage::RayGeneration: + // Don't expect this case to have any `in out` parameters. + getSink(context)->diagnose(state.loc, Diagnostics::dontExpectOutParametersForStage, getStageName(state.stage)); + break; + + case Stage::AnyHit: + case Stage::Callable: + case Stage::ClosestHit: + case Stage::Miss: + // `in out` or `out` parameter is payload + return CreateTypeLayout(context->layoutContext.with( + context->getRulesFamily()->getRayPayloadParameterRules()), + type); + } + } + else + { + switch(state.stage) + { + default: + // Not a raytracing shader. + break; + + case Stage::Intersection: + case Stage::RayGeneration: + case Stage::Miss: + case Stage::Callable: + // Don't expect this case to have any `in` parameters. + // + // TODO: For a miss or callable shader we could interpret + // an `in` parameter as indicating a payload that the + // programmer doesn't intend to write to. + // + getSink(context)->diagnose(state.loc, Diagnostics::dontExpectInParametersForStage, getStageName(state.stage)); + break; + + case Stage::AnyHit: + case Stage::ClosestHit: + // `in` parameter is hit attributes + return CreateTypeLayout(context->layoutContext.with( + context->getRulesFamily()->getHitAttributesParameterRules()), + type); + } + } + // If there is an available semantic name and index, // then we should apply it to this parameter unconditionally // (that is, not just if it is a leaf parameter). @@ -2067,6 +2131,7 @@ static void collectEntryPointParameters( continue; // We have an entry-point parameter, and need to figure out what to do with it. + state.loc = paramDecl->loc; // TODO: need to handle `uniform`-qualified parameters here if (paramDecl->HasModifier<HLSLUniformModifier>()) @@ -2113,10 +2178,12 @@ static void collectEntryPointParameters( entryPointLayout->mapVarToLayout.Add(paramDecl, paramVarLayout); } - // If we can find an output type for the entry point, then process it as + // If we have a non-`void` output type for the entry point, then process it as // an output parameter. - if( auto resultType = entryPointFuncDecl->ReturnType.type ) + auto resultType = entryPointFuncDecl->ReturnType.type; + if( !resultType->Equals(resultType->getSession()->getVoidType()) ) { + state.loc = entryPointFuncDecl->loc; state.directionMask = kEntryPointParameterDirection_Output; RefPtr<VarLayout> resultLayout = new VarLayout(); diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index 37cc24459..4ebbdd191 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -112,6 +112,13 @@ namespace Slang currentScope = newScope; } + + void pushScopeAndSetParent(ContainerDecl* containerDecl) + { + containerDecl->ParentDecl = currentScope->containerDecl; + PushScope(containerDecl); + } + void PopScope() { currentScope = currentScope->parent; @@ -3256,7 +3263,7 @@ namespace Slang parser->ReadToken(TokenType::RParent); parser->ReadToken(TokenType::RParent); - parser->PushScope(scopeDecl); + parser->pushScopeAndSetParent(scopeDecl); AddMember(parser->currentScope, varDecl); stmt->body = parser->ParseStatement(); @@ -3372,7 +3379,7 @@ namespace Slang statement = ParseExpressionStatement(); } - if (statement) + if (statement && !statement->As<DeclStmt>()) { // Install any modifiers onto the statement. // Note: this path is bypassed in the case of a @@ -3389,7 +3396,7 @@ namespace Slang RefPtr<ScopeDecl> scopeDecl = new ScopeDecl(); RefPtr<BlockStmt> blockStatement = new BlockStmt(); blockStatement->scopeDecl = scopeDecl; - PushScope(scopeDecl.Ptr()); + pushScopeAndSetParent(scopeDecl.Ptr()); ReadToken(TokenType::LBrace); RefPtr<Stmt> body; @@ -3491,7 +3498,7 @@ namespace Slang stmt->scopeDecl = scopeDecl; if(!brokenScoping) - PushScope(scopeDecl.Ptr()); + pushScopeAndSetParent(scopeDecl.Ptr()); FillPosition(stmt.Ptr()); ReadToken("for"); ReadToken(TokenType::LParent); diff --git a/source/slang/profile-defs.h b/source/slang/profile-defs.h index 93703f3b9..238621084 100644 --- a/source/slang/profile-defs.h +++ b/source/slang/profile-defs.h @@ -81,7 +81,6 @@ PROFILE_STAGE_ALIAS(Fragment, fragment, Pixel) PROFILE_FAMILY(DX) PROFILE_FAMILY(GLSL) -PROFILE_FAMILY(SPRIV) // Profile versions @@ -110,6 +109,7 @@ PROFILE_VERSION(GLSL_420, GLSL) PROFILE_VERSION(GLSL_430, GLSL) PROFILE_VERSION(GLSL_440, GLSL) PROFILE_VERSION(GLSL_450, GLSL) +PROFILE_VERSION(GLSL_460, GLSL) // Specific profiles @@ -217,6 +217,7 @@ PROFILE(GLSL_None_420, glsl_420, Unknown, GLSL_420) PROFILE(GLSL_None_430, glsl_430, Unknown, GLSL_430) PROFILE(GLSL_None_440, glsl_440, Unknown, GLSL_440) PROFILE(GLSL_None_450, glsl_450, Unknown, GLSL_450) +PROFILE(GLSL_None_460, glsl_460, Unknown, GLSL_460) #define P(UPPER, LOWER, VERSION) \ PROFILE(GLSL_##UPPER##_##VERSION, glsl_##LOWER##_##VERSION, UPPER, GLSL_##VERSION) diff --git a/source/slang/profile.cpp b/source/slang/profile.cpp index 6090ddc2e..5f506741f 100644 --- a/source/slang/profile.cpp +++ b/source/slang/profile.cpp @@ -14,4 +14,21 @@ ProfileFamily getProfileFamily(ProfileVersion version) } } +const char* getStageName(Stage stage) +{ + switch(stage) + { +#define PROFILE_STAGE(ID, NAME, ENUM) \ + case Stage::ID: return #NAME; + +#include "profile-defs.h" + + default: + return nullptr; + } + +} + + + } diff --git a/source/slang/profile.h b/source/slang/profile.h index ceccf20a9..2967d5b09 100644 --- a/source/slang/profile.h +++ b/source/slang/profile.h @@ -45,6 +45,8 @@ namespace Slang #include "profile-defs.h" }; + const char* getStageName(Stage stage); + ProfileFamily getProfileFamily(ProfileVersion version); struct Profile @@ -85,6 +87,8 @@ namespace Slang RawVal raw = Unknown; }; + + Stage findStageByName(String const& name); } #endif diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index b94e146dd..7c06a11e7 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -116,6 +116,40 @@ Profile getEffectiveProfile(EntryPointRequest* entryPoint, TargetRequest* target auto entryPointProfile = entryPoint->profile; auto targetProfile = target->targetProfile; + // Depending on the target *format* we might have to restrict the + // profile family to one that makes sense. + // + // TODO: Some of this should really be handled as validation at + // the front-end. People shouldn't be allowed to ask for SPIR-V + // output with Shader Model 5.0... + switch(target->target) + { + default: + break; + + case CodeGenTarget::GLSL: + case CodeGenTarget::GLSL_Vulkan: + case CodeGenTarget::GLSL_Vulkan_OneDesc: + case CodeGenTarget::SPIRV: + case CodeGenTarget::SPIRVAssembly: + if(targetProfile.getFamily() != ProfileFamily::GLSL) + { + targetProfile.setVersion(ProfileVersion::GLSL_110); + } + break; + + case CodeGenTarget::HLSL: + case CodeGenTarget::DXBytecode: + case CodeGenTarget::DXBytecodeAssembly: + case CodeGenTarget::DXIL: + case CodeGenTarget::DXILAssembly: + if(targetProfile.getFamily() != ProfileFamily::DX) + { + targetProfile.setVersion(ProfileVersion::DX_4_0); + } + break; + } + auto entryPointProfileVersion = entryPointProfile.GetVersion(); auto targetProfileVersion = targetProfile.GetVersion(); @@ -166,7 +200,24 @@ Profile getEffectiveProfile(EntryPointRequest* entryPoint, TargetRequest* target } break; - // TODO: Add equivalent logic for the GL/VK case. + case ProfileFamily::GLSL: + switch(effectiveProfile.GetStage()) + { + default: + break; + + case Stage::RayGeneration: + case Stage::Intersection: + case Stage::ClosestHit: + case Stage::AnyHit: + case Stage::Miss: + case Stage::Callable: + stageMinVersion = ProfileVersion::GLSL_460; + break; + + // TODO: Add equivalent logic for geometry, tessellation, and compute stages. + } + break; default: break; diff --git a/source/slang/type-defs.h b/source/slang/type-defs.h index bce8edcd3..25bb5387f 100644 --- a/source/slang/type-defs.h +++ b/source/slang/type-defs.h @@ -205,9 +205,6 @@ SIMPLE_SYNTAX_CLASS(RaytracingAccelerationStructureType, UntypedBufferResourceTy SIMPLE_SYNTAX_CLASS(HLSLAppendStructuredBufferType, HLSLStructuredBufferTypeBase) SIMPLE_SYNTAX_CLASS(HLSLConsumeStructuredBufferType, HLSLStructuredBufferTypeBase) -SIMPLE_SYNTAX_CLASS(RayDescType, BuiltinType) -SIMPLE_SYNTAX_CLASS(BuiltInTriangleIntersectionAttributesType, BuiltinType) - SYNTAX_CLASS(HLSLPatchType, BuiltinType) RAW( Type* getElementType(); diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp index 3fbb9b31b..6915def2c 100644 --- a/source/slang/type-layout.cpp +++ b/source/slang/type-layout.cpp @@ -375,6 +375,23 @@ struct HLSLObjectLayoutRulesImpl : ObjectLayoutRulesImpl }; HLSLObjectLayoutRulesImpl kHLSLObjectLayoutRulesImpl; +// HACK: Treating ray-tracing input/output as if it was another +// case of varying input/output when it really needs to be +// based on byte storage/layout. +// +struct GLSLRayTracingLayoutRulesImpl : DefaultVaryingLayoutRulesImpl +{ + GLSLRayTracingLayoutRulesImpl(LayoutResourceKind kind) + : DefaultVaryingLayoutRulesImpl(kind) + {} +}; +struct HLSLRayTracingLayoutRulesImpl : DefaultVaryingLayoutRulesImpl +{ + HLSLRayTracingLayoutRulesImpl(LayoutResourceKind kind) + : DefaultVaryingLayoutRulesImpl(kind) + {} +}; + Std140LayoutRulesImpl kStd140LayoutRulesImpl; Std430LayoutRulesImpl kStd430LayoutRulesImpl; HLSLConstantBufferLayoutRulesImpl kHLSLConstantBufferLayoutRulesImpl; @@ -383,9 +400,15 @@ HLSLStructuredBufferLayoutRulesImpl kHLSLStructuredBufferLayoutRulesImpl; GLSLVaryingLayoutRulesImpl kGLSLVaryingInputLayoutRulesImpl(LayoutResourceKind::VertexInput); GLSLVaryingLayoutRulesImpl kGLSLVaryingOutputLayoutRulesImpl(LayoutResourceKind::FragmentOutput); +GLSLRayTracingLayoutRulesImpl kGLSLRayPayloadParameterLayoutRulesImpl(LayoutResourceKind::RayPayload); +GLSLRayTracingLayoutRulesImpl kGLSLHitAttributesParameterLayoutRulesImpl(LayoutResourceKind::HitAttributes); + HLSLVaryingLayoutRulesImpl kHLSLVaryingInputLayoutRulesImpl(LayoutResourceKind::VertexInput); HLSLVaryingLayoutRulesImpl kHLSLVaryingOutputLayoutRulesImpl(LayoutResourceKind::FragmentOutput); +HLSLRayTracingLayoutRulesImpl kHLSLRayPayloadParameterLayoutRulesImpl(LayoutResourceKind::RayPayload); +HLSLRayTracingLayoutRulesImpl kHLSLHitAttributesParameterLayoutRulesImpl(LayoutResourceKind::HitAttributes); + // struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl @@ -398,6 +421,9 @@ struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl virtual LayoutRulesImpl* getSpecializationConstantRules() override; virtual LayoutRulesImpl* getShaderStorageBufferRules() override; virtual LayoutRulesImpl* getParameterBlockRules() override; + + LayoutRulesImpl* getRayPayloadParameterRules() override; + LayoutRulesImpl* getHitAttributesParameterRules() override; }; struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl @@ -410,6 +436,9 @@ struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl virtual LayoutRulesImpl* getSpecializationConstantRules() override; virtual LayoutRulesImpl* getShaderStorageBufferRules() override; virtual LayoutRulesImpl* getParameterBlockRules() override; + + LayoutRulesImpl* getRayPayloadParameterRules() override; + LayoutRulesImpl* getHitAttributesParameterRules() override; }; GLSLLayoutRulesFamilyImpl kGLSLLayoutRulesFamilyImpl; @@ -442,6 +471,14 @@ LayoutRulesImpl kGLSLSpecializationConstantLayoutRulesImpl_ = { &kGLSLLayoutRulesFamilyImpl, &kGLSLSpecializationConstantLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl, }; +LayoutRulesImpl kGLSLRayPayloadParameterLayoutRulesImpl_ = { + &kGLSLLayoutRulesFamilyImpl, &kGLSLRayPayloadParameterLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kGLSLHitAttributesParameterLayoutRulesImpl_ = { + &kGLSLLayoutRulesFamilyImpl, &kGLSLHitAttributesParameterLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl, +}; + // HLSL cases LayoutRulesImpl kHLSLConstantBufferLayoutRulesImpl_ = { @@ -460,6 +497,14 @@ LayoutRulesImpl kHLSLVaryingOutputLayoutRulesImpl_ = { &kHLSLLayoutRulesFamilyImpl, &kHLSLVaryingOutputLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl, }; +LayoutRulesImpl kHLSLRayPayloadParameterLayoutRulesImpl_ = { + &kHLSLLayoutRulesFamilyImpl, &kHLSLRayPayloadParameterLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kHLSLHitAttributesParameterLayoutRulesImpl_ = { + &kHLSLLayoutRulesFamilyImpl, &kHLSLHitAttributesParameterLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl, +}; + // LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules() @@ -503,6 +548,16 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getShaderStorageBufferRules() return &kStd430LayoutRulesImpl_; } +LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getRayPayloadParameterRules() +{ + return &kGLSLRayPayloadParameterLayoutRulesImpl_; +} + +LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getHitAttributesParameterRules() +{ + return &kGLSLHitAttributesParameterLayoutRulesImpl_; +} + // LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getConstantBufferRules() @@ -547,6 +602,18 @@ LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getShaderStorageBufferRules() return nullptr; } +LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getRayPayloadParameterRules() +{ + return &kHLSLRayPayloadParameterLayoutRulesImpl_; +} + +LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getHitAttributesParameterRules() +{ + return &kHLSLHitAttributesParameterLayoutRulesImpl_; +} + + + // LayoutRulesImpl* GetLayoutRulesImpl(LayoutRule rule) diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h index 35466a42c..a66d59801 100644 --- a/source/slang/type-layout.h +++ b/source/slang/type-layout.h @@ -594,6 +594,9 @@ struct LayoutRulesFamilyImpl virtual LayoutRulesImpl* getSpecializationConstantRules()= 0; virtual LayoutRulesImpl* getShaderStorageBufferRules() = 0; virtual LayoutRulesImpl* getParameterBlockRules() = 0; + + virtual LayoutRulesImpl* getRayPayloadParameterRules() = 0; + virtual LayoutRulesImpl* getHitAttributesParameterRules()= 0; }; struct TypeLayoutContext |
