summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2017-11-06 10:37:27 -0800
committerGitHub <noreply@github.com>2017-11-06 10:37:27 -0800
commit9919c823938ae929b16efac9d507f6d5eb122bf4 (patch)
tree8fc65791d416cffa8267180177d3f3d179679460
parent296e89ca4f3d6d99126bf2ee59666bc946add431 (diff)
Parameter blocks (#245)
* Rename existing ParameterBlock to ParameterGroup We are planning to add a new `ParameterBlock<T>` type, which maps to the notion of a "parameter block" as used in the Spire research work. Unfortunately, the compiler codebase already uses the term `ParameterBlock` as catch-all to encompass all of HLSL `cbuffer`/`tbuffer` and GLSL `uniform`/`buffer`/`in`/`out` blocks (all of which are lexical `{}`-enclosed blocks that define parameters...). This change instead renames all of the existing concepts over to `ParameterGroup`, which isn't an ideal name, but at least doesn't directly overlap the new terminology or any existing terminology. The new `ParameterBlockType` case will probably be a subclass of `ParameterGroupType`, since it is a logical extension of the underlying concept. * Add Shader Model 5.1 profiles The HLSL `register(..., space0)` syntax is only allowed on "SM5.1" and later profiles (which is supported by the newer version of `d3dcompiler_47.dll` that comes with the Win10 SDK, but not the older version of `d3dcompiler_47.dll` - good luck figuring out which you have!). This change adds those profiles to our master list of profiles, and nothing else. * First pass at support for `ParameterBlock<T>` - Add the type declaration in stdlib - Add a special case of `ParameterGroupType` for parameter blocks - Handle parameter blocks in type layout (currently handling them identically to constant buffers for now, which isn't going to be right in the long term) - Add an IR pass that basically replaces `ParameterBlock<T>` with `T` - Eventually this should replace it with either `T` or `ConstantBuffer<T>`, depending on whether the layout that was computed required a constant buffer to hold any "free" uniforms - Add first stab at an IR pass to "scalarize" global variables using aggregate types with resources inside. - This currently only applies to global variables, so it won't handle things passed through functions, or used as local variables - It also only supports cases where the references to the original variable are always references to its fields, and not the whole value itself - Add a single test case that technically passes with this level of support, but probably isn't very representative of what we need from the feature * Fold parameter-block desugaring into a more complete "type legalization" pass The basic problem that was arising is that once you desugar `ParameterBlock<T>` into `T`, you then need todeal with splitting `T` into its constituent fields if it contains any resource types. Handling those transformations by following the usual use-def chains wasn't really helping, because you might need systematic rewriting that can really only be handled bottom-up. This change adds a new pass that is intended to perform multiple kinds of type "legalization" at once: - It will turn `ParameterBlock<T>` into `T` - It may at some point also convert `ConstantBuffer<T>` into `T` as well - It will turn an value of an aggregate type that contains resources into N different values (one per field) - As a result of this, it will also deal with AOS-to-SOA conversion of these types Legalization is applied to *every* function/instruction/value, so that it can make large-scale changes that would be tough to manage with a work list. This pass needs to be run *after* generics have been fully specialized, so that we know we are always dealing with fully concrete types, so that their legalization for a given target is completely known. This is still work in progress; there's more to be done to get this working with all our test cases, and finish the remaining `ParameterBlock<T>` work. * Improve binding/layout information when using parameter blocks - When doing type layout for a parameter block, don't include the resources consumed by the element type in the resource usage for the parameter block - Note that this is pretty much identical to how a `ConstantBuffer<T>` does not report any `LayoutResourceKind::Uniform` usage, except that `ParameterBlock<T>` is *also* going to hide underlying texture/sampler reigster usage - The one exception here is that any nested items that use up entire `space`s or `set`s those need to be exposed in the resource usage of the parent (I don't have a test for this) - When type legalization needs to scalarize things, it must propagate layout information down to the new leaf variables. In general, the register/index for a new leaf parameter should be the sum of the offsets for all of the parent variables along the "chain" from the original variable down to the leaf (we aren't dealing with arrays here just yet). - When type legalization decides to eliminate a pointer(-like) type (e.g., desugar `ParameterBlock<T>` over to `T`), actually deal with that in terms of the `LegalVal`s created, so that we can know to turn a `load` into a no-op when applied to a value that got indirection removed. - Hack up the "complex" parameter-block test so that it actually passes (the big hack here is that the HLSL baseline is using names that are generated by the IR, and are unlikely to be stable as we add/remove transformations). - Note: I can't make these be compute tests right now, because regsiter spaces/sets are a feature of D3D12/Vulkan, and our test runner isn't using those APIs.
-rw-r--r--slang.h3
-rw-r--r--source/slang/bytecode.cpp2
-rw-r--r--source/slang/compiler.h4
-rw-r--r--source/slang/core.meta.slang2
-rw-r--r--source/slang/core.meta.slang.h2
-rw-r--r--source/slang/emit.cpp130
-rw-r--r--source/slang/glsl.meta.slang4
-rw-r--r--source/slang/glsl.meta.slang.h4
-rw-r--r--source/slang/ir-legalize-types.cpp1356
-rw-r--r--source/slang/ir.cpp27
-rw-r--r--source/slang/ir.h7
-rw-r--r--source/slang/lower.cpp6
-rw-r--r--source/slang/modifier-defs.h6
-rw-r--r--source/slang/parameter-binding.cpp56
-rw-r--r--source/slang/parser.cpp24
-rw-r--r--source/slang/profile-defs.h7
-rw-r--r--source/slang/reflection.cpp12
-rw-r--r--source/slang/slang.vcxproj1
-rw-r--r--source/slang/slang.vcxproj.filters1
-rw-r--r--source/slang/syntax.cpp16
-rw-r--r--source/slang/type-defs.h19
-rw-r--r--source/slang/type-layout.cpp180
-rw-r--r--source/slang/type-layout.h42
-rw-r--r--tests/bindings/multiple-parameter-blocks.slang48
-rw-r--r--tests/bindings/parameter-blocks.slang39
-rw-r--r--tests/bugs/gh-171.slang6
-rw-r--r--tests/bugs/gh-172.slang10
-rw-r--r--tests/rewriter/resources-in-structs.glsl8
-rw-r--r--tests/rewriter/type-splitting.hlsl6
29 files changed, 1828 insertions, 200 deletions
diff --git a/slang.h b/slang.h
index 5c84ce6f3..6ccf04d83 100644
--- a/slang.h
+++ b/slang.h
@@ -564,6 +564,7 @@ extern "C"
SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT,
SLANG_PARAMETER_CATEGORY_SPECIALIZATION_CONSTANT,
SLANG_PARAMETER_CATEGORY_PUSH_CONSTANT_BUFFER,
+ SLANG_PAREMTER_CATEGORY_PARAMETER_BLOCK,
//
SLANG_PARAMETER_CATEGORY_COUNT,
@@ -817,6 +818,7 @@ namespace slang
DescriptorTableSlot = SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT,
SpecializationConstant = SLANG_PARAMETER_CATEGORY_SPECIALIZATION_CONSTANT,
PushConstantBuffer = SLANG_PARAMETER_CATEGORY_PUSH_CONSTANT_BUFFER,
+ ParameterBlock = SLANG_PAREMTER_CATEGORY_PARAMETER_BLOCK,
};
struct TypeLayoutReflection
@@ -1084,6 +1086,7 @@ namespace slang
#include "source/slang/dxc-support.cpp"
#include "source/slang/emit.cpp"
#include "source/slang/ir.cpp"
+#include "source/slang/ir-legalize-types.cpp"
#include "source/slang/lexer.cpp"
#include "source/slang/mangle.cpp"
#include "source/slang/name.cpp"
diff --git a/source/slang/bytecode.cpp b/source/slang/bytecode.cpp
index 943a9a5a3..3b92b78e1 100644
--- a/source/slang/bytecode.cpp
+++ b/source/slang/bytecode.cpp
@@ -653,7 +653,7 @@ BytecodeGenerationPtr<char> tryGenerateNameForSymbol(
if (auto highLevelDeclDecoration = inst->findDecoration<IRHighLevelDeclDecoration>())
{
auto decl = highLevelDeclDecoration->decl;
- if (auto reflectionNameMod = decl->FindModifier<ParameterBlockReflectionName>())
+ if (auto reflectionNameMod = decl->FindModifier<ParameterGroupReflectionName>())
{
return allocateString(context, reflectionNameMod->name);
}
diff --git a/source/slang/compiler.h b/source/slang/compiler.h
index 7b539ce69..7a045d23c 100644
--- a/source/slang/compiler.h
+++ b/source/slang/compiler.h
@@ -433,6 +433,10 @@ namespace Slang
// Construct pointer types on-demand
RefPtr<PtrType> getPtrType(RefPtr<Type> valueType);
+ RefPtr<ArrayExpressionType> getArrayType(
+ Type* elementType,
+ IntVal* elementCount);
+
RefPtr<GroupSharedType> getGroupSharedType(RefPtr<Type> valueType);
SyntaxClass<RefObject> findSyntaxClass(Name* name);
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index b792a74a3..8accc83a4 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -170,6 +170,8 @@ sb << "__generic<T>\n";
sb << "__intrinsic_type(" << kIROp_TextureBufferType << ")\n";
sb << "__magic_type(TextureBuffer) struct TextureBuffer {};\n";
+sb << "__generic<T>\n";
+sb << "__magic_type(ParameterBlockType) struct ParameterBlock {};\n";
static const char* kComponentNames[]{ "x", "y", "z", "w" };
static const char* kVectorNames[]{ "", "x", "xy", "xyz", "xyzw" };
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index e47e2a90b..6b60d2896 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -173,6 +173,8 @@ sb << "__generic<T>\n";
sb << "__intrinsic_type(" << kIROp_TextureBufferType << ")\n";
sb << "__magic_type(TextureBuffer) struct TextureBuffer {};\n";
+sb << "__generic<T>\n";
+sb << "__magic_type(ParameterBlockType) struct ParameterBlock {};\n";
static const char* kComponentNames[]{ "x", "y", "z", "w" };
static const char* kVectorNames[]{ "", "x", "xy", "xyz", "xyzw" };
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 056454845..d95946204 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -3316,7 +3316,7 @@ struct EmitVisitor
{
// Don't emit a declaration that was only generated implicitly, for
// the purposes of semantic checking.
- if(decl->HasModifier<ImplicitParameterBlockElementTypeModifier>())
+ if(decl->HasModifier<ImplicitParameterGroupElementTypeModifier>())
return;
Emit("struct ");
@@ -3498,7 +3498,7 @@ struct EmitVisitor
return varLayout;
}
- void emitHLSLParameterBlockFieldLayoutSemantics(
+ void emitHLSLParameterGroupFieldLayoutSemantics(
RefPtr<VarLayout> layout,
RefPtr<VarLayout> fieldLayout)
{
@@ -3528,13 +3528,13 @@ struct EmitVisitor
}
}
- void emitHLSLParameterBlockDecl(
+ void emitHLSLParameterGroupDecl(
RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<ParameterGroupType> parameterGroupType,
RefPtr<VarLayout> layout)
{
// The data type that describes where stuff in the constant buffer should go
- RefPtr<Type> dataType = parameterBlockType->elementType;
+ RefPtr<Type> dataType = parameterGroupType->elementType;
// We expect/require the data type to be a user-defined `struct` type
auto declRefType = dataType->As<DeclRefType>();
@@ -3545,22 +3545,22 @@ struct EmitVisitor
SLANG_RELEASE_ASSERT(layout);
// We expect the layout to be for a structured type...
- RefPtr<ParameterBlockTypeLayout> bufferLayout = layout->typeLayout.As<ParameterBlockTypeLayout>();
+ RefPtr<ParameterGroupTypeLayout> bufferLayout = layout->typeLayout.As<ParameterGroupTypeLayout>();
SLANG_RELEASE_ASSERT(bufferLayout);
RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementTypeLayout.As<StructTypeLayout>();
SLANG_RELEASE_ASSERT(structTypeLayout);
- if( auto constantBufferType = parameterBlockType->As<ConstantBufferType>() )
+ if( auto constantBufferType = parameterGroupType->As<ConstantBufferType>() )
{
Emit("cbuffer ");
}
- else if( auto textureBufferType = parameterBlockType->As<TextureBufferType>() )
+ else if( auto textureBufferType = parameterGroupType->As<TextureBufferType>() )
{
Emit("tbuffer ");
}
- if( auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>() )
+ if( auto reflectionNameModifier = varDecl->FindModifier<ParameterGroupReflectionName>() )
{
Emit(" ");
emitName(reflectionNameModifier->nameAndLoc);
@@ -3587,7 +3587,7 @@ struct EmitVisitor
SLANG_RELEASE_ASSERT(fieldLayout->varDecl.GetName() == field.GetName());
// Emit explicit layout annotations for every field
- emitHLSLParameterBlockFieldLayoutSemantics(layout, fieldLayout);
+ emitHLSLParameterGroupFieldLayoutSemantics(layout, fieldLayout);
emitVarDeclInit(field);
@@ -3696,13 +3696,13 @@ struct EmitVisitor
}
}
- void emitGLSLParameterBlockDecl(
+ void emitGLSLParameterGroupDecl(
RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<ParameterGroupType> parameterGroupType,
RefPtr<VarLayout> layout)
{
// The data type that describes where stuff in the constant buffer should go
- RefPtr<Type> dataType = parameterBlockType->elementType;
+ RefPtr<Type> dataType = parameterGroupType->elementType;
// We expect/require the data type to be a user-defined `struct` type
auto declRefType = dataType->As<DeclRefType>();
@@ -3714,7 +3714,7 @@ struct EmitVisitor
{
auto typeLayout = layout->typeLayout;
- if (auto bufferLayout = typeLayout.As<ParameterBlockTypeLayout>())
+ if (auto bufferLayout = typeLayout.As<ParameterGroupTypeLayout>())
{
typeLayout = bufferLayout->elementTypeLayout;
}
@@ -3729,19 +3729,19 @@ struct EmitVisitor
EmitModifiers(varDecl);
// Emit an apprpriate declaration keyword based on the kind of block
- if (parameterBlockType->As<ConstantBufferType>())
+ if (parameterGroupType->As<ConstantBufferType>())
{
Emit("uniform");
}
- else if (parameterBlockType->As<GLSLInputParameterBlockType>())
+ else if (parameterGroupType->As<GLSLInputParameterGroupType>())
{
Emit("in");
}
- else if (parameterBlockType->As<GLSLOutputParameterBlockType>())
+ else if (parameterGroupType->As<GLSLOutputParameterGroupType>())
{
Emit("out");
}
- else if (parameterBlockType->As<GLSLShaderStorageBufferType>())
+ else if (parameterGroupType->As<GLSLShaderStorageBufferType>())
{
Emit("buffer");
}
@@ -3751,7 +3751,7 @@ struct EmitVisitor
Emit("uniform");
}
- if( auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>() )
+ if( auto reflectionNameModifier = varDecl->FindModifier<ParameterGroupReflectionName>() )
{
Emit(" ");
emitName(reflectionNameModifier->nameAndLoc);
@@ -3789,19 +3789,19 @@ struct EmitVisitor
Emit(";\n");
}
- void emitParameterBlockDecl(
+ void emitParameterGroupDecl(
RefPtr<VarDeclBase> varDecl,
- RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<ParameterGroupType> parameterGroupType,
RefPtr<VarLayout> layout)
{
switch(context->shared->target)
{
case CodeGenTarget::HLSL:
- emitHLSLParameterBlockDecl(varDecl, parameterBlockType, layout);
+ emitHLSLParameterGroupDecl(varDecl, parameterGroupType, layout);
break;
case CodeGenTarget::GLSL:
- emitGLSLParameterBlockDecl(varDecl, parameterBlockType, layout);
+ emitGLSLParameterGroupDecl(varDecl, parameterGroupType, layout);
break;
default:
@@ -3843,9 +3843,9 @@ struct EmitVisitor
//
// TODO(tfoley): there might be a better way to detect this, e.g.,
// with an attribute that gets attached to the variable declaration.
- if (auto parameterBlockType = decl->type->As<ParameterBlockType>())
+ if (auto parameterGroupType = decl->type->As<ParameterGroupType>())
{
- emitParameterBlockDecl(decl, parameterBlockType, layout);
+ emitParameterGroupDecl(decl, parameterGroupType, layout);
return;
}
@@ -4145,7 +4145,7 @@ emitDeclImpl(decl, nullptr);
if(auto decoration = inst->findDecoration<IRHighLevelDeclDecoration>())
{
auto decl = decoration->decl;
- if (auto reflectionNameMod = decl->FindModifier<ParameterBlockReflectionName>())
+ if (auto reflectionNameMod = decl->FindModifier<ParameterGroupReflectionName>())
{
return getText(reflectionNameMod->nameAndLoc.name);
}
@@ -4492,7 +4492,7 @@ emitDeclImpl(decl, nullptr);
// because they aren't allowed as types for temporary
// variables.
auto type = inst->getType();
- if(type->As<UniformParameterBlockType>())
+ if(type->As<UniformParameterGroupType>())
{
// TODO: we need to be careful here, because
// HLSL shader model 6 allows these as explicit
@@ -4518,7 +4518,7 @@ emitDeclImpl(decl, nullptr);
{
auto type = inst->getType();
- if(type->As<UniformParameterBlockType>())
+ if(type->As<UniformParameterGroupType>())
{
// TODO: we need to be careful here, because
// HLSL shader model 6 allows these as explicit
@@ -5812,10 +5812,10 @@ emitDeclImpl(decl, nullptr);
}
}
- void emitHLSLParameterBlock(
+ void emitHLSLParameterGroup(
EmitContext* ctx,
IRGlobalVar* varDecl,
- UniformParameterBlockType* type)
+ UniformParameterGroupType* type)
{
emit("cbuffer ");
emit(getIRName(varDecl));
@@ -5832,9 +5832,9 @@ emitDeclImpl(decl, nullptr);
auto elementType = type->getElementType();
auto typeLayout = layout->typeLayout;
- if( auto parameterBlockTypeLayout = typeLayout.As<ParameterBlockTypeLayout>() )
+ if( auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>() )
{
- typeLayout = parameterBlockTypeLayout->elementTypeLayout;
+ typeLayout = parameterGroupTypeLayout->elementTypeLayout;
}
if(auto declRefType = elementType->As<DeclRefType>())
@@ -5864,7 +5864,7 @@ emitDeclImpl(decl, nullptr);
auto fieldType = GetType(ff);
emitIRType(ctx, fieldType, getIRName(ff));
- emitHLSLParameterBlockFieldLayoutSemantics(layout, fieldLayout);
+ emitHLSLParameterGroupFieldLayoutSemantics(layout, fieldLayout);
emit(";\n");
}
@@ -5878,10 +5878,10 @@ emitDeclImpl(decl, nullptr);
emit("}\n");
}
- void emitGLSLParameterBlock(
+ void emitGLSLParameterGroup(
EmitContext* ctx,
IRGlobalVar* varDecl,
- UniformParameterBlockType* type)
+ UniformParameterGroupType* type)
{
auto layout = getVarLayout(ctx, varDecl);
assert(layout);
@@ -5909,9 +5909,9 @@ emitDeclImpl(decl, nullptr);
auto elementType = type->getElementType();
auto typeLayout = layout->typeLayout;
- if( auto parameterBlockTypeLayout = typeLayout.As<ParameterBlockTypeLayout>() )
+ if( auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>() )
{
- typeLayout = parameterBlockTypeLayout->elementTypeLayout;
+ typeLayout = parameterGroupTypeLayout->elementTypeLayout;
}
if(auto declRefType = elementType->As<DeclRefType>())
@@ -5941,7 +5941,7 @@ emitDeclImpl(decl, nullptr);
auto fieldType = GetType(ff);
emitIRType(ctx, fieldType, getIRName(ff));
-// emitHLSLParameterBlockFieldLayoutSemantics(layout, fieldLayout);
+// emitHLSLParameterGroupFieldLayoutSemantics(layout, fieldLayout);
emit(";\n");
}
@@ -5960,19 +5960,19 @@ emitDeclImpl(decl, nullptr);
emit("};\n");
}
- void emitIRParameterBlock(
+ void emitIRParameterGroup(
EmitContext* ctx,
IRGlobalVar* varDecl,
- UniformParameterBlockType* type)
+ UniformParameterGroupType* type)
{
switch (ctx->shared->target)
{
case CodeGenTarget::HLSL:
- emitHLSLParameterBlock(ctx, varDecl, type);
+ emitHLSLParameterGroup(ctx, varDecl, type);
break;
case CodeGenTarget::GLSL:
- emitGLSLParameterBlock(ctx, varDecl, type);
+ emitGLSLParameterGroup(ctx, varDecl, type);
break;
}
}
@@ -5990,7 +5990,7 @@ emitDeclImpl(decl, nullptr);
{
case kIROp_ConstantBufferType:
case kIROp_TextureBufferType:
- emitIRParameterBlock(ctx, varDecl, (IRUniformBufferType*) varType);
+ emitIRParameterGroup(ctx, varDecl, (IRUniformBufferType*) varType);
return;
default:
@@ -6033,9 +6033,9 @@ emitDeclImpl(decl, nullptr);
auto varType = allocatedType->getValueType();
// auto addressSpace = allocatedType->getAddressSpace();
- if (auto paramBlockType = varType->As<UniformParameterBlockType>())
+ if (auto paramBlockType = varType->As<UniformParameterGroupType>())
{
- emitIRParameterBlock(
+ emitIRParameterGroup(
ctx,
varDecl,
paramBlockType);
@@ -6305,7 +6305,7 @@ StructTypeLayout* getGlobalStructLayout(
{
return gs.Ptr();
}
- else if( auto globalConstantBufferLayout = globalScopeLayout.As<ParameterBlockTypeLayout>() )
+ else if( auto globalConstantBufferLayout = globalScopeLayout.As<ParameterGroupTypeLayout>() )
{
// TODO: the `cbuffer` case really needs to be emitted very
// carefully, but that is beyond the scope of what a simple rewriter
@@ -6338,6 +6338,9 @@ StructTypeLayout* getGlobalStructLayout(
}
}
+void legalizeTypes(
+ IRModule* module);
+
String emitEntryPoint(
EntryPointRequest* entryPoint,
ProgramLayout* programLayout,
@@ -6401,20 +6404,37 @@ String emitEntryPoint(
// so that we "just" need to specialize it as needed for the
// specific target and entry point in use.
//
+ // The first pass is to extract the IR code of the entry point,
+ // and any other symbols it references. At the same time,
+ // we go ahead and select the target-specific version of
+ // any such functions if they are available. We also go
+ // ahead and apply the layout information (from `programLayout`)
+ // to the IR code (which previously had no layout).
+ //
+ // Note: it is important that we extract a *copy* of all the
+ // relevant IR, so that transformations we make for one
+ // entry point (or target) don't mess up the IR used for other
+ // entry points (targets).
+ //
auto lowered = specializeIRForEntryPoint(
entryPoint,
programLayout,
target);
- // debugging:
+ // If the user specified the flag that they want us to dump
+ // IR, then do it here, for the target-specific, but
+ // un-specialized IR.
if (translationUnit->compileRequest->shouldDumpIR)
{
dumpIR(lowered);
}
- // TODO: we should apply some guaranteed transformations here,
- // to eliminate constructs that aren't legal downstream (e.g. generics).
-
+ // Next, we need to ensure that the code we emit for
+ // the target doesn't contain any operations that would
+ // be illegal on the target platform. For example,
+ // none of our target supports generics, or interfaces,
+ // so we need to specialize those away.
+ //
specializeGenerics(lowered);
// Debugging code for IR transformations...
@@ -6424,10 +6444,12 @@ String emitEntryPoint(
fprintf(stderr, "###\n");
#endif
- //
- // TODO: Need to decide whether to do these before or after
- // target-specific legalization steps. Currently I've folded
- // legalization into the specialization above.
+ // After we've fully specialized all generics, and
+ // "devirtualized" all the calls through interfaces,
+ // we need to ensure that the code only uses types
+ // that are legal on the chosen target.
+ //
+ legalizeTypes(lowered);
// TODO: do we want to emit directly from IR, or translate the
// IR back into AST for emission?
diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang
index 4aa3f059c..a9f4fcb3c 100644
--- a/source/slang/glsl.meta.slang
+++ b/source/slang/glsl.meta.slang
@@ -150,8 +150,8 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
}
}
-sb << "__generic<T> __magic_type(GLSLInputParameterBlockType) struct __GLSLInputParameterBlock {};\n";
-sb << "__generic<T> __magic_type(GLSLOutputParameterBlockType) struct __GLSLOutputParameterBlock {};\n";
+sb << "__generic<T> __magic_type(GLSLInputParameterGroupType) struct __GLSLInputParameterGroup {};\n";
+sb << "__generic<T> __magic_type(GLSLOutputParameterGroupType) struct __GLSLOutputParameterGroup {};\n";
sb << "__generic<T> __magic_type(GLSLShaderStorageBufferType) struct __GLSLShaderStorageBuffer {};\n";
sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerState) << ") struct sampler {};";
diff --git a/source/slang/glsl.meta.slang.h b/source/slang/glsl.meta.slang.h
index a735b3beb..24c219c29 100644
--- a/source/slang/glsl.meta.slang.h
+++ b/source/slang/glsl.meta.slang.h
@@ -151,8 +151,8 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
}
}
-sb << "__generic<T> __magic_type(GLSLInputParameterBlockType) struct __GLSLInputParameterBlock {};\n";
-sb << "__generic<T> __magic_type(GLSLOutputParameterBlockType) struct __GLSLOutputParameterBlock {};\n";
+sb << "__generic<T> __magic_type(GLSLInputParameterGroupType) struct __GLSLInputParameterGroup {};\n";
+sb << "__generic<T> __magic_type(GLSLOutputParameterGroupType) struct __GLSLOutputParameterGroup {};\n";
sb << "__generic<T> __magic_type(GLSLShaderStorageBufferType) struct __GLSLShaderStorageBuffer {};\n";
sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerState) << ") struct sampler {};";
diff --git a/source/slang/ir-legalize-types.cpp b/source/slang/ir-legalize-types.cpp
new file mode 100644
index 000000000..677ccedd3
--- /dev/null
+++ b/source/slang/ir-legalize-types.cpp
@@ -0,0 +1,1356 @@
+// ir-legalize-types.cpp
+
+// This file implements a pass that takes IR
+// that has been fully specialized (no more
+// generics/interfaces needing to be specialized
+// away) and replaces any types that can't actually
+// be used as-is on the target.
+//
+// The particular case we are focused on is
+// aggregate types (e.g., `struct` types) that
+// contain resources (textures, samplers, etc.)
+// or that mix resources and ordinary "uniform"
+// data.
+
+#include "ir.h"
+#include "ir-insts.h"
+
+namespace Slang
+{
+
+struct LegalTypeImpl : RefObject
+{
+};
+struct ImplicitDerefType;
+struct TupleType;
+
+struct LegalType
+{
+ enum class Flavor
+ {
+ // Nothing: a NULL type
+ none,
+
+ // A simple type that can be represented directly as a `Type`
+ simple,
+
+ // Logically, we have a pointer-like type, but we are
+ // going to represnet it as the pointed-to type
+ implicitDeref,
+
+ tuple,
+ };
+
+ Flavor flavor = Flavor::none;
+ RefPtr<RefObject> obj;
+
+ static LegalType simple(Type* type)
+ {
+ LegalType result;
+ result.flavor = Flavor::simple;
+ result.obj = type;
+ return result;
+ }
+
+ RefPtr<Type> getSimple()
+ {
+ assert(flavor == Flavor::simple);
+ return obj.As<Type>();
+ }
+
+ static LegalType implicitDeref(
+ LegalType const& valueType);
+
+ RefPtr<ImplicitDerefType> getImplicitDeref()
+ {
+ assert(flavor == Flavor::implicitDeref);
+ return obj.As<ImplicitDerefType>();
+ }
+
+ static LegalType tuple(
+ RefPtr<TupleType> tupleType);
+
+ RefPtr<TupleType> getTuple()
+ {
+ assert(flavor == Flavor::tuple);
+ return obj.As<TupleType>();
+ }
+};
+
+struct ImplicitDerefType : LegalTypeImpl
+{
+ LegalType valueType;
+};
+
+LegalType LegalType::implicitDeref(
+ LegalType const& valueType)
+{
+ RefPtr<ImplicitDerefType> obj = new ImplicitDerefType();
+ obj->valueType = valueType;
+
+ LegalType result;
+ result.flavor = Flavor::implicitDeref;
+ result.obj = obj;
+ return result;
+}
+
+struct TupleType : LegalTypeImpl
+{
+ struct Element
+ {
+ DeclRef<VarDeclBase> fieldDeclRef;
+ LegalType type;
+ };
+
+ List<Element> elements;
+};
+
+LegalType LegalType::tuple(
+ RefPtr<TupleType> tupleType)
+{
+ LegalType result;
+ result.flavor = Flavor::tuple;
+ result.obj = tupleType;
+ return result;
+}
+
+struct LegalValImpl : RefObject
+{
+};
+struct TupleVal;
+
+struct LegalVal
+{
+ enum class Flavor
+ {
+ none,
+ simple,
+ implicitDeref,
+ tuple,
+ };
+
+ Flavor flavor;
+ RefPtr<RefObject> obj;
+ IRValue* irValue;
+
+ static LegalVal simple(IRValue* irValue)
+ {
+ LegalVal result;
+ result.flavor = Flavor::simple;
+ result.irValue = irValue;
+ return result;
+ }
+
+ IRValue* getSimple()
+ {
+ assert(flavor == Flavor::simple);
+ return irValue;
+ }
+
+ static LegalVal tuple(RefPtr<TupleVal> tupleVal);
+
+ RefPtr<TupleVal> getTuple()
+ {
+ assert(flavor == Flavor::tuple);
+ return obj.As<TupleVal>();
+ }
+
+ static LegalVal implicitDeref(LegalVal const& val);
+ LegalVal getImplicitDeref();
+};
+
+struct TupleVal : LegalValImpl
+{
+ struct Element
+ {
+ DeclRef<VarDeclBase> fieldDeclRef;
+ LegalVal val;
+ };
+
+ List<Element> elements;
+};
+
+LegalVal LegalVal::tuple(RefPtr<TupleVal> tupleVal)
+{
+ LegalVal result;
+ result.flavor = LegalVal::Flavor::tuple;
+ result.obj = tupleVal;
+ return result;
+}
+
+struct ImplicitDerefVal : LegalValImpl
+{
+ LegalVal val;
+};
+
+LegalVal LegalVal::implicitDeref(LegalVal const& val)
+{
+ RefPtr<ImplicitDerefVal> implicitDerefVal = new ImplicitDerefVal();
+ implicitDerefVal->val = val;
+
+ LegalVal result;
+ result.flavor = LegalVal::Flavor::implicitDeref;
+ result.obj = implicitDerefVal;
+ return result;
+}
+
+LegalVal LegalVal::getImplicitDeref()
+{
+ assert(flavor == Flavor::implicitDeref);
+ return obj.As<ImplicitDerefVal>()->val;
+}
+
+
+struct TypeLegalizationContext
+{
+ Session* session;
+ IRModule* module;
+ IRBuilder* builder;
+
+ // When inserting new globals, put them before this one.
+ IRGlobalValue* insertBeforeGlobal = nullptr;
+
+ Dictionary<IRValue*, LegalVal> mapValToLegalVal;
+};
+
+static void registerLegalizedValue(
+ TypeLegalizationContext* context,
+ IRValue* irValue,
+ LegalVal const& legalVal)
+{
+ context->mapValToLegalVal.Add(irValue, legalVal);
+}
+
+
+static bool isResourceType(Type* type)
+{
+ while (auto arrayType = type->As<ArrayExpressionType>())
+ {
+ type = arrayType->baseType;
+ }
+
+ if (auto textureTypeBase = type->As<TextureTypeBase>())
+ {
+ return true;
+ }
+ else if (auto samplerType = type->As<SamplerStateType>())
+ {
+ return true;
+ }
+
+ // TODO: need more comprehensive coverage here
+
+ return false;
+}
+
+// Legalize a type, including any nested types
+// that it transitively contains.
+static LegalType legalizeType(
+ TypeLegalizationContext* context,
+ Type* type)
+{
+ if (auto parameterBlockType = type->As<ParameterBlockType>())
+ {
+ // We basically legalize the `ParameterBlock<T>` type
+ // over to `T`. In order to represent this preoperly,
+ // we need to be careful to wrap it up in a way that
+ // tells us to eliminate downstream deferences...
+
+ auto legalElementType = legalizeType(context,
+ parameterBlockType->getElementType());
+ return LegalType::implicitDeref(legalElementType);
+ }
+ else if (isResourceType(type))
+ {
+ // We assume that any resource types not handled above
+ // are legal as-is.
+ return LegalType::simple(type);
+ }
+ else if (type->As<BasicExpressionType>())
+ {
+ return LegalType::simple(type);
+ }
+ else if (type->As<VectorExpressionType>())
+ {
+ return LegalType::simple(type);
+ }
+ else if (type->As<MatrixExpressionType>())
+ {
+ return LegalType::simple(type);
+ }
+ else if (auto declRefType = type->As<DeclRefType>())
+ {
+ auto declRef = declRefType->declRef;
+ if (auto aggTypeDeclRef = declRef.As<AggTypeDecl>())
+ {
+ // Look at the (non-static) fields, and
+ // see if anything needs to be cleaned up.
+
+ // We collect the legalized types for the fields,
+ // along with whether we've seen anything non-simple.
+ List<TupleType::Element> legalizedElements;
+ bool anyComplex = false;
+ bool anyResource = false;
+
+ for (auto ff : getMembersOfType<StructField>(aggTypeDeclRef))
+ {
+ if (ff.getDecl()->HasModifier<HLSLStaticModifier>())
+ continue;
+
+ auto fieldType = GetType(ff);
+ if (isResourceType(fieldType))
+ {
+ anyResource = true;
+ }
+
+ auto legalFieldType = legalizeType(context, fieldType);
+
+ TupleType::Element element;
+ element.fieldDeclRef = ff;
+ element.type = legalFieldType;
+ legalizedElements.Add(element);
+
+ switch (legalFieldType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ break;
+
+ default:
+ anyComplex = true;
+ break;
+ }
+ }
+
+ // If we didn't see anything that requires work,
+ // we can conceivably just use the type as-is
+ //
+ // TODO: this might be a good place to turn
+ // a reference to a generic `struct` type into
+ // a concrete non-generic type so that downstream
+ // codegen doesn't have to deal with generics...
+ //
+ // TODO: In fact, why not just fully replace
+ // all aggregate types here with some structural
+ // types defined in the IR?
+ if (!anyComplex && !anyResource)
+ {
+ return LegalType::simple(type);
+ }
+
+ // Okay, we are going to have to generate a
+ // "tuple" type.
+ //
+ // TODO: split out the "simple" fields into
+ // their own sub-type?
+
+ RefPtr<TupleType> tupleType = new TupleType();
+ tupleType->elements = legalizedElements;
+
+ return LegalType::tuple(tupleType);
+ }
+ }
+
+ return LegalType::simple(type);
+}
+
+// Legalize a type, and then expect it to
+// result in a simple type.
+static RefPtr<Type> legalizeSimpleType(
+ TypeLegalizationContext* context,
+ Type* type)
+{
+ auto legalType = legalizeType(context, type);
+ switch (legalType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ return legalType.getSimple();
+
+ default:
+ // TODO: need to issue a diagnostic here.
+ SLANG_UNEXPECTED("unexpected type case");
+ break;
+ }
+}
+
+// Take a value that is being used as an operand,
+// and turn it into the equivalent legalized value.
+static LegalVal legalizeOperand(
+ TypeLegalizationContext* context,
+ IRValue* irValue)
+{
+ LegalVal legalVal;
+ if (context->mapValToLegalVal.TryGetValue(irValue, legalVal))
+ return legalVal;
+
+ // For now, assume that anything not covered
+ // by the mapping is legal as-is.
+
+ return LegalVal::simple(irValue);
+}
+
+static LegalVal legalizeLoad(
+ TypeLegalizationContext* context,
+ LegalVal legalPtrVal)
+{
+ switch (legalPtrVal.flavor)
+ {
+ case LegalVal::Flavor::simple:
+ {
+ return LegalVal::simple(
+ context->builder->emitLoad(legalPtrVal.getSimple()));
+ }
+ break;
+
+ case LegalVal::Flavor::implicitDeref:
+ // We have turne a pointer(-like) type into its pointed-to (value)
+ // type, and so the operation of loading goes away; we just use
+ // the underlying value.
+ return legalPtrVal.getImplicitDeref();
+
+ case LegalVal::Flavor::tuple:
+ {
+ // We need to emit a load for each element of
+ // the tuple.
+ RefPtr<TupleVal> tupleVal = new TupleVal();
+ for (auto ee : legalPtrVal.getTuple()->elements)
+ {
+ TupleVal::Element element;
+ element.fieldDeclRef = ee.fieldDeclRef;
+ element.val = legalizeLoad(context, ee.val);
+
+
+ tupleVal->elements.Add(element);
+ }
+ return LegalVal::tuple(tupleVal);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unhandled case");
+ break;
+ }
+}
+
+static LegalVal legalizeFieldAddress(
+ TypeLegalizationContext* context,
+ LegalType type,
+ LegalVal legalPtrOperand,
+ LegalVal legalFieldOperand)
+{
+ auto builder = context->builder;
+
+ // We don't expect any legalization to affect
+ // the "field" argument.
+ auto fieldOperand = legalFieldOperand.getSimple();
+ assert(fieldOperand->op == kIROp_decl_ref);
+ auto fieldDeclRef = ((IRDeclRef*)fieldOperand)->declRef;
+
+ switch (legalPtrOperand.flavor)
+ {
+ case LegalVal::Flavor::simple:
+ return LegalVal::simple(
+ builder->emitFieldAddress(
+ type.getSimple(),
+ legalPtrOperand.getSimple(),
+ fieldOperand));
+
+ case LegalVal::Flavor::tuple:
+ {
+ // The operand is a tuple of pointer-like
+ // values, we want to extract the element
+ // corresponding to a field. We will handle
+ // this by simply returning the corresponding
+ // element from the operand.
+ for (auto ee : legalPtrOperand.getTuple()->elements)
+ {
+ if (ee.fieldDeclRef.Equals(fieldDeclRef))
+ {
+ return ee.val;
+ }
+ }
+ SLANG_UNEXPECTED("didn't find tuple element");
+ return LegalVal();
+ }
+
+ default:
+ SLANG_UNEXPECTED("unhandled");
+ return LegalVal();
+ }
+}
+
+static LegalVal legalizeInst(
+ TypeLegalizationContext* context,
+ IRInst* inst,
+ LegalType type,
+ LegalVal const* args)
+{
+ switch (inst->op)
+ {
+ case kIROp_Load:
+ return legalizeLoad(context, args[0]);
+
+ case kIROp_FieldAddress:
+ return legalizeFieldAddress(context, type, args[0], args[1]);
+
+ default:
+ // TODO: produce a user-visible diagnostic here
+ SLANG_UNEXPECTED("non-simple operand(s)!");
+ break;
+ }
+}
+
+static LegalVal legalizeInst(
+ TypeLegalizationContext* context,
+ IRInst* inst)
+{
+ // Need to legalize all the operands.
+ auto argCount = inst->getArgCount();
+ List<LegalVal> legalArgs;
+ bool anyComplex = false;
+ for (UInt aa = 0; aa < argCount; ++aa)
+ {
+ auto oldArg = inst->getArg(aa);
+ auto legalArg = legalizeOperand(context, oldArg);
+ legalArgs.Add(legalArg);
+
+ if (legalArg.flavor != LegalVal::Flavor::simple)
+ anyComplex = true;
+ }
+
+ // Also legalize the type of the instruction
+ LegalType legalType = legalizeType(context, inst->type);
+
+ if (!anyComplex && legalType.flavor == LegalType::Flavor::simple)
+ {
+ // Nothing interesting happened to the operands,
+ // so we seem to be okay, right?
+
+ for (UInt aa = 0; aa < argCount; ++aa)
+ {
+ auto legalArg = legalArgs[aa];
+ inst->setArg(aa, legalArg.getSimple());
+ }
+
+ inst->type = legalType.getSimple();
+
+ return LegalVal::simple(inst);
+ }
+
+ // We have at least one "complex" operand, and we
+ // need to figure out what to do with it. The anwer
+ // will, in general, depend on what we are doing.
+
+ // We will set up the IR builder so that any new
+ // instructions generated will be placed after
+ // the location of the original instruct.
+ auto builder = context->builder;
+ builder->curBlock = inst->getParentBlock();
+ builder->insertBeforeInst = inst->getNextInst();
+
+ LegalVal legalVal = legalizeInst(
+ context,
+ inst,
+ legalType,
+ legalArgs.Buffer());
+
+ // After we are done, we will eliminate the
+ // original instruction by removing it from
+ // the IR.
+ //
+ // TODO: we need to add it to a list of
+ // instructions to be cleaned up...
+ inst->removeFromParent();
+
+ // The value to be used when referencing
+ // the original instruction will now be
+ // whatever value(s) we created to replace it.
+ return legalVal;
+}
+
+static void legalizeFunc(
+ TypeLegalizationContext* context,
+ IRFunc* irFunc)
+{
+ // Overwrite the function's type with
+ // the result of legalization.
+ irFunc->type = legalizeSimpleType(context, irFunc->type);
+
+ // Go through the blocks of the function
+ for (auto bb = irFunc->getFirstBlock(); bb; bb = bb->getNextBlock())
+ {
+ // Legalize the parameters of the block, which may
+ // involve increasing the number of parameters
+ for (auto pp = bb->getFirstParam(); pp; pp = pp->getNextParam())
+ {
+ auto legalParamType = legalizeType(context, pp->getType());
+
+ switch (legalParamType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ // The type is simple, so we can just rewrite it in place
+ pp->type = legalParamType.getSimple();
+ break;
+
+ default:
+ // We have something like a tuple, and will need
+ // to expand into multiple parameters now.
+ SLANG_UNEXPECTED("need to handle it!");
+ break;
+ }
+
+ }
+
+
+ // Now legalize the instructions inside the block
+ IRInst* nextInst = nullptr;
+ for (auto ii = bb->getFirstInst(); ii; ii = nextInst)
+ {
+ nextInst = ii->getNextInst();
+
+ LegalVal legalVal = legalizeInst(context, ii);
+
+ registerLegalizedValue(context, ii, legalVal);
+ }
+ }
+}
+
+// Represents the "chain" of declarations that
+// were followed to get to a variable that we
+// are now declaring as a leaf variable.
+struct LegalVarChain
+{
+ LegalVarChain* next;
+ VarLayout* varLayout;
+};
+
+static LegalVal declareSimpleVar(
+ TypeLegalizationContext* context,
+ IROp op,
+ Type* type,
+ TypeLayout* typeLayout,
+ LegalVarChain* varChain)
+{
+ RefPtr<VarLayout> varLayout;
+ if (typeLayout)
+ {
+ // We need to construct a layout for the new variable
+ // that reflects both the type we have given it, as
+ // well as all the offset information that has accumulated
+ // along the chain of parent variables.
+
+ varLayout = new VarLayout();
+ varLayout->typeLayout = typeLayout;
+
+ for (auto rr : typeLayout->resourceInfos)
+ {
+ auto resInfo = varLayout->findOrAddResourceInfo(rr.kind);
+
+ for (auto vv = varChain; vv; vv = vv->next)
+ {
+ if (auto parentResInfo = vv->varLayout->FindResourceInfo(rr.kind))
+ {
+ resInfo->index += parentResInfo->index;
+ resInfo->space += parentResInfo->space;
+ }
+ }
+ }
+
+ // Some of the parent variables might actually contain offsets
+ // to the `space` or `set` of the field, and we need to apply
+ // those to all the nested resource infos.
+ for (auto vv = varChain; vv; vv = vv->next)
+ {
+ auto parentSpaceInfo = vv->varLayout->findOrAddResourceInfo(LayoutResourceKind::ParameterBlock);
+ if (!parentSpaceInfo)
+ continue;
+
+ for (auto& rr : varLayout->resourceInfos)
+ {
+ if (rr.kind == LayoutResourceKind::ParameterBlock)
+ {
+ rr.index += parentSpaceInfo->index;
+ }
+ else
+ {
+ rr.space += parentSpaceInfo->index;
+ }
+ }
+ }
+ }
+
+ switch (op)
+ {
+ case kIROp_global_var:
+ {
+ IRBuilder* builder = context->builder;
+
+ auto globalVar = builder->createGlobalVar(type);
+ globalVar->removeFromParent();
+ globalVar->insertBefore(context->insertBeforeGlobal);
+
+ if (varLayout)
+ {
+ builder->addLayoutDecoration(globalVar, varLayout);
+ }
+
+ return LegalVal::simple(globalVar);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unexpected IR opcode");
+ break;
+ }
+}
+
+static RefPtr<TypeLayout> getDerefTypeLayout(
+ TypeLayout* typeLayout)
+{
+ if (!typeLayout)
+ return nullptr;
+
+ if (auto parameterGroupTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout))
+ {
+ return parameterGroupTypeLayout->elementTypeLayout;
+ }
+
+ return typeLayout;
+}
+
+static RefPtr<VarLayout> getFieldLayout(
+ TypeLayout* typeLayout,
+ DeclRef<VarDeclBase> fieldDeclRef)
+{
+ if (!typeLayout)
+ return nullptr;
+
+ if (auto structTypeLayout = dynamic_cast<StructTypeLayout*>(typeLayout))
+ {
+ RefPtr<VarLayout> fieldLayout;
+ if (structTypeLayout->mapVarToLayout.TryGetValue(fieldDeclRef.getDecl(), fieldLayout))
+ return fieldLayout;
+ }
+
+ return nullptr;
+}
+
+static LegalVal declareVars(
+ TypeLegalizationContext* context,
+ IROp op,
+ LegalType type,
+ TypeLayout* typeLayout,
+ LegalVarChain* varChain)
+{
+ switch (type.flavor)
+ {
+ case LegalType::Flavor::simple:
+ return declareSimpleVar(context, op, type.getSimple(), typeLayout, varChain);
+ break;
+
+ case LegalType::Flavor::implicitDeref:
+ {
+ // Just declare a variable of the pointed-to type,
+ // since we are removing the indirection.
+
+ auto val = declareVars(
+ context,
+ op,
+ type.getImplicitDeref()->valueType,
+ getDerefTypeLayout(typeLayout),
+ varChain);
+ return LegalVal::implicitDeref(val);
+ }
+ break;
+
+ case LegalType::Flavor::tuple:
+ {
+ // Declare one variable for each element of the tuple
+ auto tupleType = type.getTuple();
+
+ RefPtr<TupleVal> tupleVal = new TupleVal();
+
+ for (auto ee : tupleType->elements)
+ {
+ auto fieldLayout = getFieldLayout(typeLayout, ee.fieldDeclRef);
+ RefPtr<TypeLayout> fieldTypeLayout = fieldLayout ? fieldLayout->typeLayout : nullptr;
+
+ // If we are processing layout information, then
+ // we need to create a new link in the chain
+ // of variables that will determine offsets
+ // for the eventual leaf fields...
+ LegalVarChain newVarChainStorage;
+ LegalVarChain* newVarChain = varChain;
+ if (fieldLayout)
+ {
+ newVarChainStorage.next = varChain;
+ newVarChainStorage.varLayout = fieldLayout;
+ newVarChain = &newVarChainStorage;
+ }
+
+ TupleVal::Element element;
+ element.fieldDeclRef = ee.fieldDeclRef;
+ element.val = declareVars(
+ context,
+ op,
+ ee.type,
+ fieldTypeLayout,
+ newVarChain);
+ tupleVal->elements.Add(element);
+ }
+
+ return LegalVal::tuple(tupleVal);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unhandled");
+ break;
+ }
+}
+
+RefPtr<VarLayout> findVarLayout(IRValue* value)
+{
+ if (auto layoutDecoration = value->findDecoration<IRLayoutDecoration>())
+ return layoutDecoration->layout.As<VarLayout>();
+ return nullptr;
+}
+
+static void legalizeGlobalVar(
+ TypeLegalizationContext* context,
+ IRGlobalVar* irGlobalVar)
+{
+ // Legalize the type for the variable's value
+ auto legalValueType = legalizeType(
+ context,
+ irGlobalVar->getType()->getValueType());
+
+ RefPtr<VarLayout> varLayout = findVarLayout(irGlobalVar);
+ RefPtr<TypeLayout> typeLayout = varLayout ? varLayout->typeLayout : nullptr;
+
+ // If we've decided to do implicit deref on the type,
+ // then go ahead and declare a value of the pointed-to type.
+ LegalType maybeSimpleType = legalValueType;
+ while (maybeSimpleType.flavor == LegalType::Flavor::implicitDeref)
+ {
+ maybeSimpleType = maybeSimpleType.getImplicitDeref()->valueType;
+ }
+
+ switch (maybeSimpleType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ // Easy case: the type is usable as-is, and we
+ // should just do that.
+ irGlobalVar->type = context->session->getPtrType(
+ maybeSimpleType.getSimple());
+ break;
+
+ default:
+ {
+ context->insertBeforeGlobal = irGlobalVar->getNextValue();
+
+ LegalVarChain* varChain = nullptr;
+ LegalVarChain varChainStorage;
+ if (varLayout)
+ {
+ varChainStorage.next = nullptr;
+ varChainStorage.varLayout = varLayout;
+ varChain = &varChainStorage;
+ }
+
+ LegalVal newVal = declareVars(context, kIROp_global_var, legalValueType, typeLayout, varChain);
+
+ // Register the new value as the replacement for the old
+ registerLegalizedValue(context, irGlobalVar, newVal);
+
+ // Remove the old global from the module.
+ irGlobalVar->removeFromParent();
+ // TODO: actually clean up the global!
+ }
+ break;
+ }
+}
+
+static void legalizeGlobalValue(
+ TypeLegalizationContext* context,
+ IRGlobalValue* irValue)
+{
+ switch (irValue->op)
+ {
+ case kIROp_witness_table:
+ // Just skip these.
+ break;
+
+ case kIROp_Func:
+ legalizeFunc(context, (IRFunc*)irValue);
+ break;
+
+ case kIROp_global_var:
+ legalizeGlobalVar(context, (IRGlobalVar*)irValue);
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unknown global value type");
+ break;
+ }
+}
+
+static void legalizeTypes(
+ TypeLegalizationContext* context)
+{
+ auto module = context->module;
+ for (auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue())
+ {
+ legalizeGlobalValue(context, gv);
+ }
+}
+
+
+void legalizeTypes(
+ IRModule* module)
+{
+ auto session = module->session;
+
+ SharedIRBuilder sharedBuilderStorage;
+ auto sharedBuilder = &sharedBuilderStorage;
+
+ sharedBuilder->session = session;
+ sharedBuilder->module = module;
+
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+
+ builder->sharedBuilder = sharedBuilder;
+
+
+ TypeLegalizationContext contextStorage;
+ auto context = &contextStorage;
+
+ context->session = session;
+ context->module = module;
+ context->builder = builder;
+
+ legalizeTypes(context);
+
+}
+
+#if 0
+ typedef unsigned int TypeScalarizationFlags;
+ enum TypeScalarizationFlag
+ {
+ anyResource = 0x1,
+ anyNonResource = 0x2,
+ anyAggregate = 0x4,
+ };
+
+ bool isResourceType(Type* type)
+ {
+ while (auto arrayType = type->As<ArrayExpressionType>())
+ {
+ type = arrayType->baseType;
+ }
+
+ if (auto textureTypeBase = type->As<TextureTypeBase>())
+ {
+ return true;
+ }
+ else if (auto samplerType = type->As<SamplerStateType>())
+ {
+ return true;
+ }
+
+ // TODO: need more comprehensive coverage here
+
+ return false;
+ }
+
+ TypeScalarizationFlags getTypeScalarizationFlags(
+ Session* session,
+ Type* type)
+ {
+ // TODO: we should probably cache flags once
+ // they are computed, to avoid O(N^2) sorts
+ // of behavior.
+
+ if (isResourceType(type))
+ return TypeScalarizationFlag::anyNonResource;
+
+ if(type->As<BasicExpressionType>())
+ {
+ return TypeScalarizationFlag::anyNonResource;
+ }
+ if(type->As<VectorExpressionType>())
+ {
+ return TypeScalarizationFlag::anyNonResource;
+ }
+ if(type->As<MatrixExpressionType>())
+ {
+ return TypeScalarizationFlag::anyNonResource;
+ }
+ else if (auto declRefType = type->As<DeclRefType>())
+ {
+ auto declRef = declRefType->declRef;
+ if (auto structDeclRef = declRef.As<StructDecl>())
+ {
+ TypeScalarizationFlags flags = TypeScalarizationFlag::anyAggregate;
+
+ // For structure types, the basic rule will be
+ // that if the type contains *any* resource-type
+ // fields, then it needs to be scalarized.
+ // If it contains any non-resource-type fields,
+ // then we should aggregate these into a single
+ // new `struct` type with just the non-resource
+ // fields.
+ for (auto fieldDeclRef : getMembersOfType<StructField>(structDeclRef))
+ {
+ auto fieldType = GetType(fieldDeclRef);
+
+ // TODO: we are making a recursive call here, so
+ // this will break if/when we ever allowed a recursive type!
+ auto fieldFlags = getTypeScalarizationFlags(session, fieldType);
+ flags |= fieldFlags;
+
+ }
+
+ return flags;
+ }
+ }
+ else if (auto arrayType = type->As<ArrayExpressionType>())
+ {
+ return getTypeScalarizationFlags(
+ session,
+ arrayType->baseType);
+ }
+
+ // Default behavior: assume we have a non-resource type
+ return TypeScalarizationFlag::anyNonResource;
+ }
+
+ struct ArrayScalarizationInfo
+ {
+ ArrayScalarizationInfo* next;
+ RefPtr<IntVal> elementCount;
+ RefPtr<ArrayTypeLayout> typeLayout;
+ };
+
+ struct SharedScalarizationContext
+ {
+
+ };
+
+ struct ScalarizationContext
+ {
+ SharedScalarizationContext* shared;
+
+ IRBuilder* builder;
+ IRGlobalVar* globalVar;
+ VarLayout* globalVarLayout;
+
+ IRGlobalValue* valueToInsertAfter;
+ };
+
+ IRValue* emitSimpleScalarizedField(
+ ScalarizationContext* context,
+ Type* inType,
+ VarLayout* fieldLayout,
+ TypeLayout* inTypeLayout,
+ ArrayScalarizationInfo* arrayInfo)
+ {
+ auto builder = context->builder;
+ auto globalVar = context->globalVar;
+ auto globalVarLayout = context->globalVarLayout;
+ auto valueToInsertAfter = context->valueToInsertAfter;
+
+ RefPtr<Type> type = inType;
+ RefPtr<TypeLayout> typeLayout = inTypeLayout;
+
+ // If we are turning an array-of-structs into
+ // a struct-of-arrays, then we need to apply
+ // all the appropriate array dimensions here.
+ for (auto aa = arrayInfo; aa; aa = aa->next)
+ {
+ type = builder->getSession()->getArrayType(type, aa->elementCount);
+
+ if (typeLayout)
+ {
+ RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout();
+ arrayTypeLayout->elementTypeLayout = typeLayout;
+
+ // TODO: fill in the other fields!
+
+ typeLayout = arrayTypeLayout;
+ }
+ }
+
+ RefPtr<VarLayout> newVarLayout;
+ if (typeLayout)
+ {
+ newVarLayout = new VarLayout();
+ newVarLayout->typeLayout = typeLayout;
+
+ if (fieldLayout)
+ {
+ for (auto fieldResourceInfo : fieldLayout->resourceInfos)
+ {
+ auto newResourceInfo = newVarLayout->findOrAddResourceInfo(fieldResourceInfo.kind);
+
+ if (globalVarLayout)
+ {
+ if (auto globalResourceInfo = globalVarLayout->FindResourceInfo(fieldResourceInfo.kind))
+ {
+ newResourceInfo->index += globalResourceInfo->index;
+ newResourceInfo->space += globalResourceInfo->space;
+ }
+ }
+
+ newResourceInfo->index += fieldResourceInfo.index;
+ newResourceInfo->space += fieldResourceInfo.space;
+ }
+ }
+ }
+
+ auto newGlobalVar = addGlobalVariable(builder->getModule(), type);
+ builder->addLayoutDecoration(newGlobalVar, newVarLayout);
+
+ newGlobalVar->removeFromParent();
+ newGlobalVar->insertAfter(valueToInsertAfter);
+
+ context->valueToInsertAfter = newGlobalVar;
+
+ return newGlobalVar;
+ }
+
+ void scalarizeGlobalVariable(
+ ScalarizationContext* context,
+ Type* valueType,
+ TypeLayout* valueTypeLayout,
+ ArrayScalarizationInfo* arrayInfo)
+ {
+ if (auto arrayType = valueType->As<ArrayExpressionType>())
+ {
+ // Okay, we need to recurse down and scalarize the
+ // array element type, wrapping up each field in
+ // an array declarator as needed.
+
+ ArrayScalarizationInfo newArrayInfo;
+ newArrayInfo.next = arrayInfo;
+ newArrayInfo.elementCount = arrayType->ArrayLength;
+
+ RefPtr<TypeLayout> elementTypeLayout;
+ if (auto arrayTypeLayout = dynamic_cast<ArrayTypeLayout*>(valueTypeLayout))
+ {
+ newArrayInfo.typeLayout = arrayTypeLayout;
+ elementTypeLayout = arrayTypeLayout->elementTypeLayout;
+ }
+
+ scalarizeGlobalVariable(
+ context,
+ arrayType->baseType,
+ elementTypeLayout,
+ &newArrayInfo);
+
+ // Now we need to look at all uses of the variable,
+ // and properly rework element-index operations
+ // to instead index into the sub-arrays...
+ }
+ else if (auto declRefType = valueType->As<DeclRefType>())
+ {
+ auto declRef = declRefType->declRef;
+ if (auto aggTypeDeclRef = declRef.As<AggTypeDecl>())
+ {
+ RefPtr<StructTypeLayout> structTypeLayout = dynamic_cast<StructTypeLayout*>(valueTypeLayout);
+
+ // Okay, we need to look through the fields, and
+ // create a new variable for each of them.
+ Dictionary<Decl*, IRValue*> fieldMap;
+ UInt fieldCounter = 0;
+ for (auto fieldDeclRef : getMembersOfType<StructField>(aggTypeDeclRef))
+ {
+ UInt fieldIndex = fieldCounter++;
+
+ RefPtr<VarLayout> fieldLayout;
+ RefPtr<TypeLayout> fieldTypeLayout;
+ if (structTypeLayout)
+ {
+ fieldLayout = structTypeLayout->fields[fieldIndex];
+ fieldTypeLayout = fieldLayout->typeLayout;
+ }
+
+ // Note: we do *not* try to deal with recursive
+ // expansion of the fields here, and instead
+ // prefer to handle those in further
+ // simplification passes.
+
+ auto fieldGlobalVar = emitSimpleScalarizedField(
+ context,
+ GetType(fieldDeclRef),
+ fieldLayout,
+ fieldTypeLayout,
+ arrayInfo);
+
+ fieldMap.Add(fieldDeclRef.getDecl(), fieldGlobalVar);
+ }
+
+ // Now we need to scan for uses of the original variable,
+ // and replace them with uses of the individual fields.
+ auto globalVar = context->globalVar;
+ IRUse* nextUse = nullptr;
+ for (IRUse* use = globalVar->firstUse; use; use = nextUse)
+ {
+ nextUse = use->nextUse;
+
+ IRUser* user = use->user;
+ switch (user->op)
+ {
+ case kIROp_FieldAddress:
+ {
+ // This should be the easy case: we are taking
+ // the address of a field inside this global
+ // value, so we can just return the adress
+ // of the global value that replaced that field.
+ IRFieldAddress* fieldAddressInst = (IRFieldAddress*)user;
+
+ IRValue* fieldOperand = fieldAddressInst->getField();
+ assert(fieldOperand->op == kIROp_decl_ref);
+ auto fieldDeclRef = ((IRDeclRef*)fieldOperand)->declRef;
+ auto fieldDecl = fieldDeclRef.getDecl();
+
+ IRValue* fieldVar = *fieldMap.TryGetValue(fieldDecl);
+
+ fieldAddressInst->replaceUsesWith(fieldVar);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("what to do?");
+ break;
+ }
+ }
+ }
+ else
+ {
+ SLANG_UNEXPECTED("not handled");
+ }
+ }
+ else
+ {
+ SLANG_UNEXPECTED("not handled");
+ }
+ }
+
+ void scalarizeGlobalVariable(
+ SharedScalarizationContext* sharedContext,
+ IRBuilder* builder,
+ IRGlobalVar* globalVar,
+ VarLayout* globalVarLayout,
+ Type* valueType,
+ TypeLayout* valueTypeLayout)
+ {
+ ScalarizationContext contextStorage;
+ auto context = &contextStorage;
+
+ context->shared = sharedContext;
+ context->builder = builder;
+ context->globalVar = globalVar;
+ context->globalVarLayout = globalVarLayout;
+ context->valueToInsertAfter = globalVar;
+
+ scalarizeGlobalVariable(
+ context,
+ valueType,
+ valueTypeLayout,
+ nullptr);
+ }
+
+ RefPtr<VarLayout> findVarLayout(IRValue* value)
+ {
+ if (auto layoutDecoration = value->findDecoration<IRLayoutDecoration>())
+ return layoutDecoration->layout.As<VarLayout>();
+ return nullptr;
+ }
+
+ void scalarizeMixedResourceTypes(
+ Session* session,
+ IRModule* module)
+ {
+ SharedIRBuilder sharedBuilderStorage;
+ auto sharedBuilder = &sharedBuilderStorage;
+
+ sharedBuilder->session = session;
+ sharedBuilder->module = module;
+
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+
+ builder->shared = sharedBuilder;
+
+ SharedScalarizationContext sharedContextStorage;
+ auto sharedContext = &sharedContextStorage;
+
+
+ List<IRValue*> workList;
+ for (auto gv = module->getFirstGlobalValue(); gv; gv = gv->getNextValue())
+ {
+ workList.Add(gv);
+ }
+
+ while (workList.Count())
+ {
+ IRValue* value = workList[0];
+ workList.FastRemoveAt(0);
+
+ switch (value->op)
+ {
+ case kIROp_Func:
+ {
+ // TODO: need to iterate over parameters of
+ // the function (and its blocks) to make
+ // sure that any types that need scalarization
+ // are properly handled.
+ }
+ break;
+
+ case kIROp_global_var:
+ {
+ IRGlobalVar* globalVar = (IRGlobalVar*)value;
+ auto valueType = globalVar->getType()->getValueType();
+
+ auto flags = getTypeScalarizationFlags(session, valueType);
+ if (!(flags & (TypeScalarizationFlag::anyNonResource | TypeScalarizationFlag::anyAggregate)))
+ continue;
+
+ auto varLayout = findVarLayout(globalVar);
+ RefPtr<TypeLayout> typeLayout = varLayout ? varLayout->typeLayout : nullptr;
+
+ // Okay, we have a variable of some composite type
+ // that we need to scalarize. Since this is a global,
+ // we also need to be careful to deal with any
+ // layout information that has been attached.
+
+ scalarizeGlobalVariable(
+ sharedContext,
+ builder,
+ globalVar,
+ varLayout,
+ valueType,
+ typeLayout);
+
+ globalVar->removeFromParent();
+ // TODO: need to destroy this global!
+ }
+ break;
+
+ default:
+ {
+ // TODO: look at the type of the value,
+ // and if it needs scalarization, replace
+ // it with a tuple here.
+ }
+ break;
+ }
+ }
+ }
+
+
+#endif
+
+}
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index ab961a159..d6a01a484 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -55,6 +55,28 @@ namespace Slang
}
}
+ void IRUse::set(IRValue* usedValue)
+ {
+ // clear out the old value
+ if (usedValue)
+ {
+ *prevLink = nextUse;
+ }
+
+ init(user, usedValue);
+ }
+
+ void IRUse::clear()
+ {
+ if (usedValue)
+ {
+ *prevLink = nextUse;
+ }
+
+ user = nullptr;
+ usedValue = nullptr;
+ }
+
//
IRUse* IRUser::getArgs()
@@ -3683,9 +3705,8 @@ namespace Slang
// these should get run whether or not the entry point
// references them.
- // Depending on the downstream target, we may need to apply some
- // guaranteed transformations to legalize things. We will go
- // ahead and apply there here for now.
+ // For GLSL only, we will need to perform "legalization" of
+ // the entry point and any entry-point parameters.
switch (target)
{
case CodeGenTarget::GLSL:
diff --git a/source/slang/ir.h b/source/slang/ir.h
index 2e15e39d6..12dc08e13 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -93,6 +93,8 @@ struct IRUse
IRUse** prevLink;
void init(IRUser* user, IRValue* usedValue);
+ void set(IRValue* usedValue);
+ void clear();
};
enum IRDecorationOp : uint16_t
@@ -255,6 +257,11 @@ struct IRUser : IRChildValue
{
return getArgs()[index].usedValue;
}
+
+ void setArg(UInt index, IRValue* value)
+ {
+ getArgs()[index].set(value);
+ }
};
// Instructions are values that are children of a basic block,
diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp
index 44ad5c272..ccf302027 100644
--- a/source/slang/lower.cpp
+++ b/source/slang/lower.cpp
@@ -3357,9 +3357,9 @@ struct LoweringVisitor
if (!typeLayout)
return nullptr;
- while (auto parameterBlockTypeLayout = typeLayout.As<ParameterBlockTypeLayout>())
+ while (auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>())
{
- typeLayout = parameterBlockTypeLayout->elementTypeLayout;
+ typeLayout = parameterGroupTypeLayout->elementTypeLayout;
}
while (auto arrayTypeLayout = typeLayout.As<ArrayTypeLayout>())
@@ -3435,7 +3435,7 @@ struct LoweringVisitor
shared->loweredDecls.Add(decl, tupleDecl);
return tupleDecl;
}
- if (auto bufferType = loweredType->As<UniformParameterBlockType>())
+ if (auto bufferType = loweredType->As<UniformParameterGroupType>())
{
auto varLayout = tryToFindLayout(decl).As<VarLayout>();
diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h
index c563d35cf..0f92fcd61 100644
--- a/source/slang/modifier-defs.h
+++ b/source/slang/modifier-defs.h
@@ -144,11 +144,11 @@ SIMPLE_SYNTAX_CLASS(GLSLPatchModifier , SimpleModifier)
// Indicates that this is a variable declaration that corresponds to
// a parameter block declaration in the source program.
-SIMPLE_SYNTAX_CLASS(ImplicitParameterBlockVariableModifier , Modifier)
+SIMPLE_SYNTAX_CLASS(ImplicitParameterGroupVariableModifier , Modifier)
// Indicates that this is a type that corresponds to the element
// type of a parameter block declaration in the source program.
-SIMPLE_SYNTAX_CLASS(ImplicitParameterBlockElementTypeModifier, Modifier)
+SIMPLE_SYNTAX_CLASS(ImplicitParameterGroupElementTypeModifier, Modifier)
// An HLSL semantic
ABSTRACT_SYNTAX_CLASS(HLSLSemantic, Modifier)
@@ -197,7 +197,7 @@ SYNTAX_CLASS(GLSLExtensionDirective, GLSLPreprocessorDirective)
FIELD(Token, dispositionToken)
END_SYNTAX_CLASS()
-SYNTAX_CLASS(ParameterBlockReflectionName, Modifier)
+SYNTAX_CLASS(ParameterGroupReflectionName, Modifier)
FIELD(NameLoc, nameAndLoc)
END_SYNTAX_CLASS()
diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp
index 18294bb3e..05b9d924e 100644
--- a/source/slang/parameter-binding.cpp
+++ b/source/slang/parameter-binding.cpp
@@ -205,6 +205,9 @@ struct SharedParameterBindingContext
// This is only used for varying input/output.
//
Dictionary<TranslationUnitRequest*, RefPtr<UsedRangeSet>> translationUnitUsedRangeSets;
+
+ // Which register spaces have been claimed so far?
+ UsedRanges usedSpaces;
};
static DiagnosticSink* getSink(SharedParameterBindingContext* shared)
@@ -379,7 +382,7 @@ static bool findLayoutArg(
static Name* getReflectionName(VarDeclBase* varDecl)
{
- if (auto reflectionNameModifier = varDecl->FindModifier<ParameterBlockReflectionName>())
+ if (auto reflectionNameModifier = varDecl->FindModifier<ParameterGroupReflectionName>())
return reflectionNameModifier->nameAndLoc.name;
return varDecl->getName();
@@ -398,7 +401,7 @@ RefPtr<Type> tryGetEffectiveTypeForGLSLVaryingInput(
return nullptr;
auto type = varDecl->getType();
- if( varDecl->HasModifier<InModifier>() || type->As<GLSLInputParameterBlockType>())
+ if( varDecl->HasModifier<InModifier>() || type->As<GLSLInputParameterGroupType>())
{
// Special case to handle "arrayed" shader inputs, as used
// for Geometry and Hull input
@@ -436,7 +439,7 @@ RefPtr<Type> tryGetEffectiveTypeForGLSLVaryingOutput(
return nullptr;
auto type = varDecl->getType();
- if( varDecl->HasModifier<OutModifier>() || type->As<GLSLOutputParameterBlockType>())
+ if( varDecl->HasModifier<OutModifier>() || type->As<GLSLOutputParameterGroupType>())
{
// Special case to handle "arrayed" shader outputs, as used
// for Hull Shader output
@@ -716,6 +719,24 @@ static RefPtr<UsedRangeSet> findUsedRangeSetForSpace(
return usedRangeSet;
}
+// Record that a particular register space (or set, in the GLSL case)
+// has been used in at least one binding, and so it should not
+// be used by auto-generated bindings that need to claim entire
+// spaces.
+static void markSpaceUsed(
+ ParameterBindingContext* context,
+ UInt space)
+{
+ context->shared->usedSpaces.Add(nullptr, space, space+1);
+}
+
+static UInt allocateUnusedSpaces(
+ ParameterBindingContext* context,
+ UInt count)
+{
+ return context->shared->usedSpaces.Allocate(nullptr, count);
+}
+
static RefPtr<UsedRangeSet> findUsedRangeSetForTranslationUnit(
ParameterBindingContext* context,
TranslationUnitRequest* translationUnit)
@@ -773,6 +794,12 @@ static void addExplicitParameterBinding(
if (!usedRangeSet)
{
usedRangeSet = findUsedRangeSetForSpace(context, semanticInfo.space);
+
+ // Record that the particular binding space was
+ // used by an explicit binding, so that we don't
+ // claim it for auto-generated bindings that
+ // need to grab a full space
+ markSpaceUsed(context, semanticInfo.space);
}
auto overlappedParameterInfo = usedRangeSet->usedResourceRanges[(int)semanticInfo.kind].Add(
parameterInfo,
@@ -950,6 +977,26 @@ static void completeBindingsForParameter(
continue;
}
+ auto count = typeRes.count;
+
+ // We need to special-case the scenario where
+ // a parameter wants to claim an entire register
+ // space to itself (for a parameter block), since
+ // that can't be handled like other resources.
+ if (kind == LayoutResourceKind::ParameterBlock)
+ {
+ // We need to snag a register space of our own.
+
+ UInt space = allocateUnusedSpaces(context, count);
+
+ bindingInfo.count = count;
+ bindingInfo.index = space;
+ bindingInfo.space = 0;
+
+ continue;
+ }
+
+
// For now we only auto-generate bindings in space zero
//
// TODO: we may want to support searching for a space with
@@ -970,7 +1017,6 @@ static void completeBindingsForParameter(
break;
}
- auto count = typeRes.count;
bindingInfo.count = count;
bindingInfo.index = usedRangeSet->usedResourceRanges[(int)kind].Allocate(parameterInfo, (int) count);
@@ -1699,7 +1745,7 @@ void generateParameterBindings(
// up a global constant buffer type layout to hold them
if( anyGlobalUniforms )
{
- auto globalConstantBufferLayout = createParameterBlockTypeLayout(
+ auto globalConstantBufferLayout = createParameterGroupTypeLayout(
nullptr,
globalScopeRules,
globalScopeRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer),
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index 57956485a..595d58d7e 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -1738,16 +1738,16 @@ namespace Slang
auto reflectionNameToken = parser->ReadToken(TokenType::Identifier);
// Attach the reflection name to the block so we can use it
- auto reflectionNameModifier = new ParameterBlockReflectionName();
+ auto reflectionNameModifier = new ParameterGroupReflectionName();
reflectionNameModifier->nameAndLoc = NameLoc(reflectionNameToken);
addModifier(bufferVarDecl, reflectionNameModifier);
// Both the buffer variable and its type need to have names generated
- bufferVarDecl->nameAndLoc.name = generateName(parser, "parameterBlock_" + reflectionNameToken.Content);
- bufferDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterBlock_" + reflectionNameToken.Content);
+ bufferVarDecl->nameAndLoc.name = generateName(parser, "parameterGroup_" + reflectionNameToken.Content);
+ bufferDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterGroup_" + reflectionNameToken.Content);
- addModifier(bufferDataTypeDecl, new ImplicitParameterBlockElementTypeModifier());
- addModifier(bufferVarDecl, new ImplicitParameterBlockVariableModifier());
+ addModifier(bufferDataTypeDecl, new ImplicitParameterGroupElementTypeModifier());
+ addModifier(bufferVarDecl, new ImplicitParameterGroupVariableModifier());
// TODO(tfoley): We end up constructing unchecked syntax here that
// is expected to type check into the right form, but it might be
@@ -1875,12 +1875,12 @@ namespace Slang
else if( auto inMod = modifiers.findModifier<InModifier>() )
{
removeModifier(modifiers, inMod);
- blockWrapperTypeName = "__GLSLInputParameterBlock";
+ blockWrapperTypeName = "__GLSLInputParameterGroup";
}
else if( auto outMod = modifiers.findModifier<OutModifier>() )
{
removeModifier(modifiers, outMod);
- blockWrapperTypeName = "__GLSLOutputParameterBlock";
+ blockWrapperTypeName = "__GLSLOutputParameterGroup";
}
else if( auto bufferMod = modifiers.findModifier<GLSLBufferModifier>() )
{
@@ -1899,11 +1899,11 @@ namespace Slang
RefPtr<StructDecl> blockDataTypeDecl = new StructDecl();
RefPtr<Variable> blockVarDecl = new Variable();
- addModifier(blockDataTypeDecl, new ImplicitParameterBlockElementTypeModifier());
- addModifier(blockVarDecl, new ImplicitParameterBlockVariableModifier());
+ addModifier(blockDataTypeDecl, new ImplicitParameterGroupElementTypeModifier());
+ addModifier(blockVarDecl, new ImplicitParameterGroupVariableModifier());
// Attach the reflection name to the block so we can use it
- auto reflectionNameModifier = new ParameterBlockReflectionName();
+ auto reflectionNameModifier = new ParameterGroupReflectionName();
reflectionNameModifier->nameAndLoc = NameLoc(reflectionNameToken);
addModifier(blockVarDecl, reflectionNameModifier);
@@ -1912,7 +1912,7 @@ namespace Slang
parser->FillPosition(blockVarDecl.Ptr());
// Generate a unique name for the data type
- blockDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterBlock_" + reflectionNameToken.Content);
+ blockDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterGroup_" + reflectionNameToken.Content);
// TODO(tfoley): We end up constructing unchecked syntax here that
// is expected to type check into the right form, but it might be
@@ -1957,7 +1957,7 @@ namespace Slang
else
{
// synthesize a dummy name
- blockVarDecl->nameAndLoc.name = generateName(parser, "parameterBlock_" + reflectionNameToken.Content);
+ blockVarDecl->nameAndLoc.name = generateName(parser, "parameterGroup_" + reflectionNameToken.Content);
// Otherwise we have a transparent declaration, similar
// to an HLSL `cbuffer`
diff --git a/source/slang/profile-defs.h b/source/slang/profile-defs.h
index ea23398e1..177c4a0c5 100644
--- a/source/slang/profile-defs.h
+++ b/source/slang/profile-defs.h
@@ -77,6 +77,7 @@ PROFILE_VERSION(DX_4_0_Level_9_1, DX)
PROFILE_VERSION(DX_4_0_Level_9_3, DX)
PROFILE_VERSION(DX_4_1, DX)
PROFILE_VERSION(DX_5_0, DX)
+PROFILE_VERSION(DX_5_1, DX)
PROFILE_VERSION(DX_6_0, DX)
PROFILE_VERSION(DX_6_1, DX)
PROFILE_VERSION(DX_6_2, DX)
@@ -100,11 +101,13 @@ PROFILE_VERSION(GLSL_450, GLSL)
PROFILE(DX_Compute_4_0, cs_4_0, Compute, DX_4_0)
PROFILE(DX_Compute_4_1, cs_4_1, Compute, DX_4_1)
PROFILE(DX_Compute_5_0, cs_5_0, Compute, DX_5_0)
+PROFILE(DX_Compute_5_1, cs_5_1, Compute, DX_5_1)
PROFILE(DX_Compute_6_0, cs_6_0, Compute, DX_6_0)
PROFILE(DX_Compute_6_1, cs_6_1, Compute, DX_6_1)
PROFILE(DX_Compute_6_2, cs_6_2, Compute, DX_6_2)
PROFILE(DX_Domain_5_0, ds_5_0, Domain, DX_5_0)
+PROFILE(DX_Domain_5_1, ds_5_1, Domain, DX_5_1)
PROFILE(DX_Domain_6_0, ds_6_0, Domain, DX_6_0)
PROFILE(DX_Domain_6_1, ds_6_1, Domain, DX_6_1)
PROFILE(DX_Domain_6_2, ds_6_2, Domain, DX_6_2)
@@ -112,12 +115,14 @@ PROFILE(DX_Domain_6_2, ds_6_2, Domain, DX_6_2)
PROFILE(DX_Geometry_4_0, gs_4_0, Geometry, DX_4_0)
PROFILE(DX_Geometry_4_1, gs_4_1, Geometry, DX_4_1)
PROFILE(DX_Geometry_5_0, gs_5_0, Geometry, DX_5_0)
+PROFILE(DX_Geometry_5_1, gs_5_1, Geometry, DX_5_1)
PROFILE(DX_Geometry_6_0, gs_6_0, Geometry, DX_6_0)
PROFILE(DX_Geometry_6_1, gs_6_1, Geometry, DX_6_1)
PROFILE(DX_Geometry_6_2, gs_6_2, Geometry, DX_6_2)
PROFILE(DX_Hull_5_0, hs_5_0, Hull, DX_5_0)
+PROFILE(DX_Hull_5_1, hs_5_1, Hull, DX_5_1)
PROFILE(DX_Hull_6_0, hs_6_0, Hull, DX_6_0)
PROFILE(DX_Hull_6_1, hs_6_1, Hull, DX_6_1)
PROFILE(DX_Hull_6_2, hs_6_2, Hull, DX_6_2)
@@ -129,6 +134,7 @@ PROFILE(DX_Fragment_4_0_Level_9_1, ps_4_0_level_9_1, Fragment, DX_4_0_Level_9_1)
PROFILE(DX_Fragment_4_0_Level_9_3, ps_4_0_level_9_3, Fragment, DX_4_0_Level_9_3)
PROFILE(DX_Fragment_4_1, ps_4_1, Fragment, DX_4_1)
PROFILE(DX_Fragment_5_0, ps_5_0, Fragment, DX_5_0)
+PROFILE(DX_Fragment_5_1, ps_5_1, Fragment, DX_5_1)
PROFILE(DX_Fragment_6_0, ps_6_0, Fragment, DX_6_0)
PROFILE(DX_Fragment_6_1, ps_6_1, Fragment, DX_6_1)
PROFILE(DX_Fragment_6_2, ps_6_2, Fragment, DX_6_2)
@@ -140,6 +146,7 @@ PROFILE(DX_Vertex_4_0_Level_9_1, vs_4_0_level_9_1, Vertex, DX_4_0_Level_9_1)
PROFILE(DX_Vertex_4_0_Level_9_3, vs_4_0_level_9_3, Vertex, DX_4_0_Level_9_3)
PROFILE(DX_Vertex_4_1, vs_4_1, Vertex, DX_4_1)
PROFILE(DX_Vertex_5_0, vs_5_0, Vertex, DX_5_0)
+PROFILE(DX_Vertex_5_1, vs_5_1, Vertex, DX_5_1)
PROFILE(DX_Vertex_6_0, vs_6_0, Vertex, DX_6_0)
PROFILE(DX_Vertex_6_1, vs_6_1, Vertex, DX_6_1)
PROFILE(DX_Vertex_6_2, vs_6_2, Vertex, DX_6_2)
diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp
index d7e8820c0..3ed182750 100644
--- a/source/slang/reflection.cpp
+++ b/source/slang/reflection.cpp
@@ -502,7 +502,7 @@ SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_GetElementTypeLayout
{
return (SlangReflectionTypeLayout*) arrayTypeLayout->elementTypeLayout.Ptr();
}
- else if( auto constantBufferTypeLayout = dynamic_cast<ParameterBlockTypeLayout*>(typeLayout))
+ else if( auto constantBufferTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout))
{
return convert(constantBufferTypeLayout->elementTypeLayout.Ptr());
}
@@ -569,7 +569,7 @@ SLANG_API char const* spReflectionVariable_GetName(SlangReflectionVariable* inVa
// If the variable is one that has an "external" name that is supposed
// to be exposed for reflection, then report it here
- if(auto reflectionNameMod = var->FindModifier<ParameterBlockReflectionName>())
+ if(auto reflectionNameMod = var->FindModifier<ParameterGroupReflectionName>())
return getText(reflectionNameMod->nameAndLoc.name).Buffer();
return getText(var->getName()).Buffer();
@@ -680,9 +680,9 @@ namespace Slang
{
static unsigned getParameterCount(RefPtr<TypeLayout> typeLayout)
{
- if(auto parameterBlockLayout = typeLayout.As<ParameterBlockTypeLayout>())
+ if(auto parameterGroupLayout = typeLayout.As<ParameterGroupTypeLayout>())
{
- typeLayout = parameterBlockLayout->elementTypeLayout;
+ typeLayout = parameterGroupLayout->elementTypeLayout;
}
if(auto structLayout = typeLayout.As<StructTypeLayout>())
@@ -695,9 +695,9 @@ namespace Slang
static VarLayout* getParameterByIndex(RefPtr<TypeLayout> typeLayout, unsigned index)
{
- if(auto parameterBlockLayout = typeLayout.As<ParameterBlockTypeLayout>())
+ if(auto parameterGroupLayout = typeLayout.As<ParameterGroupTypeLayout>())
{
- typeLayout = parameterBlockLayout->elementTypeLayout;
+ typeLayout = parameterGroupLayout->elementTypeLayout;
}
if(auto structLayout = typeLayout.As<StructTypeLayout>())
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index b09eae3ab..c50eaaae4 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -211,6 +211,7 @@
<ClCompile Include="diagnostics.cpp" />
<ClCompile Include="dxc-support.cpp" />
<ClCompile Include="emit.cpp" />
+ <ClCompile Include="ir-legalize-types.cpp" />
<ClCompile Include="ir.cpp" />
<ClCompile Include="lexer.cpp" />
<ClCompile Include="lookup.cpp" />
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index ef9b3c8e5..ce933db6e 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -71,6 +71,7 @@
<ClCompile Include="vm.cpp" />
<ClCompile Include="mangle.cpp" />
<ClCompile Include="dxc-support.cpp" />
+ <ClCompile Include="ir-legalize-types.cpp" />
</ItemGroup>
<ItemGroup>
<CustomBuild Include="core.meta.slang" />
diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp
index 9acdadad6..9025c545a 100644
--- a/source/slang/syntax.cpp
+++ b/source/slang/syntax.cpp
@@ -294,6 +294,17 @@ void Type::accept(IValVisitor* visitor, void* extra)
declRef)->As<PtrType>();
}
+ RefPtr<ArrayExpressionType> Session::getArrayType(
+ Type* elementType,
+ IntVal* elementCount)
+ {
+ RefPtr<ArrayExpressionType> arrayType = new ArrayExpressionType();
+ arrayType->baseType = elementType;
+ arrayType->ArrayLength = elementCount;
+ return arrayType;
+ }
+
+
RefPtr<GroupSharedType> Session::getGroupSharedType(RefPtr<Type> valueType)
{
RefPtr<GroupSharedType> groupSharedType = new GroupSharedType();
@@ -641,8 +652,9 @@ void Type::accept(IValVisitor* visitor, void* extra)
CASE(ConstantBuffer, ConstantBufferType)
CASE(TextureBuffer, TextureBufferType)
- CASE(GLSLInputParameterBlockType, GLSLInputParameterBlockType)
- CASE(GLSLOutputParameterBlockType, GLSLOutputParameterBlockType)
+ CASE(ParameterBlockType, ParameterBlockType)
+ CASE(GLSLInputParameterGroupType, GLSLInputParameterGroupType)
+ CASE(GLSLOutputParameterGroupType, GLSLOutputParameterGroupType)
CASE(GLSLShaderStorageBufferType, GLSLShaderStorageBufferType)
CASE(HLSLStructuredBufferType, HLSLStructuredBufferType)
diff --git a/source/slang/type-defs.h b/source/slang/type-defs.h
index 60f519c82..34c5b5936 100644
--- a/source/slang/type-defs.h
+++ b/source/slang/type-defs.h
@@ -278,24 +278,27 @@ SIMPLE_SYNTAX_CLASS(GLSLInputAttachmentType, DeclRefType)
// Base class for types used when desugaring parameter block
// declarations, includeing HLSL `cbuffer` or GLSL `uniform` blocks.
-SIMPLE_SYNTAX_CLASS(ParameterBlockType, PointerLikeType)
+SIMPLE_SYNTAX_CLASS(ParameterGroupType, PointerLikeType)
-SIMPLE_SYNTAX_CLASS(UniformParameterBlockType, ParameterBlockType)
-SIMPLE_SYNTAX_CLASS(VaryingParameterBlockType, ParameterBlockType)
+SIMPLE_SYNTAX_CLASS(UniformParameterGroupType, ParameterGroupType)
+SIMPLE_SYNTAX_CLASS(VaryingParameterGroupType, ParameterGroupType)
// type for HLSL `cbuffer` declarations, and `ConstantBuffer<T>`
// ALso used for GLSL `uniform` blocks.
-SIMPLE_SYNTAX_CLASS(ConstantBufferType, UniformParameterBlockType)
+SIMPLE_SYNTAX_CLASS(ConstantBufferType, UniformParameterGroupType)
// type for HLSL `tbuffer` declarations, and `TextureBuffer<T>`
-SIMPLE_SYNTAX_CLASS(TextureBufferType, UniformParameterBlockType)
+SIMPLE_SYNTAX_CLASS(TextureBufferType, UniformParameterGroupType)
// type for GLSL `in` and `out` blocks
-SIMPLE_SYNTAX_CLASS(GLSLInputParameterBlockType, VaryingParameterBlockType)
-SIMPLE_SYNTAX_CLASS(GLSLOutputParameterBlockType, VaryingParameterBlockType)
+SIMPLE_SYNTAX_CLASS(GLSLInputParameterGroupType, VaryingParameterGroupType)
+SIMPLE_SYNTAX_CLASS(GLSLOutputParameterGroupType, VaryingParameterGroupType)
// type for GLLSL `buffer` blocks
-SIMPLE_SYNTAX_CLASS(GLSLShaderStorageBufferType, UniformParameterBlockType)
+SIMPLE_SYNTAX_CLASS(GLSLShaderStorageBufferType, UniformParameterGroupType)
+
+// type for Slang `ParameterBlock<T>` type
+SIMPLE_SYNTAX_CLASS(ParameterBlockType, ParameterGroupType)
SYNTAX_CLASS(ArrayExpressionType, Type)
SYNTAX_FIELD(RefPtr<Type>, baseType)
diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp
index 822311f60..9f2aee922 100644
--- a/source/slang/type-layout.cpp
+++ b/source/slang/type-layout.cpp
@@ -427,6 +427,7 @@ struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl
virtual LayoutRulesImpl* getVaryingOutputRules() override;
virtual LayoutRulesImpl* getSpecializationConstantRules() override;
virtual LayoutRulesImpl* getShaderStorageBufferRules() override;
+ virtual LayoutRulesImpl* getParameterBlockRules() override;
virtual MatrixLayoutMode getDefaultMatrixLayoutMode() override
{
@@ -457,6 +458,7 @@ struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl
virtual LayoutRulesImpl* getVaryingOutputRules() override;
virtual LayoutRulesImpl* getSpecializationConstantRules() override;
virtual LayoutRulesImpl* getShaderStorageBufferRules() override;
+ virtual LayoutRulesImpl* getParameterBlockRules() override;
virtual MatrixLayoutMode getDefaultMatrixLayoutMode() override
{
@@ -519,6 +521,12 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules()
return &kStd140LayoutRulesImpl_;
}
+LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getParameterBlockRules()
+{
+ // TODO: actually pick something appropriate
+ return &kStd140LayoutRulesImpl_;
+}
+
LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getPushConstantBufferRules()
{
return &kGLSLPushConstantLayoutRulesImpl_;
@@ -556,6 +564,13 @@ LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getConstantBufferRules()
return &kHLSLConstantBufferLayoutRulesImpl_;
}
+LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getParameterBlockRules()
+{
+ // TODO: actually pick something appropriate...
+ return &kHLSLConstantBufferLayoutRulesImpl_;
+}
+
+
LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getPushConstantBufferRules()
{
return &kHLSLConstantBufferLayoutRulesImpl_;
@@ -685,8 +700,8 @@ SimpleLayoutInfo GetSimpleLayoutImpl(
return info;
}
-static SimpleLayoutInfo getParameterBlockLayoutInfo(
- RefPtr<ParameterBlockType> type,
+static SimpleLayoutInfo getParameterGroupLayoutInfo(
+ RefPtr<ParameterGroupType> type,
LayoutRulesImpl* rules)
{
if( type->As<ConstantBufferType>() )
@@ -701,14 +716,22 @@ static SimpleLayoutInfo getParameterBlockLayoutInfo(
{
return rules->GetObjectLayout(ShaderParameterKind::ShaderStorageBuffer);
}
+ else if (type->As<ParameterBlockType>())
+ {
+ // TODO(tfoley): Should a parameter block *always* consume at least
+ // one `set`/`space`, or should we hold back and just allocate this
+ // if it actually contains anything?
+ return SimpleLayoutInfo(LayoutResourceKind::ParameterBlock, 1);
+ }
+
// TODO: the vertex-input and fragment-output cases should
// only actually apply when we are at the appropriate stage in
// the pipeline...
- else if( type->As<GLSLInputParameterBlockType>() )
+ else if( type->As<GLSLInputParameterGroupType>() )
{
return SimpleLayoutInfo(LayoutResourceKind::VertexInput, 0);
}
- else if( type->As<GLSLOutputParameterBlockType>() )
+ else if( type->As<GLSLOutputParameterGroupType>() )
{
return SimpleLayoutInfo(LayoutResourceKind::FragmentOutput, 0);
}
@@ -736,19 +759,19 @@ RefPtr<TypeLayout> createTypeLayout(
Type* type,
SimpleLayoutInfo offset);
-RefPtr<ParameterBlockTypeLayout>
-createParameterBlockTypeLayout(
+RefPtr<ParameterGroupTypeLayout>
+createParameterGroupTypeLayout(
TypeLayoutContext* context,
- RefPtr<ParameterBlockType> parameterBlockType,
- SimpleLayoutInfo parameterBlockInfo,
+ RefPtr<ParameterGroupType> parameterGroupType,
+ SimpleLayoutInfo parameterGroupInfo,
RefPtr<TypeLayout> elementTypeLayout)
{
- auto parameterBlockRules = context->rules;
+ auto parameterGroupRules = context->rules;
- auto typeLayout = new ParameterBlockTypeLayout();
+ auto typeLayout = new ParameterGroupTypeLayout();
- typeLayout->type = parameterBlockType;
- typeLayout->rules = parameterBlockRules;
+ typeLayout->type = parameterGroupType;
+ typeLayout->rules = parameterGroupRules;
typeLayout->elementTypeLayout = elementTypeLayout;
@@ -757,7 +780,7 @@ createParameterBlockTypeLayout(
// originally (which should be a single binding "slot"
// and hence no uniform data).
//
- typeLayout->uniformAlignment = parameterBlockInfo.alignment;
+ typeLayout->uniformAlignment = parameterGroupInfo.alignment;
SLANG_RELEASE_ASSERT(!typeLayout->FindResourceInfo(LayoutResourceKind::Uniform));
SLANG_RELEASE_ASSERT(typeLayout->uniformAlignment == 1);
@@ -771,73 +794,96 @@ createParameterBlockTypeLayout(
// Make sure that we allocate resource usage for the
// parameter block itself.
- if( parameterBlockInfo.size )
+ if( parameterGroupInfo.size )
{
typeLayout->addResourceUsage(
- parameterBlockInfo.kind,
- parameterBlockInfo.size);
+ parameterGroupInfo.kind,
+ parameterGroupInfo.size);
}
- // Now, if the element type itself had any resources, then
- // we need to make these part of the layout for our block
+ // The layout rules for a constant buffer, vs. a "parameter block"
+ // are different, with respect to how they expose layout information
+ // for underlying resources.
//
- // TODO: re-consider this decision, since it creates
- // complications...
- for( auto elementResourceInfo : elementTypeLayout->resourceInfos )
+ // A parameter block should *not* expose the fine-grained resource
+ // prameters it contains, and should only expose a total number
+ // of `space`s or `set`s that it consumes.
+ if (parameterGroupInfo.kind == LayoutResourceKind::ParameterBlock)
{
- // Skip uniform data, since that is encapsualted behind the constant buffer
- if(elementResourceInfo.kind == LayoutResourceKind::Uniform)
- break;
+ // Iterate over element types, but *only* accumulate usage
+ // info for types that consume whole register sets/spaces.
+ for( auto elementResourceInfo : elementTypeLayout->resourceInfos )
+ {
+ if(elementResourceInfo.kind != LayoutResourceKind::ParameterBlock)
+ break;
+
+ typeLayout->addResourceUsage(elementResourceInfo);
+ }
+ }
+ else
+ {
+ // In the ordinary case (e.g., a constant buffer) then we need
+ // to make sure that any resources nested in the element type
+ // get counted against the container type, so that we can
+ // allocate registers to it directly.
+ for( auto elementResourceInfo : elementTypeLayout->resourceInfos )
+ {
+ // Skip uniform data, since that is encapsualted behind the constant buffer
+ if(elementResourceInfo.kind == LayoutResourceKind::Uniform)
+ break;
- typeLayout->addResourceUsage(elementResourceInfo);
+ typeLayout->addResourceUsage(elementResourceInfo);
+ }
}
+
+
return typeLayout;
}
-RefPtr<ParameterBlockTypeLayout>
-createParameterBlockTypeLayout(
- RefPtr<ParameterBlockType> parameterBlockType,
- LayoutRulesImpl* parameterBlockRules,
- SimpleLayoutInfo parameterBlockInfo,
+RefPtr<ParameterGroupTypeLayout>
+createParameterGroupTypeLayout(
+ RefPtr<ParameterGroupType> parameterGroupType,
+ LayoutRulesImpl* parameterGroupRules,
+ SimpleLayoutInfo parameterGroupInfo,
RefPtr<TypeLayout> elementTypeLayout)
{
TypeLayoutContext context;
- context.rules = parameterBlockRules;
- context.matrixLayoutMode = parameterBlockRules->getDefaultMatrixLayoutMode();
+ context.rules = parameterGroupRules;
+ context.matrixLayoutMode = parameterGroupRules->getDefaultMatrixLayoutMode();
- return createParameterBlockTypeLayout(
+ return createParameterGroupTypeLayout(
&context,
- parameterBlockType,
- parameterBlockInfo,
+ parameterGroupType,
+ parameterGroupInfo,
elementTypeLayout);
}
-RefPtr<ParameterBlockTypeLayout>
-createParameterBlockTypeLayout(
+RefPtr<ParameterGroupTypeLayout>
+createParameterGroupTypeLayout(
TypeLayoutContext* context,
- RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<ParameterGroupType> parameterGroupType,
RefPtr<Type> elementType,
LayoutRulesImpl* elementTypeRules)
{
- auto parameterBlockRules = context->rules;
+ auto parameterGroupRules = context->rules;
// First compute resource usage of the block itself.
// For now we assume that the layout of the block can
// always be described in a `SimpleLayoutInfo` (only
// a single resource kind consumed).
SimpleLayoutInfo info;
- if (parameterBlockType)
+ if (parameterGroupType)
{
- info = getParameterBlockLayoutInfo(
- parameterBlockType,
- parameterBlockRules);
+ info = getParameterGroupLayoutInfo(
+ parameterGroupType,
+ parameterGroupRules);
}
else
{
// If there is no concrete type, then it seems like we are
// being asked to compute layout for the global scope
- info = parameterBlockRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer);
+ info = parameterGroupRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer);
}
// Now compute a layout for the elements of the parameter block.
@@ -852,37 +898,41 @@ createParameterBlockTypeLayout(
elementType,
info);
- return createParameterBlockTypeLayout(
+ return createParameterGroupTypeLayout(
context,
- parameterBlockType,
+ parameterGroupType,
info,
elementTypeLayout);
}
LayoutRulesImpl* getParameterBufferElementTypeLayoutRules(
- RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<ParameterGroupType> parameterGroupType,
LayoutRulesImpl* rules)
{
- if( parameterBlockType->As<ConstantBufferType>() )
+ if( parameterGroupType->As<ConstantBufferType>() )
{
return rules->getLayoutRulesFamily()->getConstantBufferRules();
}
- else if( parameterBlockType->As<TextureBufferType>() )
+ else if( parameterGroupType->As<TextureBufferType>() )
{
return rules->getLayoutRulesFamily()->getTextureBufferRules();
}
- else if( parameterBlockType->As<GLSLInputParameterBlockType>() )
+ else if( parameterGroupType->As<GLSLInputParameterGroupType>() )
{
return rules->getLayoutRulesFamily()->getVaryingInputRules();
}
- else if( parameterBlockType->As<GLSLOutputParameterBlockType>() )
+ else if( parameterGroupType->As<GLSLOutputParameterGroupType>() )
{
return rules->getLayoutRulesFamily()->getVaryingOutputRules();
}
- else if( parameterBlockType->As<GLSLShaderStorageBufferType>() )
+ else if( parameterGroupType->As<GLSLShaderStorageBufferType>() )
{
return rules->getLayoutRulesFamily()->getShaderStorageBufferRules();
}
+ else if (parameterGroupType->As<ParameterBlockType>())
+ {
+ return rules->getLayoutRulesFamily()->getParameterBlockRules();
+ }
else
{
SLANG_UNEXPECTED("uhandled parameter block type");
@@ -890,23 +940,23 @@ LayoutRulesImpl* getParameterBufferElementTypeLayoutRules(
}
}
-RefPtr<ParameterBlockTypeLayout>
-createParameterBlockTypeLayout(
+RefPtr<ParameterGroupTypeLayout>
+createParameterGroupTypeLayout(
TypeLayoutContext* context,
- RefPtr<ParameterBlockType> parameterBlockType)
+ RefPtr<ParameterGroupType> parameterGroupType)
{
- auto parameterBlockRules = context->rules;
+ auto parameterGroupRules = context->rules;
// Determine the layout rules to use for the contents of the block
auto elementTypeRules = getParameterBufferElementTypeLayoutRules(
- parameterBlockType,
- parameterBlockRules);
+ parameterGroupType,
+ parameterGroupRules);
- auto elementType = parameterBlockType->elementType;
+ auto elementType = parameterGroupType->elementType;
- return createParameterBlockTypeLayout(
+ return createParameterGroupTypeLayout(
context,
- parameterBlockType,
+ parameterGroupType,
elementType,
elementTypeRules);
}
@@ -1015,14 +1065,14 @@ SimpleLayoutInfo GetLayoutImpl(
{
auto rules = context->rules;
- if (auto parameterBlockType = type->As<ParameterBlockType>())
+ if (auto parameterGroupType = type->As<ParameterGroupType>())
{
// If the user is just interested in uniform layout info,
// then this is easy: a `ConstantBuffer<T>` is really no
// different from a `Texture2D<U>` in terms of how it
// should be handled as a member of a container.
//
- auto info = getParameterBlockLayoutInfo(parameterBlockType, rules);
+ auto info = getParameterGroupLayoutInfo(parameterGroupType, rules);
// The more interesting case, though, is when the user
// is requesting us to actually create a `TypeLayout`,
@@ -1037,9 +1087,9 @@ SimpleLayoutInfo GetLayoutImpl(
//
if (outTypeLayout)
{
- *outTypeLayout = createParameterBlockTypeLayout(
+ *outTypeLayout = createParameterGroupTypeLayout(
context,
- parameterBlockType);
+ parameterGroupType);
}
return info;
diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h
index d078d9554..257bfa310 100644
--- a/source/slang/type-layout.h
+++ b/source/slang/type-layout.h
@@ -296,7 +296,7 @@ public:
};
// type layout for a variable that has a constant-buffer type
-class ParameterBlockTypeLayout : public TypeLayout
+class ParameterGroupTypeLayout : public TypeLayout
{
public:
RefPtr<TypeLayout> elementTypeLayout;
@@ -391,7 +391,7 @@ public:
// We store a layout for the declarations at the global
// scope. Note that this will *either* be a single
// `StructTypeLayout` with the fields stored directly,
- // or it will be a single `ParameterBlockTypeLayout`,
+ // or it will be a single `ParameterGroupTypeLayout`,
// where the global-scope fields are the members of
// that constant buffer.
//
@@ -450,6 +450,8 @@ enum class ShaderParameterKind
Image,
MutableImage,
+
+ RegisterSpace,
};
struct SimpleLayoutRulesImpl
@@ -550,8 +552,10 @@ struct LayoutRulesFamilyImpl
virtual LayoutRulesImpl* getTextureBufferRules() = 0;
virtual LayoutRulesImpl* getVaryingInputRules() = 0;
virtual LayoutRulesImpl* getVaryingOutputRules() = 0;
- virtual LayoutRulesImpl* getSpecializationConstantRules() = 0;
- virtual LayoutRulesImpl* getShaderStorageBufferRules() = 0;
+ virtual LayoutRulesImpl* getSpecializationConstantRules()= 0;
+ virtual LayoutRulesImpl* getShaderStorageBufferRules() = 0;
+ virtual LayoutRulesImpl* getParameterBlockRules() = 0;
+
virtual MatrixLayoutMode getDefaultMatrixLayoutMode() = 0;
};
@@ -571,30 +575,30 @@ RefPtr<TypeLayout> CreateTypeLayout(Type* type, LayoutRulesImpl* rules, SimpleLa
struct TypeLayoutContext;
// Create a type layout for a parameter block type.
-RefPtr<ParameterBlockTypeLayout>
-createParameterBlockTypeLayout(
+RefPtr<ParameterGroupTypeLayout>
+createParameterGroupTypeLayout(
TypeLayoutContext* context,
- RefPtr<ParameterBlockType> parameterBlockType);
+ RefPtr<ParameterGroupType> parameterGroupType);
-RefPtr<ParameterBlockTypeLayout>
-createParameterBlockTypeLayout(
+RefPtr<ParameterGroupTypeLayout>
+createParameterGroupTypeLayout(
TypeLayoutContext* context,
- RefPtr<ParameterBlockType> parameterBlockType,
+ RefPtr<ParameterGroupType> parameterGroupType,
RefPtr<Type> elementType,
LayoutRulesImpl* elementTypeRules);
-RefPtr<ParameterBlockTypeLayout>
-createParameterBlockTypeLayout(
+RefPtr<ParameterGroupTypeLayout>
+createParameterGroupTypeLayout(
TypeLayoutContext* context,
- RefPtr<ParameterBlockType> parameterBlockType,
- SimpleLayoutInfo parameterBlockInfo,
+ RefPtr<ParameterGroupType> parameterGroupType,
+ SimpleLayoutInfo parameterGroupInfo,
RefPtr<TypeLayout> elementTypeLayout);
-RefPtr<ParameterBlockTypeLayout>
-createParameterBlockTypeLayout(
- RefPtr<ParameterBlockType> parameterBlockType,
- LayoutRulesImpl* parameterBlockRules,
- SimpleLayoutInfo parameterBlockInfo,
+RefPtr<ParameterGroupTypeLayout>
+createParameterGroupTypeLayout(
+ RefPtr<ParameterGroupType> parameterGroupType,
+ LayoutRulesImpl* parameterGroupRules,
+ SimpleLayoutInfo parameterGroupInfo,
RefPtr<TypeLayout> elementTypeLayout);
// Create a type layout for a structured buffer type.
diff --git a/tests/bindings/multiple-parameter-blocks.slang b/tests/bindings/multiple-parameter-blocks.slang
new file mode 100644
index 000000000..0fdf7d429
--- /dev/null
+++ b/tests/bindings/multiple-parameter-blocks.slang
@@ -0,0 +1,48 @@
+//TEST:COMPARE_HLSL:-no-mangle -use-ir -target dxbc-assembly -profile ps_5_1 -entry main
+
+// Confirm that Slang `ParameterBlock<T>` generates
+// parameter bindings like we expect.
+
+
+float4 use(float4 val) { return val; };
+float4 use(Texture2D t, SamplerState s) { return t.Sample(s, 0.0); }
+
+#ifdef __SLANG__
+
+struct S
+{
+ Texture2D t;
+ Texture2D ta[4];
+ SamplerState s;
+};
+
+ParameterBlock<S> p;
+ParameterBlock<S> p1;
+
+float4 main(float v : V) : SV_Target
+{
+ return use(p.t, p.s)
+ + use(p.ta[int(v)], p.s)
+ + use(p1.t, p1.s)
+ + use(p1.ta[int(v)], p1.s);
+}
+
+#else
+
+Texture2D _S1 : register(t0, space0);
+Texture2D _S2[4] : register(t1, space0);
+SamplerState _S3 : register(s0, space0);
+
+Texture2D _S12 : register(t0, space1);
+Texture2D _S13[4] : register(t1, space1);
+SamplerState _S14 : register(s0, space1);
+
+float4 main(float v : V) : SV_Target
+{
+ return use(_S1, _S3)
+ + use(_S2[int(v)], _S3)
+ + use(_S12, _S14)
+ + use(_S13[int(v)], _S14);
+}
+
+#endif
diff --git a/tests/bindings/parameter-blocks.slang b/tests/bindings/parameter-blocks.slang
new file mode 100644
index 000000000..cd916f144
--- /dev/null
+++ b/tests/bindings/parameter-blocks.slang
@@ -0,0 +1,39 @@
+//TEST:COMPARE_HLSL:-no-mangle -use-ir -target dxbc-assembly -profile ps_5_1 -entry main
+
+// Confirm that Slang `ParameterBlock<T>` generates
+// parameter bindings like we expect.
+
+
+float4 use(float4 val) { return val; };
+float4 use(Texture2D t, SamplerState s) { return t.Sample(s, 0.0); }
+
+#ifdef __SLANG__
+
+struct S
+{
+ Texture2D t;
+ Texture2D ta[4];
+ SamplerState s;
+};
+
+ParameterBlock<S> p;
+
+float4 main(float v : V) : SV_Target
+{
+ return use(p.ta[int(v)], p.s)
+ + use(p.t, p.s);
+}
+
+#else
+
+Texture2D _S1 : register(t0, space0);
+Texture2D _S2[4] : register(t1, space0);
+SamplerState _S3 : register(s0, space0);
+
+float4 main(float v : V) : SV_Target
+{
+ return use(_S2[int(v)], _S3)
+ + use(_S1, _S3);
+}
+
+#endif
diff --git a/tests/bugs/gh-171.slang b/tests/bugs/gh-171.slang
index b37f77ce2..1df82501e 100644
--- a/tests/bugs/gh-171.slang
+++ b/tests/bugs/gh-171.slang
@@ -17,12 +17,12 @@ float4 main(float2 uv: UV) : SV_Target
#else
-Texture2D SLANG_parameterBlock_C_t : register(t0);
-SamplerState SLANG_parameterBlock_C_s : register(s0);
+Texture2D SLANG_parameterGroup_C_t : register(t0);
+SamplerState SLANG_parameterGroup_C_s : register(s0);
float4 main(float2 uv: UV) : SV_Target
{
- return SLANG_parameterBlock_C_t.Sample(SLANG_parameterBlock_C_s, uv);
+ return SLANG_parameterGroup_C_t.Sample(SLANG_parameterGroup_C_s, uv);
}
#endif
diff --git a/tests/bugs/gh-172.slang b/tests/bugs/gh-172.slang
index f898b5f4b..f959c7ac6 100644
--- a/tests/bugs/gh-172.slang
+++ b/tests/bugs/gh-172.slang
@@ -26,14 +26,14 @@ cbuffer C : register(b0)
float2 uv;
};
-Texture2D SLANG_parameterBlock_C_t0 : register(t0);
-Texture2D SLANG_parameterBlock_C_t1 : register(t1);
-SamplerState SLANG_parameterBlock_C_s : register(s0);
+Texture2D SLANG_parameterGroup_C_t0 : register(t0);
+Texture2D SLANG_parameterGroup_C_t1 : register(t1);
+SamplerState SLANG_parameterGroup_C_s : register(s0);
float4 main() : SV_Target
{
- return SLANG_parameterBlock_C_t0.Sample(SLANG_parameterBlock_C_s, uv)
- + SLANG_parameterBlock_C_t1.Sample(SLANG_parameterBlock_C_s, uv);
+ return SLANG_parameterGroup_C_t0.Sample(SLANG_parameterGroup_C_s, uv)
+ + SLANG_parameterGroup_C_t1.Sample(SLANG_parameterGroup_C_s, uv);
}
#endif
diff --git a/tests/rewriter/resources-in-structs.glsl b/tests/rewriter/resources-in-structs.glsl
index 206e4a8d8..8df64f244 100644
--- a/tests/rewriter/resources-in-structs.glsl
+++ b/tests/rewriter/resources-in-structs.glsl
@@ -42,10 +42,10 @@ uniform U
};
layout(binding = 1)
-uniform texture2D SLANG_parameterBlock_U_m_t;
+uniform texture2D SLANG_parameterGroup_U_m_t;
layout(binding = 2)
-uniform sampler SLANG_parameterBlock_U_m_s;
+uniform sampler SLANG_parameterGroup_U_m_s;
layout(location = 0)
in vec2 uv;
@@ -58,8 +58,8 @@ void main()
Material SLANG_tmp_0 = m;
color = evaluateMaterial(
SLANG_tmp_0,
- SLANG_parameterBlock_U_m_t,
- SLANG_parameterBlock_U_m_s, uv);
+ SLANG_parameterGroup_U_m_t,
+ SLANG_parameterGroup_U_m_s, uv);
}
#endif
diff --git a/tests/rewriter/type-splitting.hlsl b/tests/rewriter/type-splitting.hlsl
index c8d69f5fd..b3cad1ce0 100644
--- a/tests/rewriter/type-splitting.hlsl
+++ b/tests/rewriter/type-splitting.hlsl
@@ -47,12 +47,12 @@ cbuffer C
Foo foo;
}
-Texture2D SLANG_parameterBlock_C_foo_t;
-SamplerState SLANG_parameterBlock_C_foo_s;
+Texture2D SLANG_parameterGroup_C_foo_t;
+SamplerState SLANG_parameterGroup_C_foo_s;
float4 main() : SV_Target
{
- return SLANG_parameterBlock_C_foo_t.Sample(SLANG_parameterBlock_C_foo_s, foo.u);
+ return SLANG_parameterGroup_C_foo_t.Sample(SLANG_parameterGroup_C_foo_s, foo.u);
}
#endif