summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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