diff options
| -rw-r--r-- | slang.h | 1 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 8 | ||||
| -rw-r--r-- | source/slang/core.meta.slang.h | 8 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 74 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang | 11 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang.h | 11 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 440 | ||||
| -rw-r--r-- | source/slang/parameter-binding.cpp | 43 | ||||
| -rw-r--r-- | source/slang/reflection.cpp | 4 | ||||
| -rw-r--r-- | source/slang/type-layout.h | 8 |
10 files changed, 570 insertions, 38 deletions
@@ -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 |
