summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang-glslang/slang-glslang.cpp7
-rw-r--r--source/slang/check.cpp29
-rw-r--r--source/slang/compiler.cpp29
-rw-r--r--source/slang/core.meta.slang8
-rw-r--r--source/slang/core.meta.slang.h8
-rw-r--r--source/slang/diagnostic-defs.h8
-rw-r--r--source/slang/emit.cpp155
-rw-r--r--source/slang/hlsl.meta.slang129
-rw-r--r--source/slang/hlsl.meta.slang.h132
-rw-r--r--source/slang/ir-inst-defs.h2
-rw-r--r--source/slang/ir-insts.h16
-rw-r--r--source/slang/ir-serialize.cpp18
-rw-r--r--source/slang/ir.cpp600
-rw-r--r--source/slang/ir.h2
-rw-r--r--source/slang/lower-to-ir.cpp348
-rw-r--r--source/slang/modifier-defs.h12
-rw-r--r--source/slang/options.cpp182
-rw-r--r--source/slang/parameter-binding.cpp73
-rw-r--r--source/slang/parser.cpp15
-rw-r--r--source/slang/profile-defs.h3
-rw-r--r--source/slang/profile.cpp17
-rw-r--r--source/slang/profile.h4
-rw-r--r--source/slang/slang.cpp53
-rw-r--r--source/slang/type-defs.h3
-rw-r--r--source/slang/type-layout.cpp67
-rw-r--r--source/slang/type-layout.h3
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