summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-08-08 17:23:03 -0400
committerGitHub <noreply@github.com>2019-08-08 17:23:03 -0400
commit41247c3942210df33b9e3dd733eafb23573a4f2f (patch)
treea49a88e680078bd379fe183cf826d49d7f935737 /source
parentc1cc93dd962a6db6c839341f11d2654cf0e62e37 (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.cpp10
-rw-r--r--source/slang/slang-emit-c-like.h23
-rw-r--r--source/slang/slang-emit-cpp.cpp1014
-rw-r--r--source/slang/slang-emit-cpp.h38
-rw-r--r--source/slang/slang-emit.cpp102
-rw-r--r--source/slang/slang-ir-entry-point-uniforms.cpp33
-rw-r--r--source/slang/slang-ir-entry-point-uniforms.h5
-rw-r--r--source/slang/slang-parameter-binding.cpp37
-rw-r--r--source/slang/slang-type-layout.cpp230
-rw-r--r--source/slang/slang-type-layout.h12
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