diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-08-08 17:23:03 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-08 17:23:03 -0400 |
| commit | 41247c3942210df33b9e3dd733eafb23573a4f2f (patch) | |
| tree | a49a88e680078bd379fe183cf826d49d7f935737 /source | |
| parent | c1cc93dd962a6db6c839341f11d2654cf0e62e37 (diff) | |
WIP: Preliminary Slang -> C++ code generation (#1009)
* Expanded prelude for some other resource types. Disable C++ output for ParameterGroup.
* WIP: Layout for CPU.
* Fixes to CPU layout.
* WIP: The uniform is output, but the variable definition is not.
* WIP: Entry point parameters to global scope in C++.
Handling of resource types (in so far as outputting)
* Some discussion of ABI and different input types.
* WIP: More C++ support around resource types.
* WIP: Split up variables into different structures on emit.
* WIP: Emitting C++ with wrapping up of 'Context'
* WIP: C++ code has access to semantic values.
Wrap in struct so can use method calls to pass shared state.
Disable legalizeResourceTypes and legalizeExistentialTypeLayout
* Fix structured buffer layout for CPU.
* Remove testing/handling of global uniforms on CPU path.
Typo fix.
Changed CPU tests to use new CPU calling convention.
* Check globals are working. Initalize context to zero globals.
* Order the global parameters for C++ ouput by their layout.
Note - that layout isn't quite working correctly because the StructuredBuffer<int> the int seems to be consuming uniform space.
* Work around for reflection not having all data needed for layout ordering for C++ code.
* Output constant buffers as pointers.
* Entry point parameters accessed through pointer to struct.
* WIP: Layout for CPU is reasonable for test case.
* Only output 'f' after float literal if type marks as a float.
* Cast construction works on C++.
* Made IntrinsicOp::ConvertConstruct to make intent clearer.
* C++ handling construction from scalar.
Handle access of a scalar with .x.
Check default initialization.
* Comment about need for split of kIROp_construct.
Release build works.
* Added support from constructVectorFromScalar to C/C++ target.
* Handling of in/out in C/C++.
* First pass documentation CPU support.
* Improvements to C++/C slang code generation documentation.
* Small doc change to include need for mechansim to specify cpp compiler path.
* Better handling of swizzling - allow swizzling a scalar into a vector.
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 10 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 23 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 1014 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 38 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 102 | ||||
| -rw-r--r-- | source/slang/slang-ir-entry-point-uniforms.cpp | 33 | ||||
| -rw-r--r-- | source/slang/slang-ir-entry-point-uniforms.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-parameter-binding.cpp | 37 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.cpp | 230 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.h | 12 |
10 files changed, 1313 insertions, 191 deletions
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 26af7b9f4..870523a3b 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -705,7 +705,7 @@ void CLikeSourceEmitter::emitDeclarator(IRDeclaratorInfo* declarator) } } -void CLikeSourceEmitter::emitSimpleValue(IRInst* inst) +void CLikeSourceEmitter::emitSimpleValueImpl(IRInst* inst) { switch(inst->op) { @@ -927,7 +927,7 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst) return true; } -void CLikeSourceEmitter::emitOperand(IRInst* inst, EmitOpInfo const& outerPrec) +void CLikeSourceEmitter::emitOperandImpl(IRInst* inst, EmitOpInfo const& outerPrec) { if( shouldFoldInstIntoUseSites(inst) ) { @@ -2541,7 +2541,7 @@ void CLikeSourceEmitter::emitSimpleFuncParamImpl(IRParam* param) emitSemantics(param); } -void CLikeSourceEmitter::emitSimpleFunc(IRFunc* func) +void CLikeSourceEmitter::emitSimpleFuncImpl(IRFunc* func) { auto resultType = func->getResultType(); @@ -2592,7 +2592,7 @@ void CLikeSourceEmitter::emitSimpleFunc(IRFunc* func) } } -void CLikeSourceEmitter::emitParamType(IRType* type, String const& name) +void CLikeSourceEmitter::emitParamTypeImpl(IRType* type, String const& name) { // An `out` or `inout` parameter will have been // encoded as a parameter of pointer type, so @@ -3242,7 +3242,7 @@ void CLikeSourceEmitter::executeEmitActions(List<EmitAction> const& actions) } } -void CLikeSourceEmitter::emitModule(IRModule* module) +void CLikeSourceEmitter::emitModuleImpl(IRModule* module) { // The IR will usually come in an order that respects // dependencies between global declarations, but this diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index 926a67e31..ee906010b 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -86,13 +86,13 @@ public: EmitVarChain* next; EmitVarChain() - : varLayout(0) - , next(0) + : varLayout(nullptr) + , next(nullptr) {} EmitVarChain(VarLayout* varLayout) : varLayout(varLayout) - , next(0) + , next(nullptr) {} EmitVarChain(VarLayout* varLayout, EmitVarChain* next) @@ -175,11 +175,11 @@ public: String getName(IRInst* inst); void emitDeclarator(IRDeclaratorInfo* declarator); - void emitSimpleValue(IRInst* inst); + void emitSimpleValue(IRInst* inst) { emitSimpleValueImpl(inst); } bool shouldFoldInstIntoUseSites(IRInst* inst); - void emitOperand(IRInst* inst, EmitOpInfo const& outerPrec); + void emitOperand(IRInst* inst, EmitOpInfo const& outerPrec) { emitOperandImpl(inst, outerPrec); } void emitArgs(IRInst* inst); @@ -218,7 +218,7 @@ public: void emitSemantics(VarLayout* varLayout); void emitSemantics(IRInst* inst); - VarLayout* getVarLayout(IRInst* var); + static VarLayout* getVarLayout(IRInst* var); void emitLayoutSemantics(IRInst* inst, char const* uniformSemanticSpelling = "register"); @@ -247,9 +247,9 @@ public: /// Emit high-level statements for the body of a function. void emitFunctionBody(IRGlobalValueWithCode* code); - void emitSimpleFunc(IRFunc* func); + void emitSimpleFunc(IRFunc* func) { emitSimpleFuncImpl(func); } - void emitParamType(IRType* type, String const& name); + void emitParamType(IRType* type, String const& name) { emitParamTypeImpl(type, name); } IRInst* getSpecializedValue(IRSpecialize* specInst); @@ -305,7 +305,7 @@ public: void computeEmitActions(IRModule* module, List<EmitAction>& ioActions); void executeEmitActions(List<EmitAction> const& actions); - void emitModule(IRModule* module); + void emitModule(IRModule* module) { emitModuleImpl(module); } void emitPreprocessorDirectives() { emitPreprocessorDirectivesImpl(); } void emitSimpleType(IRType* type); @@ -335,6 +335,11 @@ public: virtual void emitVarDecorationsImpl(IRInst* varDecl) { SLANG_UNUSED(varDecl); } virtual void emitMatrixLayoutModifiersImpl(VarLayout* layout) { SLANG_UNUSED(layout); } virtual void emitTypeImpl(IRType* type, const StringSliceLoc* nameLoc); + virtual void emitSimpleValueImpl(IRInst* inst); + virtual void emitModuleImpl(IRModule* module); + virtual void emitSimpleFuncImpl(IRFunc* func); + virtual void emitOperandImpl(IRInst* inst, EmitOpInfo const& outerPrec); + virtual void emitParamTypeImpl(IRType* type, String const& name); // Only needed for glsl output with $ prefix intrinsics - so perhaps removable in the future virtual void emitTextureOrTextureSamplerTypeImpl(IRTextureTypeBase* type, char const* baseName) { SLANG_UNUSED(type); SLANG_UNUSED(baseName); } diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index 2ec087eef..0228955dc 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -10,6 +10,69 @@ #include <assert.h> +/* +ABI +--- + +In terms of ABI we need to discuss the variety of variables/resources that need to be defined by the host for appropriate execution +of the output code. + +https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-syntax + +Broadly we could categorize these as.. + +1) Varying entry point parameters (or 'varying') +2) Uniform entry point parameters +3) Uniform globals +4) Thread shared (such as group shared) or ('thread shared') +5) Thread local ('static') + +If we can invoke a bunch of threads as a single invocation we could effectively have the ThreadShared not part of the ABI, but something +that is say allocated on the stack before the threads are kicked off. If we kick of threads individually then we would need to pass this +in as part of ABI. NOTE that it isn't right in so far as memory barriers etc couldn't work, as each thread would run to completion, but +we aren't going to worry about barriers for now. + +On 1 - there could be potentially input and outputs (perhaps in out?). On CPU I guess that's fine. + +On 2 and 3 they are effectively the same, and so for now 2+3 will be referred to together as 'uniforms'. +They should be copied into a single structure that has a well known order. + +On 1 these are parameters that vary on an invocation. Thus a caller might call many times with same globals structure +and different varying entry point parameters. + +On 5 - This would be a global that can be set and then accessed within the context of single thread + +So in order of rate of change + +1 : Probably change on every invocation (in the future such an invocation might be behind the API) +2 + 3 : Changes per group of 'threads' executed together +4 : Does not change between invocations +5 : Could be placed on the stack, and so not necessarily part of the ABI + +For now we are only going to implement something 'Compute shader'-like. Doing so makes the varying parameter always the same. + +So for now we would need to pass in + +ComputeVaryingInput - Fixed because we are doing compute shader +Uniform - All the uniform data in a big blob, both from uniform entry point parameters, and uniform globals + +When called we can have a structure that holds the thread local variables, and these two pointers. + + +We can stick pointers to these in a structure lets call it 'Context'. On C++ we could make all the functions 'methods', and then +we don't need to pass around the context as a parameter. For C this doesn't work, so it might be worth just biting the bullet and +just adding the context to the output. + +Issues: + +* How does this work with layout? The layout if it's going to specify offsets will need to know that they will be allocated into each +of these structs AND that the order they are placed needs to be consistent. + +* When variables access one of these sources, we will now need code that will add the dereferencing. Hopefully this can be done by looking +at the type of the variable, and then adding the appropriate access via part of emit. + +*/ + namespace Slang { static const char s_elemNames[] = "xyzw"; @@ -345,7 +408,23 @@ UnownedStringSlice CPPSourceEmitter::_getTypeName(IRType* inType) return m_slicePool.getSlice(handle); } - handle = _calcTypeName(type); + if (type->op == kIROp_MatrixType) + { + auto matType = static_cast<IRMatrixType*>(type); + + auto elementType = matType->getElementType(); + const auto rowCount = int(GetIntVal(matType->getRowCount())); + const auto colCount = int(GetIntVal(matType->getColumnCount())); + + // Make sure the vector type the matrix is built on is added + useType(_getVecType(elementType, colCount)); + } + + StringBuilder builder; + if (SLANG_SUCCEEDED(_calcTypeName(type, m_target, builder))) + { + handle = m_slicePool.add(builder); + } m_typeNameMap.Add(type, handle); @@ -353,14 +432,63 @@ UnownedStringSlice CPPSourceEmitter::_getTypeName(IRType* inType) return m_slicePool.getSlice(handle); } -StringSlicePool::Handle CPPSourceEmitter::_calcTypeName(IRType* type) +SlangResult CPPSourceEmitter::_calcTextureTypeName(IRTextureTypeBase* texType, StringBuilder& outName) +{ + switch (texType->getAccess()) + { + case SLANG_RESOURCE_ACCESS_READ: + break; + case SLANG_RESOURCE_ACCESS_READ_WRITE: + outName << "RW"; + break; + case SLANG_RESOURCE_ACCESS_RASTER_ORDERED: + outName << "RasterizerOrdered"; + break; + case SLANG_RESOURCE_ACCESS_APPEND: + outName << "Append"; + break; + case SLANG_RESOURCE_ACCESS_CONSUME: + outName << "Consume"; + break; + default: + SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled resource access mode"); + return SLANG_FAIL; + } + + switch (texType->GetBaseShape()) + { + case TextureFlavor::Shape::Shape1D: outName << "Texture1D"; break; + case TextureFlavor::Shape::Shape2D: outName << "Texture2D"; break; + case TextureFlavor::Shape::Shape3D: outName << "Texture3D"; break; + case TextureFlavor::Shape::ShapeCube: outName << "TextureCube"; break; + case TextureFlavor::Shape::ShapeBuffer: outName << "Buffer"; break; + default: + SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled resource shape"); + return SLANG_FAIL; + } + + if (texType->isMultisample()) + { + outName << "MS"; + } + if (texType->isArray()) + { + outName << "Array"; + } + outName << "<" << _getTypeName(texType->getElementType()) << " >"; + + return SLANG_OK; +} + +SlangResult CPPSourceEmitter::_calcTypeName(IRType* type, CodeGenTarget target, StringBuilder& out) { switch (type->op) { case kIROp_HalfType: { // Special case half - return m_slicePool.add(getBuiltinTypeName(kIROp_FloatType)); + out << getBuiltinTypeName(kIROp_FloatType); + return SLANG_OK; } case kIROp_VectorType: { @@ -368,26 +496,23 @@ StringSlicePool::Handle CPPSourceEmitter::_calcTypeName(IRType* type) auto vecCount = int(GetIntVal(vecType->getElementCount())); const IROp elemType = vecType->getElementType()->op; - if (m_target == CodeGenTarget::CPPSource) + if (target == CodeGenTarget::CPPSource) { - StringBuilder builder; - builder << "Vector<" << getBuiltinTypeName(elemType) << ", " << vecCount << ">"; - return m_slicePool.add(builder); + out << "Vector<" << getBuiltinTypeName(elemType) << ", " << vecCount << ">"; } else { - StringBuilder builder; - builder << "Vec"; + out << "Vec"; UnownedStringSlice postFix = _getCTypeVecPostFix(elemType); - builder << postFix; + out << postFix; if (postFix.size() > 1) { - builder << "_"; + out << "_"; } - builder << vecCount; - return m_slicePool.add(builder); + out << vecCount; } + return SLANG_OK; } case kIROp_MatrixType: { @@ -397,42 +522,32 @@ StringSlicePool::Handle CPPSourceEmitter::_calcTypeName(IRType* type) const auto rowCount = int(GetIntVal(matType->getRowCount())); const auto colCount = int(GetIntVal(matType->getColumnCount())); - if (m_target == CodeGenTarget::CPPSource) + if (target == CodeGenTarget::CPPSource) { - StringBuilder builder; - builder << "Matrix<" << getBuiltinTypeName(elementType->op) << ", " << rowCount << ", " << colCount << ">"; - return m_slicePool.add(builder); + out << "Matrix<" << getBuiltinTypeName(elementType->op) << ", " << rowCount << ", " << colCount << ">"; } else { - // Make sure there is the vector name too - _getTypeName(_getVecType(elementType, colCount)); - - StringBuilder builder; - - builder << "Mat"; + out << "Mat"; const UnownedStringSlice postFix = _getCTypeVecPostFix(_getCType(elementType->op)); - builder << postFix; + out << postFix; if (postFix.size() > 1) { - builder << "_"; + out << "_"; } - builder << rowCount; - builder << colCount; - - return m_slicePool.add(builder); + out << rowCount; + out << colCount; } + return SLANG_OK; } case kIROp_HLSLRWStructuredBufferType: { auto bufType = static_cast<IRHLSLRWStructuredBufferType*>(type); - StringBuilder builder; - builder << "RWStructuredBuffer<"; - builder << _getTypeName(bufType->getElementType()); - builder << ">"; - - return m_slicePool.add(builder); + out << "RWStructuredBuffer<"; + SLANG_RETURN_ON_FAIL(_calcTypeName(bufType->getElementType(), target, out)); + out << ">"; + return SLANG_OK; } case kIROp_ArrayType: { @@ -440,24 +555,44 @@ StringSlicePool::Handle CPPSourceEmitter::_calcTypeName(IRType* type) auto elementType = arrayType->getElementType(); int elementCount = int(GetIntVal(arrayType->getElementCount())); - StringBuilder builder; - builder << "FixedArray<"; - builder << _getTypeName(elementType); - builder << ", " << elementCount << ">"; - - return m_slicePool.add(builder); + out << "FixedArray<"; + SLANG_RETURN_ON_FAIL(_calcTypeName(elementType, target, out)); + out << ", " << elementCount << ">"; + return SLANG_OK; + } + case kIROp_SamplerStateType: + { + out << "SamplerState"; + return SLANG_OK; + } + case kIROp_SamplerComparisonStateType: + { + out << "SamplerComparisonState"; + return SLANG_OK; } default: { if (IRBasicType::isaImpl(type->op)) { - return m_slicePool.add(getBuiltinTypeName(type->op)); + out << getBuiltinTypeName(type->op); + return SLANG_OK; } + + if (auto texType = as<IRTextureTypeBase>(type)) + { + // We don't support TextureSampler, so ignore that + if (texType->op != kIROp_TextureSamplerType) + { + return _calcTextureTypeName(texType, out); + } + } + break; } } - return StringSlicePool::kNullHandle; + SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unhandled type for C/C++ emit"); + return SLANG_FAIL; } void CPPSourceEmitter::useType(IRType* type) @@ -615,8 +750,12 @@ static IRBasicType* _getElementType(IRType* type) static bool _isOperator(const UnownedStringSlice& funcName) { - const char c = funcName[0]; - return !((c >= 'a' && c <='z') || (c >= 'A' && c <= 'Z') || c == '_'); + if (funcName.size() > 0) + { + const char c = funcName[0]; + return !((c >= 'a' && c <='z') || (c >= 'A' && c <= 'Z') || c == '_'); + } + return false; } void CPPSourceEmitter::_emitAryDefinition(const SpecializedIntrinsic& specOp) @@ -989,6 +1128,130 @@ void CPPSourceEmitter::_emitNormalizeDefinition(const UnownedStringSlice& funcNa writer->emit("}\n\n"); } +void CPPSourceEmitter::_emitConstructConvertDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp) +{ + SourceWriter* writer = getSourceWriter(); + IRFuncType* funcType = specOp.signatureType; + + SLANG_ASSERT(funcType->getParamCount() == 2); + + IRType* srcType = funcType->getParamType(1); + IRType* retType = specOp.returnType; + + emitType(retType); + writer->emit(" "); + writer->emit(funcName); + writer->emit("("); + emitType(srcType); + writer->emitChar(' '); + writer->emitChar(char('a' + 0)); + writer->emit(")"); + + writer->emit("\n{\n"); + writer->indent(); + + writer->emit("return "); + emitType(retType); + writer->emit("{ "); + + IRType* dstElemType = _getElementType(retType); + //IRType* srcElemType = _getElementType(srcType); + + TypeDimension dim = _getTypeDimension(srcType, false); + + for (int i = 0; i < dim.rowCount; ++i) + { + if (dim.rowCount > 1) + { + if (i > 0) + { + writer->emit(", \n"); + } + writer->emit("{ "); + } + + for (int j = 0; j < dim.colCount; ++j) + { + if (j > 0) + { + writer->emit(", "); + } + + emitType(dstElemType); + writer->emit("("); + _emitAccess(UnownedStringSlice::fromLiteral("a"), dim, i, j, writer); + writer->emit(")"); + } + if (dim.rowCount > 1) + { + writer->emit("}"); + } + } + + writer->emit("};\n"); + + writer->dedent(); + writer->emit("}\n\n"); +} + +void CPPSourceEmitter::_emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp) +{ + SourceWriter* writer = getSourceWriter(); + IRFuncType* funcType = specOp.signatureType; + + SLANG_ASSERT(funcType->getParamCount() == 2); + + IRType* srcType = funcType->getParamType(1); + IRType* retType = specOp.returnType; + + emitType(retType); + writer->emit(" "); + writer->emit(funcName); + writer->emit("("); + emitType(srcType); + writer->emitChar(' '); + writer->emitChar(char('a' + 0)); + writer->emit(")"); + + writer->emit("\n{\n"); + writer->indent(); + + writer->emit("return "); + emitType(retType); + writer->emit("{ "); + + const TypeDimension dim = _getTypeDimension(retType, false); + + for (int i = 0; i < dim.rowCount; ++i) + { + if (dim.rowCount > 1) + { + if (i > 0) + { + writer->emit(", \n"); + } + writer->emit("{ "); + } + for (int j = 0; j < dim.colCount; ++j) + { + if (j > 0) + { + writer->emit(", "); + } + writer->emit("a"); + } + if (dim.rowCount > 1) + { + writer->emit("}"); + } + } + + writer->emit("};\n"); + + writer->dedent(); + writer->emit("}\n\n"); +} + void CPPSourceEmitter::_emitReflectDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp) { SourceWriter* writer = getSourceWriter(); @@ -1061,6 +1324,14 @@ void CPPSourceEmitter::emitSpecializedOperationDefinition(const SpecializedIntri { return _emitReflectDefinition(_getFuncName(specOp), specOp); } + case IntrinsicOp::ConstructConvert: + { + return _emitConstructConvertDefinition(_getFuncName(specOp), specOp); + } + case IntrinsicOp::ConstructFromScalar: + { + return _emitConstructFromScalarDefinition(_getFuncName(specOp), specOp); + } default: { const auto& info = getOperationInfo(specOp.op); @@ -1183,47 +1454,40 @@ void CPPSourceEmitter::emitCall(const SpecializedIntrinsic& specOp, IRInst* inst } case IntrinsicOp::Swizzle: { - // For C++ we don't need to emit a swizzle function - // For C we need a construction function + // Currently only works for C++ (we use {} constuction) - which means we don't need to generate a function. + // For C we need to generate suitable construction function auto swizzleInst = static_cast<IRSwizzle*>(inst); const Index elementCount = Index(swizzleInst->getElementCount()); - if (elementCount == 1) - { - defaultEmitInstExpr(inst, inOuterPrec); - } - else - { - // TODO(JS): Not sure this is correct on the parens handling front - IRType* retType = specOp.returnType; - emitType(retType); - writer->emit("{"); + // TODO(JS): Not 100% sure this is correct on the parens handling front + IRType* retType = specOp.returnType; + emitType(retType); + writer->emit("{"); - for (Index i = 0; i < elementCount; ++i) + for (Index i = 0; i < elementCount; ++i) + { + if (i > 0) { - if (i > 0) - { - writer->emit(", "); - } - - auto outerPrec = getInfo(EmitOp::General); + writer->emit(", "); + } - auto prec = getInfo(EmitOp::Postfix); - emitOperand(swizzleInst->getBase(), leftSide(outerPrec, prec)); + auto outerPrec = getInfo(EmitOp::General); - writer->emit("."); + auto prec = getInfo(EmitOp::Postfix); + emitOperand(swizzleInst->getBase(), leftSide(outerPrec, prec)); - IRInst* irElementIndex = swizzleInst->getElementIndex(i); - SLANG_RELEASE_ASSERT(irElementIndex->op == kIROp_IntLit); - IRConstant* irConst = (IRConstant*)irElementIndex; - UInt elementIndex = (UInt)irConst->value.intVal; - SLANG_RELEASE_ASSERT(elementIndex < 4); + writer->emit("."); - writer->emitChar(s_elemNames[elementIndex]); - } + IRInst* irElementIndex = swizzleInst->getElementIndex(i); + SLANG_RELEASE_ASSERT(irElementIndex->op == kIROp_IntLit); + IRConstant* irConst = (IRConstant*)irElementIndex; + UInt elementIndex = (UInt)irConst->value.intVal; + SLANG_RELEASE_ASSERT(elementIndex < 4); - writer->emit("}"); + writer->emitChar(s_elemNames[elementIndex]); } + + writer->emit("}"); break; } default: @@ -1302,6 +1566,46 @@ StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const SpecializedIntrins } else { + switch (specOp.op) + { + case IntrinsicOp::ConstructConvert: + { + // Work out the function name + IRFuncType* signatureType = specOp.signatureType; + SLANG_ASSERT(signatureType->getParamCount() == 2); + + IRType* dstType = signatureType->getParamType(0); + //IRType* srcType = signatureType->getParamType(1); + + StringBuilder builder; + builder << "convert_"; + // I need a function that is called that will construct this + if (SLANG_FAILED(_calcTypeName(dstType, CodeGenTarget::CSource, builder))) + { + return StringSlicePool::kNullHandle; + } + return m_slicePool.add(builder); + } + case IntrinsicOp::ConstructFromScalar: + { + // Work out the function name + IRFuncType* signatureType = specOp.signatureType; + SLANG_ASSERT(signatureType->getParamCount() == 2); + + IRType* dstType = signatureType->getParamType(0); + + StringBuilder builder; + builder << "constructFromScalar_"; + // I need a function that is called that will construct this + if (SLANG_FAILED(_calcTypeName(dstType, CodeGenTarget::CSource, builder))) + { + return StringSlicePool::kNullHandle; + } + return m_slicePool.add(builder); + } + default: break; + } + const auto& info = getOperationInfo(specOp.op); if (info.funcName.size()) { @@ -1316,6 +1620,38 @@ StringSlicePool::Handle CPPSourceEmitter::_calcFuncName(const SpecializedIntrins void CPPSourceEmitter::emitOperationCall(IntrinsicOp op, IRInst* inst, IRUse* operands, int operandCount, IRType* retType, const EmitOpInfo& inOuterPrec) { + switch (op) + { + case IntrinsicOp::ConstructFromScalar: + { + SLANG_ASSERT(operandCount == 1); + IRType* dstType = inst->getDataType(); + IRType* srcType = _getElementType(dstType); + IRType* argTypes[2] = { dstType, srcType }; + + SpecializedIntrinsic specOp = getSpecializedOperation(op, argTypes, 2, retType); + + emitCall(specOp, inst, operands, operandCount, inOuterPrec); + return; + } + case IntrinsicOp::ConstructConvert: + { + SLANG_ASSERT(inst->getOperandCount() == 1); + IRType* argTypes[2] = {inst->getDataType(), inst->getOperand(0)->getDataType() }; + + SpecializedIntrinsic specOp = getSpecializedOperation(op, argTypes, 2, retType); + + IRFuncType* signatureType = specOp.signatureType; + SLANG_UNUSED(signatureType); + + SLANG_ASSERT(signatureType->getParamType(0) != signatureType->getParamType(1)); + + emitCall(specOp, inst, operands, operandCount, inOuterPrec); + return; + } + default: break; + } + if (operandCount > 8) { List<IRType*> argTypes; @@ -1381,11 +1717,94 @@ CPPSourceEmitter::CPPSourceEmitter(const Desc& desc): } } -void CPPSourceEmitter::emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type) +void CPPSourceEmitter::_emitInOutParamType(IRType* type, String const& name, IRType* valueType) +{ + StringSliceLoc nameAndLoc(name.getUnownedSlice()); + + if (auto refType = as<IRRefType>(type)) + { + m_writer->emit("const "); + } + + UnownedStringSlice slice = _getTypeName(valueType); + m_writer->emit(slice); + m_writer->emit("& "); + m_writer->emitName(nameAndLoc); +} + +void CPPSourceEmitter::emitParamTypeImpl(IRType* type, String const& name) +{ + // An `out` or `inout` parameter will have been + // encoded as a parameter of pointer type, so + // we need to decode that here. + // + if (auto outType = as<IROutType>(type)) + { + return _emitInOutParamType(type, name, outType->getValueType()); + } + else if (auto inOutType = as<IRInOutType>(type)) + { + return _emitInOutParamType(type, name, inOutType->getValueType()); + } + else if (auto refType = as<IRRefType>(type)) + { + return _emitInOutParamType(type, name, refType->getValueType()); + } + + emitType(type, name); +} + +bool CPPSourceEmitter::tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) { SLANG_UNUSED(varDecl); - SLANG_UNUSED(type); - SLANG_ASSERT(!"Not implemented"); + SLANG_UNUSED(varType); + + switch (varType->op) + { + case kIROp_StructType: + { + String name = getName(varDecl); + + UnownedStringSlice typeName = _getTypeName(varType); + m_writer->emit(typeName); + m_writer->emit("* "); + m_writer->emit(name); + m_writer->emit(";\n"); + return true; + } + } + + return false; +} + +void CPPSourceEmitter::emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type) +{ + // Output global parameters + auto varLayout = getVarLayout(varDecl); + SLANG_RELEASE_ASSERT(varLayout); + + String name = getName(varDecl); + auto elementType = type->getElementType(); + + switch (type->op) + { + case kIROp_ParameterBlockType: + case kIROp_ConstantBufferType: + { + UnownedStringSlice typeName = _getTypeName(elementType); + m_writer->emit(typeName); + m_writer->emit("* "); + m_writer->emit(name); + m_writer->emit(";\n"); + break; + } + default: + { + emitType(elementType, name); + m_writer->emit(";\n"); + break; + } + } } void CPPSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, EntryPointLayout* entryPointLayout) @@ -1426,6 +1845,87 @@ void CPPSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, EntryPointLa m_writer->emit("SLANG_PRELUDE_EXPORT\n"); } +void CPPSourceEmitter::emitSimpleFuncImpl(IRFunc* func) +{ + auto resultType = func->getResultType(); + + auto name = getFuncName(func); + + // Deal with decorations that need + // to be emitted as attributes + + // We are going to ignore the parameters passed and just pass in the Context + + auto entryPointLayout = asEntryPoint(func); + if (entryPointLayout) + { + StringBuilder prefixName; + prefixName << "_" << name; + emitType(resultType, prefixName); + m_writer->emit("()\n"); + } + else + { + emitType(resultType, name); + + m_writer->emit("("); + auto firstParam = func->getFirstParam(); + for (auto pp = firstParam; pp; pp = pp->getNextParam()) + { + if (pp != firstParam) + m_writer->emit(", "); + + emitSimpleFuncParamImpl(pp); + } + m_writer->emit(")"); + + emitSemantics(func); + } + + // TODO: encode declaration vs. definition + if (isDefinition(func)) + { + m_writer->emit("\n{\n"); + m_writer->indent(); + + // HACK: forward-declare all the local variables needed for the + // parameters of non-entry blocks. + emitPhiVarDecls(func); + + // Need to emit the operations in the blocks of the function + emitFunctionBody(func); + + m_writer->dedent(); + m_writer->emit("}\n\n"); + } + else + { + m_writer->emit(";\n\n"); + } +} + +void CPPSourceEmitter::emitSimpleValueImpl(IRInst* inst) +{ + switch (inst->op) + { + case kIROp_FloatLit: + { + IRConstant* constantInst = static_cast<IRConstant*>(inst); + + m_writer->emit(constantInst->value.floatVal); + + // If the literal is a float, then we need to add 'f' at end + IRType* type = constantInst->getDataType(); + if (type && type->op == kIROp_FloatType ) + { + m_writer->emitChar('f'); + } + break; + } + default: Super::emitSimpleValueImpl(inst); + } +} + void CPPSourceEmitter::emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) { emitSimpleType(_getVecType(elementType, int(elementCount))); @@ -1578,7 +2078,44 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut switch (inst->op) { + case kIROp_constructVectorFromScalar: + { + SLANG_ASSERT(inst->getOperandCount() == 1); + IRType* dstType = inst->getDataType(); + + // Check it's a vector + SLANG_ASSERT(dstType->op == kIROp_VectorType); + // Source must be a scalar + SLANG_ASSERT(as<IRBasicType>(inst->getOperand(0)->getDataType())); + + emitOperationCall(IntrinsicOp::ConstructFromScalar, inst, inst->getOperands(), int(inst->getOperandCount()), dstType, inOuterPrec); + return true; + } case kIROp_Construct: + { + IRType* dstType = inst->getDataType(); + IRType* srcType = inst->getOperand(0)->getDataType(); + + if ((dstType->op == kIROp_VectorType || dstType->op == kIROp_MatrixType) && + inst->getOperandCount() == 1) + { + if (as<IRBasicType>(srcType)) + { + emitOperationCall(IntrinsicOp::ConstructFromScalar, inst, inst->getOperands(), int(inst->getOperandCount()), dstType, inOuterPrec); + } + else + { + SLANG_ASSERT(_getElementType(dstType) != _getElementType(srcType)); + // If it's constructed from a type conversion + emitOperationCall(IntrinsicOp::ConstructConvert, inst, inst->getOperands(), int(inst->getOperandCount()), dstType, inOuterPrec); + } + } + else + { + emitOperationCall(IntrinsicOp::Init, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec); + } + return true; + } case kIROp_makeVector: case kIROp_MakeMatrix: { @@ -1599,7 +2136,44 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut } case kIROp_swizzle: { - emitOperationCall(IntrinsicOp::Swizzle, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec); + // For C++ we don't need to emit a swizzle function + // For C we need a construction function + auto swizzleInst = static_cast<IRSwizzle*>(inst); + + IRInst* baseInst = swizzleInst->getBase(); + IRType* baseType = baseInst->getDataType(); + + // If we are swizzling from a built in type, + if (as<IRBasicType>(baseType)) + { + // We can swizzle a scalar type to be a vector, or just a scalar + IRType* dstType = swizzleInst->getDataType(); + if (as<IRBasicType>(dstType)) + { + // If the output is a scalar, then could only have been a .x, which we can just ignore the '.x' part + emitOperand(baseInst, inOuterPrec); + } + else + { + SLANG_ASSERT(dstType->op == kIROp_VectorType); + emitOperationCall(IntrinsicOp::ConstructFromScalar, inst, inst->getOperands(), 1, dstType, inOuterPrec); + } + } + else + { + const Index elementCount = Index(swizzleInst->getElementCount()); + if (elementCount == 1) + { + // If just one thing is extracted then the . syntax will just work + defaultEmitInstExpr(inst, inOuterPrec); + } + else + { + // Will need to generate a swizzle method + emitOperationCall(IntrinsicOp::Swizzle, inst, inst->getOperands(), int(inst->getOperandCount()), inst->getDataType(), inOuterPrec); + } + } + return true; } case kIROp_Call: @@ -1619,6 +2193,7 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut return false; } + default: { IntrinsicOp op = getOperation(inst->op); @@ -1652,15 +2227,290 @@ void CPPSourceEmitter::emitPreprocessorDirectivesImpl() { emitSpecializedOperationDefinition(keyValue.Key); } +} + +void CPPSourceEmitter::emitOperandImpl(IRInst* inst, EmitOpInfo const& outerPrec) +{ + if (shouldFoldInstIntoUseSites(inst)) + { + emitInstExpr(inst, outerPrec); + return; + } + + switch (inst->op) + { + case 0: // nothing yet + case kIROp_GlobalParam: + { + // It's in UniformState + String name = getName(inst); + m_writer->emit("("); + switch (inst->getDataType()->op) + { + case kIROp_ParameterBlockType: + case kIROp_ConstantBufferType: + case kIROp_StructType: + { + m_writer->emit("*"); + break; + } + default: break; + } + m_writer->emit("uniformState->"); + m_writer->emit(name); + m_writer->emit(")"); + break; + } + case kIROp_Param: + { + auto varLayout = getVarLayout(inst); + + if (varLayout) + { + auto semanticNameSpelling = varLayout->systemValueSemantic; + if (semanticNameSpelling.getLength()) + { + semanticNameSpelling = semanticNameSpelling.toLower(); + + if (semanticNameSpelling == "sv_dispatchthreadid") + { + + m_writer->emit("dispatchThreadID"); + return; + } + else if (semanticNameSpelling == "sv_groupid") + { + m_writer->emit("varyingInput.groupID"); + return; + } + else if (semanticNameSpelling == "sv_groupthreadid") + { + m_writer->emit("varyingInput.groupThreadID"); + return; + } + } + } + + ; // Fall-thru + } + case kIROp_GlobalVar: + default: + // GlobalVar should be fine as should just be a member of Context + m_writer->emit(getName(inst)); + break; + } +} + +static bool _isVariable(IROp op) +{ + switch (op) + { + case kIROp_GlobalVar: + case kIROp_GlobalParam: + //case kIROp_Var: + { + return true; + } + default: return false; + } +} + +static bool _isFunction(IROp op) +{ + return op == kIROp_Func; +} + +struct GlobalParamInfo +{ + typedef GlobalParamInfo ThisType; + bool operator<(const ThisType& rhs) const { return offset < rhs.offset; } + bool operator==(const ThisType& rhs) const { return offset == rhs.offset; } + bool operator!=(const ThisType& rhs) const { return !(*this == rhs); } + + IRInst* inst; + UInt offset; + UInt size; +}; + +void CPPSourceEmitter::emitModuleImpl(IRModule* module) +{ + List<EmitAction> actions; + computeEmitActions(module, actions); + + // Emit forward declarations. Don't emit variables that need to be grouped or function definitions (which will ref those types) + for (auto action : actions) + { + switch (action.level) + { + case EmitAction::Level::ForwardDeclaration: + emitFuncDecl(cast<IRFunc>(action.inst)); + break; + + case EmitAction::Level::Definition: + if (_isVariable(action.inst->op) || _isFunction(action.inst->op)) + { + // Don't emit functions or variables that have to be grouped into structures yet + } + else + { + emitGlobalInst(action.inst); + } + break; + } + } - // Lets take a look at layout + // Output the global parameters in a 'UniformState' structure + { + m_writer->emit("struct UniformState\n{\n"); + m_writer->indent(); - ProgramLayout* programLayout = m_programLayout; + List<GlobalParamInfo> params; - if (programLayout) + for (auto action : actions) + { + if (action.level == EmitAction::Level::Definition && action.inst->op == kIROp_GlobalParam) + { + VarLayout* varLayout = CLikeSourceEmitter::getVarLayout(action.inst); + SLANG_ASSERT(varLayout); + const VarLayout::ResourceInfo* varInfo = varLayout->FindResourceInfo(LayoutResourceKind::Uniform); + TypeLayout* typeLayout = varLayout->getTypeLayout(); + TypeLayout::ResourceInfo* typeInfo = typeLayout->FindResourceInfo(LayoutResourceKind::Uniform); + + GlobalParamInfo paramInfo; + paramInfo.inst = action.inst; + // Index is the byte offset for uniform + paramInfo.offset = varInfo ? varInfo->index : 0; + paramInfo.size = typeInfo ? typeInfo->count.raw : 0; + + params.add(paramInfo); + } + } + + // We want to sort by layout offset, and insert suitable padding + params.sort(); + + int padIndex = 0; + size_t offset = 0; + for (const auto& paramInfo : params) + { + if (offset < paramInfo.offset) + { + // We want to output some padding + StringBuilder builder; + builder << "uint8_t _pad" << (padIndex++) << "[" << (paramInfo.offset - offset) << "];\n"; + } + + emitGlobalInst(paramInfo.inst); + // Set offset after this + offset = paramInfo.offset + paramInfo.size; + } + + m_writer->emit("\n"); + m_writer->dedent(); + m_writer->emit("\n};\n\n"); + } + + // Output the 'Context' which will be used for execution { + m_writer->emit("struct Context\n{\n"); + m_writer->indent(); + m_writer->emit("UniformState* uniformState;\n"); + m_writer->emit("ComputeVaryingInput varyingInput;\n"); + m_writer->emit("uint3 dispatchThreadID;\n"); + // Output all the thread locals + for (auto action : actions) + { + if (action.level == EmitAction::Level::Definition && action.inst->op == kIROp_GlobalVar) + { + emitGlobalInst(action.inst); + } + } + + // Finally output the functions as methods on the context + for (auto action : actions) + { + if (action.level == EmitAction::Level::Definition && _isFunction(action.inst->op)) + { + emitGlobalInst(action.inst); + } + } + + m_writer->dedent(); + m_writer->emit("};\n\n"); + } + + // Finally we need to output dll entry points + + for (auto action : actions) + { + if (action.level == EmitAction::Level::Definition && _isFunction(action.inst->op)) + { + IRFunc* func = as<IRFunc>(action.inst); + + auto entryPointLayout = asEntryPoint(func); + if (entryPointLayout) + { + auto resultType = func->getResultType(); + auto name = getFuncName(func); + + // Emit the actual function + emitEntryPointAttributes(func, entryPointLayout); + emitType(resultType, name); + + m_writer->emit("(ComputeVaryingInput* varyingInput, UniformState* uniformState)\n{\n"); + emitSemantics(func); + + m_writer->indent(); + // Initialize when constructing so that globals are zeroed + m_writer->emit("Context context = {};\n"); + m_writer->emit("context.uniformState = uniformState;\n"); + m_writer->emit("context.varyingInput = *varyingInput;\n"); + + // Emit dispatchThreadID + if (entryPointLayout->profile.GetStage() == Stage::Compute) + { + // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sv-dispatchthreadid + // SV_DispatchThreadID is the sum of SV_GroupID * numthreads and GroupThreadID. + + static const UInt kAxisCount = 3; + UInt sizeAlongAxis[kAxisCount]; + + // TODO: this is kind of gross because we are using a public + // reflection API function, rather than some kind of internal + // utility it forwards to... + spReflectionEntryPoint_getComputeThreadGroupSize((SlangReflectionEntryPoint*)entryPointLayout, kAxisCount, &sizeAlongAxis[0]); + + m_writer->emit("context.dispatchThreadID = {\n"); + m_writer->indent(); + + StringBuilder builder; + + for (int i = 0; i < kAxisCount; ++i) + { + builder.Clear(); + const char elem[2] = {s_elemNames[i], 0}; + builder << "varyingInput->groupID." << elem << " * " << sizeAlongAxis[i] << " + varyingInput->groupThreadID." << elem; + if (i < kAxisCount - 1) + { + builder << ","; + } + builder << "\n"; + m_writer->emit(builder); + } + + m_writer->dedent(); + m_writer->emit("};\n"); + } + + m_writer->emit("context._"); + m_writer->emit(name); + m_writer->emit("();\n"); + m_writer->dedent(); + m_writer->emit("}\n"); + } + } } } diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h index ea793ee95..4280bdc80 100644 --- a/source/slang/slang-emit-cpp.h +++ b/source/slang/slang-emit-cpp.h @@ -10,6 +10,24 @@ namespace Slang { +/* TODO(JS): Note that there are multiple methods to handle 'construction' operations. That is because 'construct' is used as a kind of +generic 'construction' for built in types including vectors and matrices. + +For the moment the cpp emit code, determines what kind of construct is needed, and has special handling for ConstructConvert and +ConstructFromScalar. + +That currently we do not see constructVectorFromScalar - for example when we do... + +int2 fromScalar = 1; + +This appears as a construction from an int. + +That the better thing to do would be that there were IR instructions for the specific types of construction. I suppose there is a question +about whether there should be separate instructions for vector/matrix, or emit code should just use the destination type. In practice I think +it's fine that there isn't an instruction separating vector/matrix. That being the case I guess we arguably don't need constructVectorFromScalar, +just constructXXXFromScalar. Would be good if there was a suitable name to encompass vector/matrix. +*/ + #define SLANG_CPP_INTRINSIC_OP(x) \ x(Invalid, "", -1) \ x(Init, "", -1) \ @@ -93,7 +111,10 @@ namespace Slang \ x(AsFloat, "asfloat", 1) \ x(AsInt, "asint", 1) \ - x(AsUInt, "asuint", 1) + x(AsUInt, "asuint", 1) \ + \ + x(ConstructConvert, "", 1) \ + x(ConstructFromScalar, "", 1) class CPPSourceEmitter: public CLikeSourceEmitter @@ -181,6 +202,13 @@ protected: virtual void emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) SLANG_OVERRIDE; virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) SLANG_OVERRIDE; virtual void emitPreprocessorDirectivesImpl() SLANG_OVERRIDE; + virtual void emitSimpleValueImpl(IRInst* value) SLANG_OVERRIDE; + virtual void emitModuleImpl(IRModule* module) SLANG_OVERRIDE; + virtual void emitSimpleFuncImpl(IRFunc* func) SLANG_OVERRIDE; + virtual void emitOperandImpl(IRInst* inst, EmitOpInfo const& outerPrec) SLANG_OVERRIDE; + virtual void emitParamTypeImpl(IRType* type, String const& name) SLANG_OVERRIDE; + + virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE; void emitIntrinsicCallExpr(IRCall* inst, IRFunc* func, EmitOpInfo const& inOuterPrec); @@ -194,9 +222,13 @@ protected: void _emitLengthDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); void _emitNormalizeDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); void _emitReflectDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); + void _emitConstructConvertDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); + void _emitConstructFromScalarDefinition(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); void _emitSignature(const UnownedStringSlice& funcName, const SpecializedIntrinsic& specOp); + void _emitInOutParamType(IRType* type, String const& name, IRType* valueType); + UnownedStringSlice _getAndEmitSpecializedOperationDefinition(IntrinsicOp op, IRType*const* argTypes, Int argCount, IRType* retType); static TypeDimension _getTypeDimension(IRType* type, bool vecSwap); @@ -216,6 +248,10 @@ protected: UnownedStringSlice _getTypeName(IRType* type); StringSlicePool::Handle _calcTypeName(IRType* type); + SlangResult _calcTypeName(IRType* type, CodeGenTarget target, StringBuilder& out); + + SlangResult _calcTextureTypeName(IRTextureTypeBase* texType, StringBuilder& outName); + Dictionary<SpecializedIntrinsic, StringSlicePool::Handle> m_intrinsicNameMap; Dictionary<IRType*, StringSlicePool::Handle> m_typeNameMap; diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index d5dcaca98..205f8ee0e 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -305,7 +305,7 @@ String emitEntryPoint( // parameters of a shader entry point and move them into // the global scope instead. // - moveEntryPointUniformParamsToGlobalScope(irModule); + moveEntryPointUniformParamsToGlobalScope(irModule, target); #if 0 dumpIRIfEnabled(compileRequest, irModule, "ENTRY POINT UNIFORMS MOVED"); #endif @@ -361,56 +361,60 @@ String emitEntryPoint( #endif validateIRModuleIfEnabled(compileRequest, irModule); - // The Slang language allows interfaces to be used like - // ordinary types (including placing them in constant - // buffers and entry-point parameter lists), but then - // getting them to lay out in a reasonable way requires - // us to treat fields/variables with interface type - // *as if* they were pointers to heap-allocated "objects." - // - // Specialization will have replaced fields/variables - // with interface types like `IFoo` with fields/variables - // with pointer-like types like `ExistentialBox<SomeType>`. - // - // We need to legalize these pointer-like types away, - // which involves two main changes: - // - // 1. Any `ExistentialBox<...>` fields need to be moved - // out of their enclosing `struct` type, so that the layout - // of the enclosing type is computed as if the field had - // zero size. - // - // 2. Once an `ExistentialBox<X>` has been floated out - // of its parent and landed somwhere permanent (e.g., either - // a dedicated variable, or a field of constant buffer), - // we need to replace it with just an `X`, after which we - // will have (more) legal shader code. - // - legalizeExistentialTypeLayout( - irModule, - sink); - eliminateDeadCode(compileRequest, irModule); - + // We don't need the legalize pass for C/C++ based types + if (!(sourceStyle == SourceStyle::CPP || sourceStyle == SourceStyle::C)) + { + // The Slang language allows interfaces to be used like + // ordinary types (including placing them in constant + // buffers and entry-point parameter lists), but then + // getting them to lay out in a reasonable way requires + // us to treat fields/variables with interface type + // *as if* they were pointers to heap-allocated "objects." + // + // Specialization will have replaced fields/variables + // with interface types like `IFoo` with fields/variables + // with pointer-like types like `ExistentialBox<SomeType>`. + // + // We need to legalize these pointer-like types away, + // which involves two main changes: + // + // 1. Any `ExistentialBox<...>` fields need to be moved + // out of their enclosing `struct` type, so that the layout + // of the enclosing type is computed as if the field had + // zero size. + // + // 2. Once an `ExistentialBox<X>` has been floated out + // of its parent and landed somwhere permanent (e.g., either + // a dedicated variable, or a field of constant buffer), + // we need to replace it with just an `X`, after which we + // will have (more) legal shader code. + // + legalizeExistentialTypeLayout( + irModule, + sink); + eliminateDeadCode(compileRequest, irModule); + #if 0 - dumpIRIfEnabled(compileRequest, irModule, "EXISTENTIALS LEGALIZED"); + dumpIRIfEnabled(compileRequest, irModule, "EXISTENTIALS LEGALIZED"); #endif - validateIRModuleIfEnabled(compileRequest, irModule); - - // Many of our target languages and/or downstream compilers - // don't support `struct` types that have resource-type fields. - // In order to work around this limitation, we will rewrite the - // IR so that any structure types with resource-type fields get - // split into a "tuple" that comprises the ordinary fields (still - // bundles up as a `struct`) and one element for each resource-type - // field (recursively). - // - // What used to be individual variables/parameters/arguments/etc. - // then become multiple variables/parameters/arguments/etc. - // - legalizeResourceTypes( - irModule, - sink); - eliminateDeadCode(compileRequest, irModule); + validateIRModuleIfEnabled(compileRequest, irModule); + + // Many of our target languages and/or downstream compilers + // don't support `struct` types that have resource-type fields. + // In order to work around this limitation, we will rewrite the + // IR so that any structure types with resource-type fields get + // split into a "tuple" that comprises the ordinary fields (still + // bundles up as a `struct`) and one element for each resource-type + // field (recursively). + // + // What used to be individual variables/parameters/arguments/etc. + // then become multiple variables/parameters/arguments/etc. + // + legalizeResourceTypes( + irModule, + sink); + eliminateDeadCode(compileRequest, irModule); + } // Debugging output of legalization #if 0 diff --git a/source/slang/slang-ir-entry-point-uniforms.cpp b/source/slang/slang-ir-entry-point-uniforms.cpp index 20e726f25..da036d798 100644 --- a/source/slang/slang-ir-entry-point-uniforms.cpp +++ b/source/slang/slang-ir-entry-point-uniforms.cpp @@ -98,6 +98,12 @@ struct MoveEntryPointUniformParametersToGlobalScope // IRModule* module; + // The target can determine how a variable is moved out into global scope + CodeGenTarget codeGenTarget; + + // If true the target needs constant buffer wrapping (for uniforms say) + bool targetNeedsConstantBuffer; + // We will process a whole module by visiting all // its global functions, looking for entry points. // @@ -162,7 +168,7 @@ struct MoveEntryPointUniformParametersToGlobalScope // an explicit IR constant buffer for that wrapper, // auto entryPointParamsLayout = entryPointLayout->parametersLayout; - bool needConstantBuffer = entryPointParamsLayout->typeLayout.is<ParameterGroupTypeLayout>(); + bool needConstantBuffer = targetNeedsConstantBuffer && entryPointParamsLayout->typeLayout.is<ParameterGroupTypeLayout>(); // We will set up an IR builder so that we are ready to generate code. // @@ -369,6 +375,10 @@ struct MoveEntryPointUniformParametersToGlobalScope return true; } + // TODO(JS): We probably want a more accurate way of determining if system semantic value + // We can use the flags Flag::SemanticValue for one. But main issue with this test, is for some + // targets currently (CPU) no resources are consumed. Perhaps this is fixed elsewhere by using a 'notional' resource. + // Varying parameters with "system value" semantics currently show up as // consuming no resources, so we need to special-case that here. // @@ -415,10 +425,29 @@ struct MoveEntryPointUniformParametersToGlobalScope }; void moveEntryPointUniformParamsToGlobalScope( - IRModule* module) + IRModule* module, + CodeGenTarget target) { MoveEntryPointUniformParametersToGlobalScope context; + context.module = module; + context.codeGenTarget = target; + context.targetNeedsConstantBuffer = true; + + // Check if this target needs constant buffer wrapping + switch (target) + { + case CodeGenTarget::CPPSource: + case CodeGenTarget::CSource: + case CodeGenTarget::Executable: + case CodeGenTarget::SharedLibrary: + { + context.targetNeedsConstantBuffer = false; + break; + } + default: break; + } + context.processModule(); } diff --git a/source/slang/slang-ir-entry-point-uniforms.h b/source/slang/slang-ir-entry-point-uniforms.h index 49994c202..0e978b9eb 100644 --- a/source/slang/slang-ir-entry-point-uniforms.h +++ b/source/slang/slang-ir-entry-point-uniforms.h @@ -1,12 +1,15 @@ // slang-ir-entry-point-uniform.h #pragma once +#include "slang-compiler.h" + namespace Slang { struct IRModule; /// Move any uniform parameters of entry points to the global scope instead. void moveEntryPointUniformParamsToGlobalScope( - IRModule* module); + IRModule* module, + CodeGenTarget target); } diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 722725af7..f9657e776 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -2632,6 +2632,21 @@ static int _calcTotalNumUsedRegistersForLayoutResourceKind(ParameterBindingConte return numUsed; } +static bool _isCPUTarget(CodeGenTarget target) +{ + switch (target) + { + case CodeGenTarget::CPPSource: + case CodeGenTarget::CSource: + case CodeGenTarget::Executable: + case CodeGenTarget::SharedLibrary: + { + return true; + } + default: return false; + } +} + /// Keep track of the running global counter for entry points and global parameters visited. /// /// Because of explicit `register` and `[[vk::binding(...)]]` support, parameter binding @@ -3022,16 +3037,22 @@ RefPtr<ProgramLayout> generateParameterBindings( // want to do so through a different feature. // bool needDefaultConstantBuffer = false; - for( auto& parameterInfo : sharedContext.parameters ) - { - SLANG_RELEASE_ASSERT(parameterInfo->varLayouts.getCount() != 0); - auto firstVarLayout = parameterInfo->varLayouts.getFirst(); - // Does the field have any uniform data? - if( firstVarLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform) ) + // On a CPU target, it's okay to have global scope parameters that use Uniform resources (because on CPU + // all resources are 'Uniform') + if (!_isCPUTarget(targetReq->target)) + { + for( auto& parameterInfo : sharedContext.parameters ) { - needDefaultConstantBuffer = true; - diagnoseGlobalUniform(&sharedContext, firstVarLayout->varDecl); + SLANG_RELEASE_ASSERT(parameterInfo->varLayouts.getCount() != 0); + auto firstVarLayout = parameterInfo->varLayouts.getFirst(); + + // Does the field have any uniform data? + if( firstVarLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform) ) + { + needDefaultConstantBuffer = true; + diagnoseGlobalUniform(&sharedContext, firstVarLayout->varDecl); + } } } diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index f76b29a51..3b14b74c2 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -346,6 +346,38 @@ struct HLSLConstantBufferLayoutRulesImpl : DefaultLayoutRulesImpl } }; +struct CPULayoutRulesImpl : DefaultLayoutRulesImpl +{ + typedef DefaultLayoutRulesImpl Super; + + SimpleLayoutInfo GetScalarLayout(BaseType baseType) override + { + switch (baseType) + { + case BaseType::Bool: + { + // TODO(JS): Much like ptr this is a problem - in knowing how to return this value. In the past it's been a word + // on some compilers for example. + // On checking though current compilers (clang, g++, visual studio) it is a single byte + return SimpleLayoutInfo( LayoutResourceKind::Uniform, 1, 1 ); + } + + default: return Super::GetScalarLayout(baseType); + } + } + + UniformLayoutInfo BeginStructLayout() override + { + return Super::BeginStructLayout(); + } + + void EndStructLayout(UniformLayoutInfo* ioStructInfo) override + { + // Conform to C/C++ size is adjusted to the largest alignment + ioStructInfo->size = RoundToAlignment(ioStructInfo->size, ioStructInfo->alignment); + } +}; + struct HLSLStructuredBufferLayoutRulesImpl : DefaultLayoutRulesImpl { // HLSL structured buffers drop the restrictions added for constant buffers, @@ -558,6 +590,8 @@ struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl LayoutRulesImpl* getHitAttributesParameterRules() override; LayoutRulesImpl* getShaderRecordConstantBufferRules() override; + + LayoutRulesImpl* getStructuredBufferRules() override; }; struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl @@ -576,11 +610,86 @@ struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl LayoutRulesImpl* getHitAttributesParameterRules() override; LayoutRulesImpl* getShaderRecordConstantBufferRules() override; + + LayoutRulesImpl* getStructuredBufferRules() override; +}; + +struct CPULayoutRulesFamilyImpl : LayoutRulesFamilyImpl +{ + virtual LayoutRulesImpl* getConstantBufferRules() override; + virtual LayoutRulesImpl* getPushConstantBufferRules() override; + virtual LayoutRulesImpl* getTextureBufferRules() override; + virtual LayoutRulesImpl* getVaryingInputRules() override; + virtual LayoutRulesImpl* getVaryingOutputRules() override; + virtual LayoutRulesImpl* getSpecializationConstantRules() override; + virtual LayoutRulesImpl* getShaderStorageBufferRules() override; + virtual LayoutRulesImpl* getParameterBlockRules() override; + + LayoutRulesImpl* getRayPayloadParameterRules() override; + LayoutRulesImpl* getCallablePayloadParameterRules() override; + LayoutRulesImpl* getHitAttributesParameterRules() override; + + LayoutRulesImpl* getShaderRecordConstantBufferRules() override; + LayoutRulesImpl* getStructuredBufferRules() override; }; GLSLLayoutRulesFamilyImpl kGLSLLayoutRulesFamilyImpl; HLSLLayoutRulesFamilyImpl kHLSLLayoutRulesFamilyImpl; +CPULayoutRulesFamilyImpl kCPULayoutRulesFamilyImpl; +// CPU case + +struct CPUObjectLayoutRulesImpl : ObjectLayoutRulesImpl +{ + virtual SimpleLayoutInfo GetObjectLayout(ShaderParameterKind kind) override + { + switch (kind) + { + case ShaderParameterKind::ConstantBuffer: + // It's a pointer to the actual uniform data + return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), sizeof(void*)); + + case ShaderParameterKind::MutableTexture: + case ShaderParameterKind::TextureUniformBuffer: + case ShaderParameterKind::Texture: + // It's a pointer to a texture interface + return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), sizeof(void*)); + + case ShaderParameterKind::StructuredBuffer: + case ShaderParameterKind::MutableStructuredBuffer: + // It's a ptr and a size of the amount of elements + return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*) * 2, sizeof(void*)); + + case ShaderParameterKind::RawBuffer: + case ShaderParameterKind::Buffer: + case ShaderParameterKind::MutableRawBuffer: + case ShaderParameterKind::MutableBuffer: + // It's a pointer and a size in bytes + return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*) * 2, sizeof(void*)); + + case ShaderParameterKind::SamplerState: + // It's a pointer + return SimpleLayoutInfo(LayoutResourceKind::Uniform, sizeof(void*), sizeof(void*)); + + case ShaderParameterKind::TextureSampler: + case ShaderParameterKind::MutableTextureSampler: + case ShaderParameterKind::InputRenderTarget: + // TODO: how to handle these? + default: + SLANG_UNEXPECTED("unhandled shader parameter kind"); + UNREACHABLE_RETURN(SimpleLayoutInfo()); + } + } +}; + + + +static CPUObjectLayoutRulesImpl kCPUObjectLayoutRulesImpl; +static CPULayoutRulesImpl kCPULayoutRulesImpl; + +LayoutRulesImpl kCPULayoutRulesImpl_ = { + &kCPULayoutRulesFamilyImpl, &kCPULayoutRulesImpl, &kCPUObjectLayoutRulesImpl, +}; // GLSL cases @@ -624,6 +733,11 @@ LayoutRulesImpl kGLSLHitAttributesParameterLayoutRulesImpl_ = { &kGLSLLayoutRulesFamilyImpl, &kGLSLHitAttributesParameterLayoutRulesImpl, &kGLSLObjectLayoutRulesImpl, }; +LayoutRulesImpl kGLSLStructuredBufferLayoutRulesImpl_ = { + &kGLSLLayoutRulesFamilyImpl, &kStd430LayoutRulesImpl, &kGLSLObjectLayoutRulesImpl, +}; + + // HLSL cases LayoutRulesImpl kHLSLConstantBufferLayoutRulesImpl_ = { @@ -654,7 +768,7 @@ LayoutRulesImpl kHLSLHitAttributesParameterLayoutRulesImpl_ = { &kHLSLLayoutRulesFamilyImpl, &kHLSLHitAttributesParameterLayoutRulesImpl, &kHLSLObjectLayoutRulesImpl, }; -// +// GLSL Family LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules() { @@ -717,7 +831,12 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getHitAttributesParameterRules() return &kGLSLHitAttributesParameterLayoutRulesImpl_; } -// +LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getStructuredBufferRules() +{ + return &kGLSLStructuredBufferLayoutRulesImpl_; +} + +// HLSL Family LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getConstantBufferRules() { @@ -741,6 +860,11 @@ LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getShaderRecordConstantBufferRules() return &kHLSLConstantBufferLayoutRulesImpl_; } +LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getStructuredBufferRules() +{ + return &kHLSLStructuredBufferLayoutRulesImpl_; +} + LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getTextureBufferRules() { return nullptr; @@ -781,21 +905,65 @@ LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getHitAttributesParameterRules() return &kHLSLHitAttributesParameterLayoutRulesImpl_; } +// CPU Family +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getConstantBufferRules() +{ + return &kCPULayoutRulesImpl_; +} -// +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getPushConstantBufferRules() +{ + return &kCPULayoutRulesImpl_; +} -LayoutRulesImpl* GetLayoutRulesImpl(LayoutRule rule) +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getTextureBufferRules() { - switch (rule) - { - case LayoutRule::Std140: return &kStd140LayoutRulesImpl_; - case LayoutRule::Std430: return &kStd430LayoutRulesImpl_; - case LayoutRule::HLSLConstantBuffer: return &kHLSLConstantBufferLayoutRulesImpl_; - case LayoutRule::HLSLStructuredBuffer: return &kHLSLStructuredBufferLayoutRulesImpl_; - default: - return nullptr; - } + return nullptr; +} + +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getVaryingInputRules() +{ + return nullptr; +} +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getVaryingOutputRules() +{ + return nullptr; +} +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getSpecializationConstantRules() +{ + return nullptr; +} +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getShaderStorageBufferRules() +{ + return nullptr; +} +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getParameterBlockRules() +{ + // Not clear - just use similar to CPU + return &kCPULayoutRulesImpl_; +} +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getRayPayloadParameterRules() +{ + return nullptr; +} +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getCallablePayloadParameterRules() +{ + return nullptr; +} +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getHitAttributesParameterRules() +{ + return nullptr; +} +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getShaderRecordConstantBufferRules() +{ + // Just following HLSLs lead for the moment + return &kCPULayoutRulesImpl_; +} + +LayoutRulesImpl* CPULayoutRulesFamilyImpl::getStructuredBufferRules() +{ + return &kCPULayoutRulesImpl_; } LayoutRulesFamilyImpl* getDefaultLayoutRulesFamilyForTarget(TargetRequest* targetReq) @@ -820,13 +988,15 @@ LayoutRulesFamilyImpl* getDefaultLayoutRulesFamilyForTarget(TargetRequest* targe case CodeGenTarget::CPPSource: case CodeGenTarget::CSource: { + // For now lets use some fairly simple CPU binding rules + // We just need to decide here what style of layout is appropriate, in terms of memory // and binding. That in terms of the actual binding that will be injected into functions // in the form of a BindContext. For now we'll go with HLSL layout - // that we may want to rethink that with the use of arrays and binding VK style binding might be // more appropriate in some ways. - return &kHLSLLayoutRulesFamilyImpl; + return &kCPULayoutRulesFamilyImpl; } default: @@ -1253,7 +1423,10 @@ static bool _usesOrdinaryData(RefPtr<TypeLayout> typeLayout) /// to the resource usage of a container like a `ConstantBuffer<X>` or /// `ParameterBlock<X>`. /// + /// TODO: letUnformBleedThrough is (hopefully temporary) a hack that was added to enable CPU targets to + /// produce workable layout. CPU targets have all bindings/variables laid out as uniforms static void _addUnmaskedResourceUsage( + bool letUniformBleedThrough, TypeLayout* dstTypeLayout, TypeLayout* srcTypeLayout, bool haveFullRegisterSpaceOrSet) @@ -1264,6 +1437,10 @@ static void _addUnmaskedResourceUsage( { case LayoutResourceKind::Uniform: // Ordinary/uniform resource usage will always be masked. + if (letUniformBleedThrough) + { + dstTypeLayout->addResourceUsage(resInfo); + } break; case LayoutResourceKind::RegisterSpace: @@ -1453,6 +1630,13 @@ static RefPtr<TypeLayout> _createParameterGroupTypeLayout( for( auto elementTypeResInfo : rawElementTypeLayout->resourceInfos ) { auto kind = elementTypeResInfo.kind; + + // TODO: Added to make layout work correctly for CPU target + if(kind == LayoutResourceKind::Uniform) + { + continue; + } + auto elementVarResInfo = elementVarLayout->findOrAddResourceInfo(kind); // If the container part of things is using the same resource kind @@ -1518,7 +1702,7 @@ static RefPtr<TypeLayout> _createParameterGroupTypeLayout( // buffer. Its resource usage will only bleed through if we // didn't allocate a full `space` or `set`. // - _addUnmaskedResourceUsage(typeLayout, containerTypeLayout, wantSpaceOrSet); + _addUnmaskedResourceUsage(true, typeLayout, containerTypeLayout, wantSpaceOrSet); // next we turn to the element type, where the cases are slightly // more involved (technically we could use this same logic for @@ -1526,7 +1710,7 @@ static RefPtr<TypeLayout> _createParameterGroupTypeLayout( // just special-case the container). // - _addUnmaskedResourceUsage(typeLayout, rawElementTypeLayout, wantSpaceOrSet); + _addUnmaskedResourceUsage(false, typeLayout, rawElementTypeLayout, wantSpaceOrSet); // At this point we have handled all the complexities that // arise for a parameter group that doesn't include interface-type @@ -1684,8 +1868,8 @@ static RefPtr<TypeLayout> _createParameterGroupTypeLayout( // up the hierarchy. // RefPtr<TypeLayout> unmaskedPendingDataTypeLayout = new TypeLayout(); - _addUnmaskedResourceUsage(unmaskedPendingDataTypeLayout, pendingContainerTypeLayout, wantSpaceOrSet); - _addUnmaskedResourceUsage(unmaskedPendingDataTypeLayout, pendingElementTypeLayout, wantSpaceOrSet); + _addUnmaskedResourceUsage(true, unmaskedPendingDataTypeLayout, pendingContainerTypeLayout, wantSpaceOrSet); + _addUnmaskedResourceUsage(false, unmaskedPendingDataTypeLayout, pendingElementTypeLayout, wantSpaceOrSet); // TODO: we should probably optimize for the case where there is no unmasked // usage that needs to be reported out, since it should be a common case. @@ -1836,9 +2020,7 @@ createStructuredBufferTypeLayout( typeLayout->elementTypeLayout = elementTypeLayout; typeLayout->uniformAlignment = info.alignment; - SLANG_RELEASE_ASSERT(!typeLayout->FindResourceInfo(LayoutResourceKind::Uniform)); - SLANG_RELEASE_ASSERT(typeLayout->uniformAlignment == 1); - + if( info.size != 0 ) { typeLayout->addResourceUsage(info.kind, info.size); @@ -1859,10 +2041,8 @@ createStructuredBufferTypeLayout( RefPtr<Type> structuredBufferType, RefPtr<Type> elementType) { - // TODO(tfoley): we should be looking up the appropriate rules - // via the `LayoutRulesFamily` in use here... - auto structuredBufferLayoutRules = GetLayoutRulesImpl( - LayoutRule::HLSLStructuredBuffer); + // look up the appropriate rules via the `LayoutRulesFamily` + auto structuredBufferLayoutRules = context.getRulesFamily()->getStructuredBufferRules(); // Create and save type layout for the buffer contents. auto elementTypeLayout = createTypeLayout( diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index b7b3c3207..5e86be113 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -17,14 +17,6 @@ class Type; // -enum class LayoutRule -{ - Std140, - Std430, - HLSLConstantBuffer, - HLSLStructuredBuffer, -}; - #if 0 enum class LayoutRulesFamily { @@ -392,7 +384,7 @@ public: typedef unsigned int VarLayoutFlags; enum VarLayoutFlag : VarLayoutFlags { - HasSemantic = 1 << 1 + HasSemantic = 1 << 0 }; // A reified layout for a particular variable, field, etc. @@ -924,6 +916,8 @@ struct LayoutRulesFamilyImpl virtual LayoutRulesImpl* getHitAttributesParameterRules()= 0; virtual LayoutRulesImpl* getShaderRecordConstantBufferRules() = 0; + + virtual LayoutRulesImpl* getStructuredBufferRules() = 0; }; struct TypeLayoutContext |
