summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-10-04 16:05:23 -0700
committerGitHub <noreply@github.com>2018-10-04 16:05:23 -0700
commit4cb2a19ef192424c0a4eb205a773624563222383 (patch)
treed2b6853ec7f22afbe69c62d48b283443443f175e /source
parent84a48475067af70c9acccc9816dd60e623714328 (diff)
Support cross-compilation of ray tracing shaders to Vulkan (#663)
* Move to newer glslang * Support cross-compilation of ray tracing shaders to Vulkan This change allows HLSL shaders authored for DirectX Raytracing (DXR) to be cross-compiled to run with the experimental `GL_NVX_raytracing` extension (aka "VKRay"). * The GLSL extension spec is marked as experimental, so that any shaders written using this support should be ready for breaking changes when the spec is finalized. * "Callable shaders" are not exposed throug the GLSL extension, so this feature of DXR will not be cross-compiled. * The experimental Vulkan raytracing extension does not have an equivalent to DXR's "local root signature" concept. This does not visibly impact shader translation (because the local/global root signature mapping is handled outside of the HLSL code), but in practice it means that applications which rely on local root signatures on their DXR path will not be able to use the translation in this change as-is; more work will be needed. The simplest part of the implementation was to go into the Slang standard library and start adding GLSL translations for the various DXR operations. In some cases, like mapping `IgnoreHit()` to `ignoreIntersectionNVX()` this is almost trivial. The various functions to query system-provided values (e.g., `RayTMin()`) were also easy, with the only gotcha being that they map to variables rather than function calls in GLSL, and our handling of `__target_intrinsic` assumes that a bare identifier represents a replacement function name, and not a full expression, so we have to wrap these definitions in parentheses. The tricky operations are then `TraceRay<P>()` and `ReportHit<A>()`, because these two are generics/templates in HLSL. GLSL doesn't support generics, even for "standard library" functions, so the raytracing extension implements a slightly complex workaround: the matching operations `traceNVX()` and `reportIntersectionNVX()` pass the payload/attributes argument data via a global variable. That is, shader code for the GLSL extensions writes to the global variable and then calls the intrinsic function. The linkage between the call site and the global is established by a modifier keyword (`rayPayloadNVX` and `hitAttributeNVX`, respectively) and in the case of ray payload also uses `location` number to identify which payload global to use (since a single shader can trace rays with multiple payload types). Our translation strategy in Slang tries to leverage standard language mechanisms instead of special-case logic. For example, to translate the `ReportHit<A>()` function, we provide both a default declaration that will work for HLSL (where the operation is built-in with the signature given), and a *definition* marked with the `__specialized_for_target(glsl)` modifier. The GLSL definition declares a function `static` variable that will fill the role of the required global, and then does what the GLSL spec requires: assigns to the global, and then calls the `reportIntersectionNVX` builtin (which we declare as a separate builtin). Our ordinary lowering process will turn that `static` variable into an ordinary global in the IR, and the `[__vulkanHitAttributes]` attribute on the variable will be emitted as `hitAttributeNVX` in the output. There is no additional cross-compilation logic in Slang specific to `ReportHit<A>()` - the target-specific definition in the standard library Just Works. The case for `TraceRay<P>()` is a bit more complicated, simply because the GLSL `traceNVX()` function needs to be passed the `location` for the payload global. We implement the payload global as a function-`static` variable, with the knowledge that every unique specialization of `TraceRay<P>()` will generate a unique global variable of type `P` to implement our function-`static` variable. We then add a slightly magical builtin function `__rayPayloadLocation()` that can map such a variable to its generated `location`; the logic for this is implemented in `emit.cpp` and described below. We also changed the `RayDesc` and `BuiltinTriangleIntersectionAttributes` types from "magic" intrinsic types over to ordinary types (because the GLSL output needs to declare them as ordinary `struct` types). This ends up removing some cases in the AST and IR type representations. By itself this change would break HLSL emit, because in that case the types really are intrinsic. We added a `__target_intrinsic` modifier to these types to make them intrinsic for HLSL, and then updated the downstream passes to handle the notion of target-intrinsic types. The logic for binding/layout of entry point inputs and outputs was updated so that raytracing stages don't follow the default logic for varying input/output parameters. This is because the input/output parameters of a raytracing entry point aren't really "varying" in the same sense as those in the rasterization pipeline. In particular, the SPIR-V model for raytracing input and output treats "ray payload" and "hit attributes" parameters as being in a distinct storage class from `in` or `out` parameters. We also detect cases where a ray tracing stage declares inputs/outputs that it shouldn't have. This logic could conceivably be extended to other stages (e.g., to give an error on a compute shader with user-defined varying input/output). The type layout logic added cases for handling raytracing payload and hit-attribute data, but this is currently just a stub implementation that follows the same logic as for varying `in` and `out` parameters (it cannot give meaningful byte sizes/offsets right now). To my knowledge the GLSL spec doesn't currently specify anything about layout, and I haven't read the DXR spec language carefully enough to know what it says about layout. A future change should update the layout logic to allow for byte-based layout of ray payloads, etc. so that we can query this information via reflection. The GLSL legalization logic in `ir.cpp` was updated to factor out the per-entry-point-parameter code into its own function, and then that function was updated to special-case the input/output of a ray-tracing shader. While for rasterization stages we typically want to take the user-declared input/output and "scalarize" it for use in GLSL (in part to deal with language limitations, and in part to tease system values apart from user-defined input/output), the GLSL spec for raytracing requires payload and hit attribute parameters to be declared as single variables. There is also the issue that even for an `in out` parameter, a ray payload parameter should only turn into a single global, whereas the handling for varying `in out` parameters generates both an `in` and an `out` global for the GLSL case. Other than the handling of entry point parameters, the GLSL legalization pass doesn't need to do anything special for ray tracing shaders. The trickiest change in the `emit.cpp` logic is that we now generate `location`s for ray payload arguments (the outgoing from a `TraceRay()` call) on demand during code generation. This is a bit hacky, and it would be nice to handle it as a separate pass on the IR rather than clutter up the emit logic, but this approach was expedient. Basically, any of the global variables that got generated from the `static` declarations in the standard library implementation of `TraceRay()` will trigger the logic to assign them a `location`. The logic for emitting intrinsic operations added a few new `$`-based escape sequences. The `$XP` case handles emitting the location of a generated ray payload variable; this is how we emit the matching location at the site where we call `traceNVX`. The `$XT` case emits the appropriate translation for `RayTCurrent()` in HLSL, because it maps to something different depending on the target stage. All of the test cases here consist of a pair of an HLSL/Slang shader written to the DXR spec, plus a matching GLSL shader for a baseline. The GLSL shaders are carefully designed so that when fed into glslang they will produce the same SPIR-V as our cross-compilation process. This kind of testing is quite fragile, but it seems to be the best we can do until our testing framework code supports *both* DXR and VKRay. A bunch of the core changes ended up being blocked on issues in the rest of the compiler, so some additional features go implemented or fixed along the way: The first big wall this work ran into was that the `__specialized_for_target` modifier hasn't actually been working correctly for a while. It turns out that for the one function that is using it, `saturate()`, we have been outputting the workaround GLSL function in *all* cases (including for HLSL output) rather than only on GLSL targets. The problem here is that for a generic function with a `__specialized_for_target` modifier or a `__target_intrinsic` modifier, the IR-level decoration will end up attached to the `IRFunc` instruction nested in the `IRGeneric`, but the logic for comparing IR declarations to see which is more specialized (via `getTargetSpecializationLevel()`) was looking only at decorations on the top-level value (the generic). The quick (hacky) fix here is to make `getTargetSpecializationLevel()` try to look at the return value of a generic rather than the generic itself, so that it can see the decorations that indicate target-specific functions. A more refined fix would be to attach target-specificity decorations to the outer-most generic (to simplify the "linking" logic). The only reason not to fold that into the current fix is that the `__target_intrinsic` modifier currently serves double-duty as a marker of target specialization *and* information to drive emit logic. The latter (the emit-related stuff) currently needs to live on the `IRFunc`, and moving it to the generic could easily break a lot of code. This needs more work in a follow-on fix, but for now target specialization should again be working. The other big gotcha that the simple "just use the standard library" strategy ran into was that function-`static` variables weren't actually implemented yet, and in particular function-`static` variables inside of generic functions required some careful coding. The logic in `lower-to-ir.cpp` has this `emitOuterGenerics()` function that is supposed to take a declaration that might be nested inside of zero or more levels of AST generics, and emit corresponding IR generics for all those levels. This is needed because two different AST functions nested inside a single generic `struct` declaration should turn into distinct `IRFunc`s nested in distinct `IRGeneric`s. The tricky bit to making that all work is that the same AST-level generic type parameter will then map to *different* IR-level instructions (the parameters of distinct `IRGeneric`s) when lowering each function. The existing logic handled this in an idiomatic way by making "sub-builders" and "sub-contexts." This change refactors some of the repeated logic into a `NestedContext` type to help simplify the pattern, and applies it consistently throughout the `lower-to-ir.cpp` file. Besides that cleanup, the major change is `lowerFunctionStaticVarDecl` which, unsurprisingly, handles lower of function-`static` variables to IR globals. The careful handling of nested contexts here is needed because if we are in the middle of lowering a generic function, then a `static` variable should turn into its *own* `IRGeneric` wrapping an `IRGlobalVar`. The body of the function should refer to the global variable by specializing the global variable's `IRGeneric` to the parameters of the *functions* `IRGeneric`. This tricky detail is handled by `defaultSpecializeOuterGenerics`. An additional subtlety not actually required for this raytracing work (and thus not properly tested right now) is handling function-`static` variables with initializers. These can't just be lowered to globals with initializers, because HLSL follows the C rule that function-`static` variables are initialized when the declaration statement is first executed (and this could be visible in the presence of side-effects). The lowering strategy here translates any `static` variable with an initializer into *two* globals: one for the actual storage, plus a second `bool` variable to track whether it has been initialized yet. There are some opportunities to optimize this case, especially for `static const` data, but that will need to wait for future changes. We've slowly been shifting away from the model where a user thinks of a "profile" as including both a stage and a feature level. Instead, the user should think about selecting a profile that only describes a feature level (e.g., `sm_6_1`, `glsl_450`, etc.), and then separately specifying a stage (`vertex`, `raygeneration, etc.) for each entry point. The challenge here is that the command-line processing still only had a single `-profile` switch, and no way to specify the stage. Adding the `-stage` option was relatively easy, but making it work with the existing validation logic for command-line arguments was tricky, because of the complex model that `slangc` supports for compiling multiple entry points in a single pass. * In `slang.h` add new reflection parameter categories for ray payloads and hit attributes, as part of entry point input/output signatures. * A previous change already updated our copy of glslang to one that supports the `GL_NVX_raytracing` extension, so in `slang-glslang.cpp` we just needed to map Slang's `enum` values for the raytracing stage names to their equivalents in the glslang code. * Moved the logic for looking up a stage by name (`findStageByName()`) out of `check.cpp` and into `compiler.cpp`, with a declaration in `profile.h` * Added a `$z` suffix to the GLSL translation of `Texture*.SampleLevel()`, to handle cases where the texture element type is not a 4-component vector. Note that this fix should actually be applied to *all* these texture-sampling operations, but I didn't want to add a bunch of changes that are (clearly) not being tested right now. * The layout logic for entry points was updated to correctly skip producing a `TypeLayout` for an entry point result of type `void`, which meant that the related emit logic now needs to guard against a null value for the result layout. * In `ir.cpp`, dump decorations on every instruction instead of just selected ones, so that our IR dump output is more complete. * Added a command-line `-line-directive-mode` option so that we can easily turn off `#line` directives in the output when debugging. Not all cases where plumbed through because the `none` case is realistically the most important. * Parser was fixed to properly initialize parent links for "scope" declarations used for statements, so that we can walk backwards from a function-scope variable (including a `static`) and see the outer function/generics/etc. * Added GLSL 460 profile, since it is required for ray tracing. Also updated the logic for computing the "effective" profile to use to recognize that GLSL raytracing stages require GLSL 460. * Added some conventional ray-tracing shader suffixes to the handling in `slang-test`. This code isn't actually used, but was relevant when I started by copy-pasting some existing VKRay shaders as the starting point for my testing. * Fixup: typos
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