summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-02-23 10:39:23 -0800
committerGitHub <noreply@github.com>2018-02-23 10:39:23 -0800
commit706675949e70b17860e9ca514c01461fdf9aa95d (patch)
tree233cbe1b46cf86203f20cd0a43464f7f5ed56021
parent42c3a7cfb6d7f508547ae7e61b0ee33af9d2070e (diff)
Initial support for cross-compilation of geometry shaders to GLSL (#423)
These changes are related to getting a first Slang geometry shader to translate to GLSL. There are some unrelated cross-compilation fixes in here as well. * Add direct support to shader parameter layout for GS output streams, so that they are reflected as a container type * Fix the declarations of the `SampleCmp` methods; they should always return `float`, independent of the nominal element type of the texture. * Fix up our handling of `__target_intrinsic` modifiers, so that we are a little bit more careful in how we detect something as being just a simple name replacement (e.g., `__target_intrinsic(glsl, "foo")` should make us output `foo(original, args, here)`) vs. a custom expression (e.g., `__target_intrinsic(glsl, "bar+1")` should output `bar+1` and not use any arguments, even without any `$` substitutions). * Don't emit the `[unroll]` modifier when outputting GLSL. Eventually we need to fully unroll loops for GLSL output anyway. * Inspect th entry point parameter list (from the layout information) when emitting a GS, so that we can write out the correct `layout` modifiers for input primitive type and output primitive topology. * Add a new case to `ScalarizedVal` to handle cases where an HLSL system value needs to map to a GLSL built-in variable with a slightly different type (e.g., `SV_RenderTargetArrayIndex` is a `uint` while `gl_Layer` is an `int`). For now this is only hanlding trivial cases (where a direct cast can achieve the result we want), but eventually it might need to handle things like conversion between arrays and vectors. * This is mostly just the infrastructure for the feature, and the actual enumeration of the correct types for all the system values is still to be done. * Handle a few more cases in assignment between `ScalarizedVal`. In particular, deal with cases where `materializeValue` is called on a tuple that has an array type, so that we need to construct the individual array elements. * Add translation for GS output stream `Append()` and `RestartStrip()` * Note that the translation of `Append()` seems to ignore its argument; this is because we desugar the operation during legalization for GLSL (see next item) * When legalizing for GLSL, detect an entry point parameter that is a GS stream, and translate it into `out` variables for its element type, and then rewrite any calls to `Append()` in the body of the entry point to be preceded by assignment to those variables. This works in tandem with the above translation of HLSL `Append()` calls into GLSL `EmitVertex()` calls. * We are detecting calls to `Append()` in a slightly hacky way, by looking at decorations on the callee to make sure that it is a function that is determined to translate to `EmitVertex()`. * Right now we aren't handling calls to `Append()` in other functions. It wouldn't be hard in principle to walk all the functions in the module and apply the translation (assuming we don't want to start supporting multiple output streams), but this wouldn't handle the passing of the GS output stream between functions. (This points out that there is a need for an additional type legalization pass that desugars away parameters of types that aren't actually meaningful on the target).
-rw-r--r--slang.h1
-rw-r--r--source/slang/core.meta.slang8
-rw-r--r--source/slang/core.meta.slang.h8
-rw-r--r--source/slang/emit.cpp74
-rw-r--r--source/slang/hlsl.meta.slang11
-rw-r--r--source/slang/hlsl.meta.slang.h11
-rw-r--r--source/slang/ir.cpp440
-rw-r--r--source/slang/parameter-binding.cpp43
-rw-r--r--source/slang/reflection.cpp4
-rw-r--r--source/slang/type-layout.h8
10 files changed, 570 insertions, 38 deletions
diff --git a/slang.h b/slang.h
index 8b1d27ab9..031f97c0b 100644
--- a/slang.h
+++ b/slang.h
@@ -526,6 +526,7 @@ extern "C"
SLANG_TYPE_KIND_PARAMETER_BLOCK,
SLANG_TYPE_KIND_GENERIC_TYPE_PARAMETER,
SLANG_TYPE_KIND_INTERFACE,
+ SLANG_TYPE_KIND_OUTPUT_STREAM,
SLANG_TYPE_KIND_COUNT,
};
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index d54eea94d..b26324e87 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -737,7 +737,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
}
// `SampleCmp()` and `SampleCmpLevelZero`
- sb << "T SampleCmp(SamplerComparisonState s, ";
+ sb << "float SampleCmp(SamplerComparisonState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue";
sb << ");\n";
@@ -783,7 +783,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << ", vec" << baseCoordCount << "(0.0)";
sb << ")\")\n";
}
- sb << "T SampleCmpLevelZero(SamplerComparisonState s, ";
+ sb << "float SampleCmpLevelZero(SamplerComparisonState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue";
sb << ");\n";
@@ -797,12 +797,12 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
// sense). I'm going to assume the documentation for `SampleCmp`
// is just wrong.
- sb << "T SampleCmp(SamplerState s, ";
+ sb << "float SampleCmp(SamplerComparisonState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue, ";
sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
- sb << "T SampleCmpLevelZero(SamplerState s, ";
+ sb << "float SampleCmpLevelZero(SamplerComparisonState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue, ";
sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index 4d63bac5f..0a24b389d 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -740,7 +740,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
}
// `SampleCmp()` and `SampleCmpLevelZero`
- sb << "T SampleCmp(SamplerComparisonState s, ";
+ sb << "float SampleCmp(SamplerComparisonState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue";
sb << ");\n";
@@ -786,7 +786,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << ", vec" << baseCoordCount << "(0.0)";
sb << ")\")\n";
}
- sb << "T SampleCmpLevelZero(SamplerComparisonState s, ";
+ sb << "float SampleCmpLevelZero(SamplerComparisonState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue";
sb << ");\n";
@@ -800,12 +800,12 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
// sense). I'm going to assume the documentation for `SampleCmp`
// is just wrong.
- sb << "T SampleCmp(SamplerState s, ";
+ sb << "float SampleCmp(SamplerComparisonState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue, ";
sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
- sb << "T SampleCmpLevelZero(SamplerState s, ";
+ sb << "float SampleCmpLevelZero(SamplerComparisonState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float compareValue, ";
sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n";
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 79fd2b444..20f31e60b 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -5344,6 +5344,26 @@ emitDeclImpl(decl, nullptr);
return nullptr;
}
+ // Check if the string being used to define a target intrinsic
+ // is an "ordinary" name, such that we can simply emit a call
+ // to the new name with the arguments of the old operation.
+ bool isOrdinaryName(String const& name)
+ {
+ char const* cursor = name.begin();
+ char const* end = name.end();
+
+ while(cursor != end)
+ {
+ int c = *cursor++;
+ if( (c >= 'a') && (c <= 'z') ) continue;
+ if( (c >= 'A') && (c <= 'Z') ) continue;
+ if( c == '_' ) continue;
+
+ return false;
+ }
+ return true;
+ }
+
void emitTargetIntrinsicCallExpr(
EmitContext* ctx,
IRCall* inst,
@@ -5361,7 +5381,7 @@ emitDeclImpl(decl, nullptr);
auto name = targetIntrinsic->definition;
- if(name.IndexOf('$') == -1)
+ if(isOrdinaryName(name))
{
// Simple case: it is just an ordinary name, so we call it like a builtin.
@@ -6379,7 +6399,11 @@ emitDeclImpl(decl, nullptr);
switch (loopControlDecoration->mode)
{
case kIRLoopControl_Unroll:
- emit("[unroll]\n");
+ // Note: loop unrolling control is only available in HLSL, not GLSL
+ if(getTarget(ctx) == CodeGenTarget::HLSL)
+ {
+ emit("[unroll]\n");
+ }
break;
default:
@@ -6792,7 +6816,6 @@ emitDeclImpl(decl, nullptr);
{
if (auto attrib = entryPointLayout->entryPoint->FindModifier<HLSLMaxVertexCountAttribute>())
{
- // TODO: need to include primitive type
emit("layout(max_vertices = ");
Emit(attrib->value);
emit(") out;\n");
@@ -6803,6 +6826,51 @@ emitDeclImpl(decl, nullptr);
Emit(attrib->value);
emit(") in;\n");
}
+
+ for(auto pp : entryPointLayout->entryPoint->GetParameters())
+ {
+ if(auto inputPrimitiveTypeModifier = pp->FindModifier<HLSLGeometryShaderInputPrimitiveTypeModifier>())
+ {
+ if(inputPrimitiveTypeModifier->As<HLSLTriangleModifier>())
+ {
+ emit("layout(triangles) in;\n");
+ }
+ else if(inputPrimitiveTypeModifier->As<HLSLLineModifier>())
+ {
+ emit("layout(lines) in;\n");
+ }
+ else if(inputPrimitiveTypeModifier->As<HLSLLineAdjModifier>())
+ {
+ emit("layout(lines_adjacency) in;\n");
+ }
+ else if(inputPrimitiveTypeModifier->As<HLSLPointModifier>())
+ {
+ emit("layout(points) in;\n");
+ }
+ else if(inputPrimitiveTypeModifier->As<HLSLTriangleAdjModifier>())
+ {
+ emit("layout(triangles_adjacency) in;\n");
+ }
+ }
+
+ if(auto outputStreamType = pp->type->As<HLSLStreamOutputType>())
+ {
+ if(outputStreamType->As<HLSLTriangleStreamType>())
+ {
+ emit("layout(triangle_strip) out;\n");
+ }
+ else if(outputStreamType->As<HLSLLineStreamType>())
+ {
+ emit("layout(line_strip) out;\n");
+ }
+ else if(outputStreamType->As<HLSLPointStreamType>())
+ {
+ emit("layout(points) out;\n");
+ }
+ }
+ }
+
+
}
break;
// TODO: There are other stages that will need this kind of handling.
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index f59e1b66c..f3caf09fd 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -207,19 +207,28 @@ struct RWStructuredBuffer
__generic<T> __magic_type(HLSLPointStreamType) struct PointStream
{
+ __target_intrinsic(glsl, "EmitVertex()")
void Append(T value);
+
+ __target_intrinsic(glsl, "EndPrimitive()")
void RestartStrip();
};
__generic<T> __magic_type(HLSLLineStreamType) struct LineStream
{
+ __target_intrinsic(glsl, "EmitVertex()")
void Append(T value);
+
+ __target_intrinsic(glsl, "EndPrimitive()")
void RestartStrip();
};
__generic<T> __magic_type(HLSLTriangleStreamType) struct TriangleStream
{
+ __target_intrinsic(glsl, "EmitVertex()")
void Append(T value);
+
+ __target_intrinsic(glsl, "EndPrimitive()")
void RestartStrip();
};
@@ -567,7 +576,7 @@ float2 GetRenderTargetSamplePosition(int Index);
__target_intrinsic(glsl, "groupMemoryBarrier")
void GroupMemoryBarrier();
-__target_intrinsic(glsl, "groupMemoryBarrier(); barrier")
+__target_intrinsic(glsl, "groupMemoryBarrier(); barrier()")
void GroupMemoryBarrierWithGroupSync();
// Atomics
diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h
index 0383880e0..995c753f5 100644
--- a/source/slang/hlsl.meta.slang.h
+++ b/source/slang/hlsl.meta.slang.h
@@ -209,19 +209,28 @@ sb << "};\n";
sb << "\n";
sb << "__generic<T> __magic_type(HLSLPointStreamType) struct PointStream\n";
sb << "{\n";
+sb << " __target_intrinsic(glsl, \"EmitVertex()\")\n";
sb << " void Append(T value);\n";
+sb << "\n";
+sb << " __target_intrinsic(glsl, \"EndPrimitive()\")\n";
sb << " void RestartStrip();\n";
sb << "};\n";
sb << "\n";
sb << "__generic<T> __magic_type(HLSLLineStreamType) struct LineStream\n";
sb << "{\n";
+sb << " __target_intrinsic(glsl, \"EmitVertex()\")\n";
sb << " void Append(T value);\n";
+sb << "\n";
+sb << " __target_intrinsic(glsl, \"EndPrimitive()\")\n";
sb << " void RestartStrip();\n";
sb << "};\n";
sb << "\n";
sb << "__generic<T> __magic_type(HLSLTriangleStreamType) struct TriangleStream\n";
sb << "{\n";
+sb << " __target_intrinsic(glsl, \"EmitVertex()\")\n";
sb << " void Append(T value);\n";
+sb << "\n";
+sb << " __target_intrinsic(glsl, \"EndPrimitive()\")\n";
sb << " void RestartStrip();\n";
sb << "};\n";
sb << "\n";
@@ -570,7 +579,7 @@ sb << "// Group memory barrier\n";
sb << "__target_intrinsic(glsl, \"groupMemoryBarrier\")\n";
sb << "void GroupMemoryBarrier();\n";
sb << "\n";
-sb << "__target_intrinsic(glsl, \"groupMemoryBarrier(); barrier\")\n";
+sb << "__target_intrinsic(glsl, \"groupMemoryBarrier(); barrier()\")\n";
sb << "void GroupMemoryBarrierWithGroupSync();\n";
sb << "\n";
sb << "// Atomics\n";
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index 8d3661452..8dc158b6c 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -2816,6 +2816,7 @@ namespace Slang
struct ScalarizedValImpl : RefObject
{};
struct ScalarizedTupleValImpl;
+ struct ScalarizedTypeAdapterValImpl;
struct ScalarizedVal
{
enum class Flavor
@@ -2831,6 +2832,11 @@ namespace Slang
// A `TupleValImpl` that represents zero or more `ScalarizedVal`s
tuple,
+
+ // A `TypeAdapterValImpl` that wraps a single `ScalarizedVal` and
+ // represents an implicit type conversion applied to it on read
+ // or write.
+ typeAdapter,
};
// Create a value representing a simple value
@@ -2860,6 +2866,14 @@ namespace Slang
return result;
}
+ static ScalarizedVal typeAdapter(ScalarizedTypeAdapterValImpl* impl)
+ {
+ ScalarizedVal result;
+ result.flavor = Flavor::typeAdapter;
+ result.impl = (ScalarizedValImpl*)impl;
+ return result;
+ }
+
Flavor flavor = Flavor::none;
IRValue* irValue = nullptr;
RefPtr<ScalarizedValImpl> impl;
@@ -2878,6 +2892,15 @@ namespace Slang
List<Element> elements;
};
+ // This is the case for a value that is stored with one type,
+ // but needs to present itself as having a different type
+ struct ScalarizedTypeAdapterValImpl : ScalarizedValImpl
+ {
+ ScalarizedVal val;
+ RefPtr<Type> actualType; // the actual type of `val`
+ RefPtr<Type> pretendType; // the type this value pretends to have
+ };
+
struct GlobalVaryingDeclarator
{
enum class Flavor
@@ -2892,7 +2915,11 @@ namespace Slang
struct GLSLSystemValueInfo
{
- char const* name;
+ // The name of the built-in GLSL variable
+ char const* name;
+
+ // The required type of the built-in variable
+ RefPtr<Type> requiredType;
};
void requireGLSLVersionImpl(
@@ -2905,6 +2932,7 @@ namespace Slang
struct GLSLLegalizationContext
{
+ Session* session;
ExtensionUsageTracker* extensionUsageTracker;
DiagnosticSink* sink;
Stage stage;
@@ -2944,6 +2972,8 @@ namespace Slang
auto semanticName = semanticNameSpelling.ToLower();
+ RefPtr<Type> requiredType;
+
if(semanticName == "sv_position")
{
// TODO: need to pick between `gl_Position` and
@@ -3058,6 +3088,7 @@ namespace Slang
}
name = "gl_Layer";
+ requiredType = context->session->getBuiltinType(BaseType::Int);
}
else if (semanticName == "sv_sampleindex")
{
@@ -3117,6 +3148,7 @@ namespace Slang
if( name )
{
inStorage->name = name;
+ inStorage->requiredType = requiredType;
return inStorage;
}
@@ -3142,11 +3174,17 @@ namespace Slang
kind,
&systemValueInfoStorage);
+ RefPtr<Type> type = inType;
+
+ // A system-value semantic might end up needing to override the type
+ // that the user specified.
+ if( systemValueInfo && systemValueInfo->requiredType )
+ {
+ type = systemValueInfo->requiredType;
+ }
+
// Construct the actual type and type-layout for the global variable
//
- // TODO: in the case of a system value, we may need to override the type
- //
- RefPtr<Type> type = inType;
RefPtr<TypeLayout> typeLayout = inTypeLayout;
for( auto dd = declarator; dd; dd = dd->next )
{
@@ -3200,14 +3238,33 @@ namespace Slang
auto globalVariable = addGlobalVariable(builder->getModule(), type);
moveValueBefore(globalVariable, builder->getFunc());
+ ScalarizedVal val = ScalarizedVal::address(globalVariable);
+
if( systemValueInfo )
{
globalVariable->mangledName = builder->getSession()->getNameObj(systemValueInfo->name);
+
+ if( auto fromType = systemValueInfo->requiredType )
+ {
+ // We may need to adapt from the declared type to/from
+ // the actual type of the GLSL global.
+ auto toType = inType;
+
+ if( !fromType->Equals(toType) )
+ {
+ RefPtr<ScalarizedTypeAdapterValImpl> typeAdapter = new ScalarizedTypeAdapterValImpl;
+ typeAdapter->actualType = systemValueInfo->requiredType;
+ typeAdapter->pretendType = inType;
+ typeAdapter->val = val;
+
+ val = ScalarizedVal::typeAdapter(typeAdapter);
+ }
+ }
}
builder->addLayoutDecoration(globalVariable, varLayout);
- return ScalarizedVal::address(globalVariable);
+ return val;
}
ScalarizedVal createGLSLGlobalVaryingsImpl(
@@ -3264,6 +3321,23 @@ namespace Slang
bindingIndex,
&arrayDeclarator);
}
+ else if( auto streamType = type->As<HLSLStreamOutputType>() )
+ {
+ auto elementType = streamType->getElementType();
+ auto streamLayout = dynamic_cast<StreamOutputTypeLayout*>(typeLayout);
+ SLANG_ASSERT(streamLayout);
+ auto elementTypeLayout = streamLayout->elementTypeLayout;
+
+ return createGLSLGlobalVaryingsImpl(
+ context,
+ builder,
+ elementType,
+ varLayout,
+ elementTypeLayout,
+ kind,
+ bindingIndex,
+ declarator);
+ }
else if( auto declRefType = type->As<DeclRefType>() )
{
auto declRef = declRefType->declRef;
@@ -3281,7 +3355,19 @@ namespace Slang
if( structTypeLayout )
{
RefPtr<ScalarizedTupleValImpl> tupleValImpl = new ScalarizedTupleValImpl();
- tupleValImpl->type = type;
+
+
+ // Construct the actual type for the tuple (including any outer arrays)
+ RefPtr<Type> fullType = type;
+ for( auto dd = declarator; dd; dd = dd->next )
+ {
+ assert(dd->flavor == GlobalVaryingDeclarator::Flavor::array);
+ fullType = builder->getSession()->getArrayType(
+ fullType,
+ dd->elementCount);
+ }
+
+ tupleValImpl->type = fullType;
// Okay, we want to walk through the fields here, and
// generate one variable for each.
@@ -3369,6 +3455,44 @@ namespace Slang
}
+ ScalarizedVal adaptType(
+ IRBuilder* builder,
+ IRValue* val,
+ Type* toType,
+ Type* /*fromType*/)
+ {
+ // TODO: actually consider what needs to go on here...
+ return ScalarizedVal::value(builder->emitConstructorInst(
+ toType,
+ 1,
+ &val));
+ }
+
+ ScalarizedVal adaptType(
+ IRBuilder* builder,
+ ScalarizedVal const& val,
+ Type* toType,
+ Type* fromType)
+ {
+ switch( val.flavor )
+ {
+ case ScalarizedVal::Flavor::value:
+ return adaptType(builder, val.irValue, toType, fromType);
+ break;
+
+ case ScalarizedVal::Flavor::address:
+ {
+ auto loaded = builder->emitLoad(val.irValue);
+ return adaptType(builder, loaded, toType, fromType);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unimplemented");
+ UNREACHABLE_RETURN(ScalarizedVal());
+ }
+ }
+
void assign(
IRBuilder* builder,
ScalarizedVal const& left,
@@ -3392,6 +3516,27 @@ namespace Slang
}
break;
+ case ScalarizedVal::Flavor::tuple:
+ {
+ // We are assigning from a tuple to a destination
+ // that is not a tuple. We will perform assignment
+ // element-by-element.
+ auto rightTupleVal = right.impl.As<ScalarizedTupleValImpl>();
+ UInt elementCount = rightTupleVal->elements.Count();
+
+ for( UInt ee = 0; ee < elementCount; ++ee )
+ {
+ auto rightElement = rightTupleVal->elements[ee];
+ auto leftElementVal = extractField(
+ builder,
+ left,
+ ee,
+ rightElement.declRef);
+ assign(builder, leftElementVal, rightElement.val);
+ }
+ }
+ break;
+
default:
SLANG_UNEXPECTED("unimplemented");
break;
@@ -3417,12 +3562,178 @@ namespace Slang
}
break;
+ case ScalarizedVal::Flavor::typeAdapter:
+ {
+ // We are trying to assign to something that had its type adjusted,
+ // so we will need to adjust the type of the right-hand side first.
+ //
+ // In this case we are converting to the actual type of the GLSL variable,
+ // from the "pretend" type that it had in the IR before.
+ auto typeAdapter = left.impl.As<ScalarizedTypeAdapterValImpl>();
+ auto adaptedRight = adaptType(builder, right, typeAdapter->actualType, typeAdapter->pretendType);
+ assign(builder, typeAdapter->val, adaptedRight);
+ }
+ break;
+
default:
SLANG_UNEXPECTED("unimplemented");
break;
}
}
+ ScalarizedVal getSubscriptVal(
+ IRBuilder* builder,
+ Type* elementType,
+ ScalarizedVal val,
+ IRValue* indexVal)
+ {
+ switch( val.flavor )
+ {
+ case ScalarizedVal::Flavor::value:
+ return ScalarizedVal::value(
+ builder->emitElementExtract(
+ elementType,
+ val.irValue,
+ indexVal));
+
+ case ScalarizedVal::Flavor::address:
+ return ScalarizedVal::address(
+ builder->emitElementAddress(
+ builder->getSession()->getPtrType(elementType),
+ val.irValue,
+ indexVal));
+
+ case ScalarizedVal::Flavor::tuple:
+ {
+ auto inputTuple = val.impl.As<ScalarizedTupleValImpl>();
+
+ RefPtr<ScalarizedTupleValImpl> resultTuple = new ScalarizedTupleValImpl();
+ resultTuple->type = elementType;
+
+ UInt elementCount = inputTuple->elements.Count();
+ UInt elementCounter = 0;
+
+ auto declRefType = dynamic_cast<DeclRefType*>(elementType);
+ SLANG_RELEASE_ASSERT(declRefType);
+
+ auto aggTypeDeclRef = declRefType->declRef.As<AggTypeDecl>();
+ SLANG_RELEASE_ASSERT(aggTypeDeclRef);
+
+ for(auto fieldDeclRef : getMembersOfType<StructField>(aggTypeDeclRef))
+ {
+ if(fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>())
+ continue;
+
+ auto tupleElementType = GetType(fieldDeclRef);
+
+ UInt elementIndex = elementCounter++;
+
+ SLANG_RELEASE_ASSERT(elementIndex < elementCount);
+ auto inputElement = inputTuple->elements[elementIndex];
+
+ ScalarizedTupleValImpl::Element resultElement;
+ resultElement.declRef = inputElement.declRef;
+ resultElement.val = getSubscriptVal(
+ builder,
+ tupleElementType,
+ inputElement.val,
+ indexVal);
+
+ resultTuple->elements.Add(resultElement);
+ }
+ SLANG_RELEASE_ASSERT(elementCounter == elementCount);
+
+ return ScalarizedVal::tuple(resultTuple);
+ }
+
+ default:
+ SLANG_UNEXPECTED("unimplemented");
+ UNREACHABLE_RETURN(ScalarizedVal());
+ }
+ }
+
+ ScalarizedVal getSubscriptVal(
+ IRBuilder* builder,
+ Type* elementType,
+ ScalarizedVal val,
+ UInt index)
+ {
+ return getSubscriptVal(
+ builder,
+ elementType,
+ val,
+ builder->getIntValue(
+ builder->getSession()->getIntType(),
+ index));
+ }
+
+ IRValue* materializeValue(
+ IRBuilder* builder,
+ ScalarizedVal const& val);
+
+ IRValue* materializeTupleValue(
+ IRBuilder* builder,
+ ScalarizedVal val)
+ {
+ auto tupleVal = val.impl.As<ScalarizedTupleValImpl>();
+ assert(tupleVal);
+
+ UInt elementCount = tupleVal->elements.Count();
+ auto type = tupleVal->type;
+
+ if( auto arrayType = type.As<ArrayExpressionType>() )
+ {
+ // The tuple represent an array, which means that the
+ // individual elements are expected to yield arrays as well.
+ //
+ // We will extract a value for each array element, and
+ // then use these to construct our result.
+
+ List<IRValue*> arrayElementVals;
+ UInt arrayElementCount = (UInt) GetIntVal(arrayType->ArrayLength);
+
+ for( UInt ii = 0; ii < arrayElementCount; ++ii )
+ {
+ auto arrayElementPseudoVal = getSubscriptVal(
+ builder,
+ arrayType->baseType,
+ val,
+ ii);
+
+ auto arrayElementVal = materializeValue(
+ builder,
+ arrayElementPseudoVal);
+
+ arrayElementVals.Add(arrayElementVal);
+ }
+
+ return builder->emitMakeArray(
+ arrayType,
+ arrayElementVals.Count(),
+ arrayElementVals.Buffer());
+ }
+ else
+ {
+ // The tuple represents a value of some aggregate type,
+ // so we can simply materialize the elements and then
+ // construct a value of that type.
+ //
+ // TODO: this should be using a `makeStruct` instruction.
+
+ List<IRValue*> elementVals;
+ for( UInt ee = 0; ee < elementCount; ++ee )
+ {
+ auto elementVal = materializeValue(builder, tupleVal->elements[ee].val);
+ elementVals.Add(elementVal);
+ }
+
+ return builder->emitConstructorInst(
+ tupleVal->type,
+ elementVals.Count(),
+ elementVals.Buffer());
+ }
+ }
+
IRValue* materializeValue(
IRBuilder* builder,
ScalarizedVal const& val)
@@ -3442,19 +3753,19 @@ namespace Slang
case ScalarizedVal::Flavor::tuple:
{
auto tupleVal = val.impl.As<ScalarizedTupleValImpl>();
- UInt elementCount = tupleVal->elements.Count();
-
- List<IRValue*> elementVals;
- for( UInt ee = 0; ee < elementCount; ++ee )
- {
- auto elementVal = materializeValue(builder, tupleVal->elements[ee].val);
- elementVals.Add(elementVal);
- }
+ return materializeTupleValue(builder, val);
+ }
+ break;
- return builder->emitConstructorInst(
- tupleVal->type,
- elementVals.Count(),
- elementVals.Buffer());
+ case ScalarizedVal::Flavor::typeAdapter:
+ {
+ // Somebody is trying to use a value where its actual type
+ // doesn't match the type it pretends to have. To make this
+ // work we need to adapt the type from its actual type over
+ // to its pretend type.
+ auto typeAdapter = val.impl.As<ScalarizedTypeAdapterValImpl>();
+ auto adapted = adaptType(builder, typeAdapter->val, typeAdapter->pretendType, typeAdapter->actualType);
+ return materializeValue(builder, adapted);
}
break;
@@ -3464,6 +3775,23 @@ namespace Slang
}
}
+ IRTargetIntrinsicDecoration* findTargetIntrinsicDecoration(
+ IRValue* val,
+ String const& targetName)
+ {
+ for( auto dd = val->firstDecoration; dd; dd = dd->next )
+ {
+ if(dd->op != kIRDecorationOp_TargetIntrinsic)
+ continue;
+
+ auto decoration = (IRTargetIntrinsicDecoration*) dd;
+ if(decoration->targetName == targetName)
+ return decoration;
+ }
+
+ return nullptr;
+ }
+
void legalizeEntryPointForGLSL(
Session* session,
IRFunc* func,
@@ -3472,6 +3800,7 @@ namespace Slang
ExtensionUsageTracker* extensionUsageTracker)
{
GLSLLegalizationContext context;
+ context.session = session;
context.stage = entryPointLayout->profile.GetStage();
context.sink = sink;
context.extensionUsageTracker = extensionUsageTracker;
@@ -3610,6 +3939,79 @@ namespace Slang
builder.curBlock = firstBlock;
builder.insertBeforeInst = firstBlock->getFirstInst();
+ // 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 = paramType->As<OutTypeBase>() )
+ {
+ auto valueType = paramPtrType->getValueType();
+ if( auto gsStreamType = valueType->As<HLSLStreamOutputType>() )
+ {
+ // 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);
+
+ // 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->getArg(0);
+ while( callee->op == kIROp_specialize )
+ {
+ callee = ((IRSpecialize*) callee)->getArg(0);
+ }
+ 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(decoration->definition != "EmitVertex()")
+ {
+ continue;
+ }
+
+ // Okay, we have a declaration, and we want to modify it!
+
+ builder.curBlock = bb;
+ builder.insertBeforeInst = ii;
+
+ assign(&builder, globalOutputVal, ScalarizedVal::value(ii->getArg(2)));
+ }
+ }
+
+ continue;
+ }
+ }
+
+
// Is the parameter type a special pointer type
// that indicates the parameter is used for `out`
// or `inout` access?
@@ -3684,7 +4086,7 @@ namespace Slang
auto globalValue = createGLSLGlobalVaryings(
&context,
- &builder, paramType, paramLayout, LayoutResourceKind::VertexInput);
+ &builder, paramType, paramLayout, LayoutResourceKind::VaryingInput);
// Next we need to replace uses of the parameter with
// references to the variable(s). We are going to do that
diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp
index 37642ac81..b0d90d435 100644
--- a/source/slang/parameter-binding.cpp
+++ b/source/slang/parameter-binding.cpp
@@ -1709,10 +1709,46 @@ static RefPtr<TypeLayout> processEntryPointParameterDecl(
static RefPtr<TypeLayout> processEntryPointParameter(
ParameterBindingContext* context,
- RefPtr<Type> type,
+ RefPtr<Type> type,
EntryPointParameterState const& state,
RefPtr<VarLayout> varLayout)
{
+ if (varLayout)
+ {
+ varLayout->stage = state.stage;
+ }
+
+ // The default handling of varying parameters should not apply
+ // to geometry shader output streams; they have their own special rules.
+ if( auto gsStreamType = type->As<HLSLStreamOutputType>() )
+ {
+ //
+
+ auto elementType = gsStreamType->getElementType();
+
+ int semanticIndex = 0;
+
+ EntryPointParameterState elementState;
+ elementState.directionMask = kEntryPointParameterDirection_Output;
+ elementState.ioSemanticIndex = &semanticIndex;
+ elementState.isSampleRate = false;
+ elementState.optSemanticName = nullptr;
+ elementState.semanticSlotCount = 0;
+ elementState.stage = state.stage;
+
+ auto elementTypeLayout = processEntryPointParameter(context, elementType, elementState, nullptr);
+
+ RefPtr<StreamOutputTypeLayout> typeLayout = new StreamOutputTypeLayout();
+ typeLayout->type = type;
+ typeLayout->rules = elementTypeLayout->rules;
+ typeLayout->elementTypeLayout = elementTypeLayout;
+
+ for(auto resInfo : elementTypeLayout->resourceInfos)
+ typeLayout->addResourceUsage(resInfo);
+
+ return typeLayout;
+ }
+
// 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).
@@ -1733,11 +1769,6 @@ static RefPtr<TypeLayout> processEntryPointParameter(
varLayout->flags |= VarLayoutFlag::HasSemantic;
}
- if (varLayout)
- {
- varLayout->stage = state.stage;
- }
-
// Scalar and vector types are treated as outputs directly
if(auto basicType = type->As<BasicExpressionType>())
{
diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp
index b0be58274..708b98f2b 100644
--- a/source/slang/reflection.cpp
+++ b/source/slang/reflection.cpp
@@ -115,6 +115,10 @@ SLANG_API SlangTypeKind spReflectionType_GetKind(SlangReflectionType* inType)
{
return SLANG_TYPE_KIND_CONSTANT_BUFFER;
}
+ else if( auto streamOutputType = type->As<HLSLStreamOutputType>() )
+ {
+ return SLANG_TYPE_KIND_OUTPUT_STREAM;
+ }
else if (type->As<TextureBufferType>())
{
return SLANG_TYPE_KIND_TEXTURE_BUFFER;
diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h
index 66f6025b4..3c2797d48 100644
--- a/source/slang/type-layout.h
+++ b/source/slang/type-layout.h
@@ -353,6 +353,14 @@ public:
size_t uniformStride;
};
+// type layout for a variable with stream-output type
+class StreamOutputTypeLayout : public TypeLayout
+{
+public:
+ RefPtr<TypeLayout> elementTypeLayout;
+};
+
+
// When storing the layout for a matrix-type
// value, we need to know whether it has been
// laid ot with row-major or column-major