diff --git a/slang.h b/slang.h index f154421e66..d4731d32e7 100644 --- a/slang.h +++ b/slang.h @@ -522,6 +522,7 @@ extern "C" SLANG_TYPE_KIND_SAMPLER_STATE, SLANG_TYPE_KIND_TEXTURE_BUFFER, SLANG_TYPE_KIND_SHADER_STORAGE_BUFFER, + SLANG_TYPE_KIND_PARAMETER_BLOCK, SLANG_TYPE_KIND_COUNT, }; @@ -759,6 +760,7 @@ namespace slang SamplerState = SLANG_TYPE_KIND_SAMPLER_STATE, TextureBuffer = SLANG_TYPE_KIND_TEXTURE_BUFFER, ShaderStorageBuffer = SLANG_TYPE_KIND_SHADER_STORAGE_BUFFER, + ParameterBlock = SLANG_TYPE_KIND_PARAMETER_BLOCK, }; enum ScalarType : SlangScalarType diff --git a/source/slang/ast-legalize.cpp b/source/slang/ast-legalize.cpp index eaceecc002..0c9d6154ea 100644 --- a/source/slang/ast-legalize.cpp +++ b/source/slang/ast-legalize.cpp @@ -797,24 +797,9 @@ struct LoweringVisitor lowerTypeEx(type->valueType)); } - RefPtr visitParameterBlockType(ParameterBlockType* type) - { - // TODO: When doing AST-to-AST lowering, we want to lower - // a `ParameterBlock` just like a `ConstantBuffer`. - // - // HACK: for now we will try to simply lower the type - // directly to its stated element type, and see how - // that works. - - return lowerTypeEx(type->getElementType()); -// return getSession()->getConstantBufferType( -// lowerType(type->getElementType()); - } - RefPtr transformSyntaxField(Type* type) { - // TODO: how to handle this... - return type; + return lowerAndLegalizeSimpleType(type); } RefPtr visitIRProxyVal(IRProxyVal* val) @@ -1807,15 +1792,73 @@ struct LoweringVisitor static LegalExpr maybeReifyTuple( LegalExpr legalExpr, - LegalType expectedType) + LegalType expectedLegalType) { - if (expectedType.flavor != LegalType::Flavor::simple) + if (expectedLegalType.flavor != LegalType::Flavor::simple) return legalExpr; + RefPtr expectedType = expectedLegalType.getSimple(); + if(auto errorType = expectedType->As()) + { + return legalExpr; + } + if (legalExpr.getFlavor() == LegalExpr::Flavor::simple) return legalExpr; - return LegalExpr(reifyTuple(legalExpr, expectedType.getSimple())); + return LegalExpr(reifyTuple(legalExpr, expectedLegalType.getSimple())); + } + + // This function exists to work around cases where `addArgs` gets called + // and the structure of the type expected in context (the legalized parameter + // type) differs from the structure of the actual argument. + // + // This function ignores type information and just adds things based on + // what is present in the actual expression. + void addArgsWorkaround( + ExprWithArgsBase* callExpr, + LegalExpr argExpr) + { + + switch (argExpr.getFlavor()) + { + case LegalExpr::Flavor::none: + break; + + case LegalExpr::Flavor::simple: + addArg(callExpr, argExpr.getSimple()); + break; + + case LegalExpr::Flavor::tuple: + { + auto aa = argExpr.getTuple(); + auto elementCount = aa->elements.Count(); + for (UInt ee = 0; ee < elementCount; ++ee) + { + addArgsWorkaround(callExpr, aa->elements[ee].expr); + } + } + break; + + case LegalExpr::Flavor::pair: + { + auto aa = argExpr.getPair(); + addArgsWorkaround(callExpr, aa->ordinary); + addArgsWorkaround(callExpr, aa->special); + } + break; + + case LegalExpr::Flavor::implicitDeref: + { + auto aa = argExpr.getImplicitDeref(); + addArgsWorkaround(callExpr, aa->valueExpr); + } + break; + + default: + SLANG_UNEXPECTED("unhandled case"); + break; + } } void addArgs( @@ -1827,7 +1870,10 @@ struct LoweringVisitor if (argExpr.getFlavor() != argType.flavor) { - SLANG_UNEXPECTED("expression and type do not match"); + // A mismatch may also arise if we are in the `-no-checking` mode, + // so that we are making a call that didn't type-check. + addArgsWorkaround(callExpr, argExpr); + return; } switch (argExpr.getFlavor()) @@ -1900,6 +1946,29 @@ struct LoweringVisitor return LegalExpr(lowerCallExpr(loweredExpr, expr)); } + LegalExpr visitHiddenImplicitCastExpr( + HiddenImplicitCastExpr* expr) + { + LegalExpr legalArg = legalizeExpr(expr->Arguments[0]); + if(legalArg.getFlavor() == LegalExpr::Flavor::simple) + { + InvokeExpr* loweredExpr = (InvokeExpr*) expr->getClass().createInstance(); + lowerExprCommon(loweredExpr, expr); + loweredExpr->FunctionExpr = legalizeSimpleExpr(expr->FunctionExpr); + addArg(loweredExpr, legalArg.getSimple()); + return LegalExpr(loweredExpr); + } + else + { + // If we hit this case, then there seems to have been a type-checking + // error around a type that needed to be desugared. We want to use + // the original expression rather than hide it behind a cast, because + // it might need to be unpacked into multiple arguments for a call, etc. + // + return legalArg; + } + } + LegalExpr visitSelectExpr( SelectExpr* expr) { @@ -2476,6 +2545,7 @@ struct LoweringVisitor RefPtr type) { auto typeType = new TypeType(); + typeType->setSession(getSession()); typeType->type = type; auto result = new SharedTypeExpr(); @@ -3320,7 +3390,6 @@ struct LoweringVisitor typeLayout, legalInit, legalTypeExpr); - } break; @@ -3329,8 +3398,17 @@ struct LoweringVisitor auto implicitDerefType = legalType.getImplicitDeref(); auto valueType = implicitDerefType->valueType; - auto valueTypeLayout = getDerefTypeLayout(typeLayout); - SLANG_ASSERT(valueTypeLayout || !typeLayout); + + // Don't apply dereferencing to the type layout, because + // other steps will also implicitly remove wrappers (like + // parameter groups) and this could mess up the final + // type layout for a variable. + // + // Instead, any other "unwrapping" that needs to occur + // when declaring variables should be handled in the + // case for the specific type (e.g., when extracting + // fields for a tuple, we should auto-dereference). + auto valueTypeLayout = typeLayout; auto valueInit = deref(legalInit); LegalExpr valueExpr = declareVars( diff --git a/source/slang/check.cpp b/source/slang/check.cpp index c1893423a7..4840ae30df 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -1005,6 +1005,40 @@ namespace Slang } } + + // Are we converting from a parameter group type to its element type? + if(auto fromParameterGroupType = fromType->As()) + { + auto fromElementType = fromParameterGroupType->getElementType(); + + // If we have, e.g., `ConstantBuffer` and we want to convert + // to `B`, where conversion from `A` to `B` is possible, then + // we will do so here. + + ConversionCost subCost = 0; + if(CanCoerce(toType, fromElementType, &subCost)) + { + if(outCost) + *outCost = subCost + kConversionCost_ImplicitDereference; + + if(outToExpr) + { + auto derefExpr = new DerefExpr(); + derefExpr->base = fromExpr; + derefExpr->type = QualType(fromElementType); + + return TryCoerceImpl( + toType, + outToExpr, + fromElementType, + derefExpr, + nullptr); + } + return true; + } + } + + // Look for an initializer/constructor declaration in the target type, // which is marked as usable for implicit conversion, and which takes // the source type as an argument. @@ -1171,6 +1205,7 @@ namespace Slang RefPtr castExpr = createImplicitCastExpr(); auto typeType = new TypeType(); + typeType->setSession(getSession()); typeType->type = toType; auto typeExpr = new SharedTypeExpr(); diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index b9aa3c0276..09d209f6e4 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -96,7 +96,9 @@ struct SharedEmitContext ExtensionUsageTracker extensionUsageTracker; + UInt uniqueIDCounter = 1; Dictionary mapIRValueToID; + Dictionary mapDeclToID; HashSet irDeclsVisited; @@ -503,6 +505,23 @@ struct EmitVisitor emitName(name, SourceLoc()); } + void emitName( + Decl* decl, + SourceLoc const& loc) + { + if(auto name = decl->getName()) + emitName(name, loc); + + Emit("_S"); + Emit(getID(decl)); + } + + void emitName( + Decl* decl) + { + emitName(decl, SourceLoc()); + } + void Emit(IntegerLiteralValue value) { char buffer[32]; @@ -3571,31 +3590,95 @@ struct EmitVisitor auto offsetResource = rr; - if(kind != LayoutResourceKind::Uniform) + if(layout + && kind != LayoutResourceKind::Uniform) { // Add the base index from the cbuffer into the index of the field // // TODO(tfoley): consider maybe not doing this, since it actually // complicates logic around constant buffers... - // If the member of the cbuffer uses a resource, it had better - // appear as part of the cubffer layout as well. + // If the member of the cbuffer uses a resource, we would typically + // expect to see that the `cbuffer` itself shows up as using that + // resource too. auto cbufferResource = layout->FindResourceInfo(kind); - SLANG_RELEASE_ASSERT(cbufferResource); - - offsetResource.index += cbufferResource->index; - offsetResource.space += cbufferResource->space; + if(cbufferResource) + { + offsetResource.index += cbufferResource->index; + offsetResource.space += cbufferResource->space; + } } emitHLSLRegisterSemantic(offsetResource, "packoffset"); } } + void emitHLSLParameterBlockDecl( + RefPtr varDecl, + RefPtr parameterBlockType, + RefPtr layout) + { + Emit("cbuffer "); + + emitName(varDecl); + + // We expect to always have layout information + layout = maybeFetchLayout(varDecl, layout); + SLANG_RELEASE_ASSERT(layout); + + // We expect the layout to be for a parameter group type... + RefPtr bufferLayout = layout->typeLayout.As(); + SLANG_RELEASE_ASSERT(bufferLayout); + + EmitSemantics(varDecl, kESemanticMask_None); + + auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer); + SLANG_RELEASE_ASSERT(info); + emitHLSLRegisterSemantic(*info); + + Emit("\n{\n"); + + // The user wrote this declaration as, e.g.: + // + // ParameterBlock gFoo; + // + // and we are desugaring it into something like: + // + // cbuffer anon0 { Foo gFoo; } + // + + RefPtr elementType = parameterBlockType->elementType; + RefPtr elementTypeLayout = bufferLayout->elementTypeLayout; + + EmitType(elementType, varDecl->getName()); + + // The layout for the field ends up coming from the layout + // for the parameter block as a whole. + emitHLSLParameterGroupFieldLayoutSemantics(nullptr, layout); + + Emit(";\n"); + Emit("}\n"); + } + void emitHLSLParameterGroupDecl( RefPtr varDecl, RefPtr parameterGroupType, RefPtr layout) { + if( auto parameterBlockType = parameterGroupType->As()) + { + emitHLSLParameterBlockDecl(varDecl, parameterBlockType, layout); + return; + } + if( auto textureBufferType = parameterGroupType->As() ) + { + Emit("tbuffer "); + } + else + { + Emit("cbuffer "); + } + // The data type that describes where stuff in the constant buffer should go RefPtr dataType = parameterGroupType->elementType; @@ -3610,20 +3693,16 @@ struct EmitVisitor RefPtr structTypeLayout = bufferLayout->elementTypeLayout.As(); SLANG_RELEASE_ASSERT(structTypeLayout); - if( auto constantBufferType = parameterGroupType->As() ) - { - Emit("cbuffer "); - } - else if( auto textureBufferType = parameterGroupType->As() ) - { - Emit("tbuffer "); - } + Emit(" "); if( auto reflectionNameModifier = varDecl->FindModifier() ) { - Emit(" "); emitName(reflectionNameModifier->nameAndLoc); } + else + { + emitName(varDecl->nameAndLoc); + } EmitSemantics(varDecl, kESemanticMask_None); @@ -4144,6 +4223,28 @@ emitDeclImpl(decl, nullptr); } } + // Utility code for generating unique IDs as needed + // during the emit process (e.g., for declarations + // that didn't origianlly have names, but now need to). + + UInt allocateUniqueID() + { + return context->shared->uniqueIDCounter++; + } + + UInt getID(Decl* decl) + { + auto& mapDeclToID = context->shared->mapDeclToID; + + UInt id = 0; + if(mapDeclToID.TryGetValue(decl, id)) + return id; + + id = allocateUniqueID(); + mapDeclToID.Add(decl, id); + return id; + } + // IR-level emit logc UInt getID(IRValue* value) @@ -4154,7 +4255,7 @@ emitDeclImpl(decl, nullptr); if (mapIRValueToID.TryGetValue(value, id)) return id; - id = mapIRValueToID.Count() + 1; + id = allocateUniqueID(); mapIRValueToID.Add(value, id); return id; } @@ -6274,11 +6375,53 @@ emitDeclImpl(decl, nullptr); } } + void emitHLSLParameterBlock( + EmitContext* ctx, + IRGlobalVar* varDecl, + ParameterBlockType* type) + { + emit("cbuffer "); + + // Generate a dummy name for the block + emit("_S"); + Emit(ctx->shared->uniqueIDCounter++); + + auto layout = getVarLayout(ctx, varDecl); + assert(layout); + + auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer); + SLANG_RELEASE_ASSERT(info); + emitHLSLRegisterSemantic(*info); + + emit("\n{\n"); + + auto elementType = type->getElementType(); + + auto typeLayout = layout->typeLayout; + if( auto parameterGroupTypeLayout = typeLayout.As() ) + { + typeLayout = parameterGroupTypeLayout->elementTypeLayout; + } + + emitIRType(ctx, elementType, getIRName(varDecl)); + + emitHLSLParameterGroupFieldLayoutSemantics(nullptr, layout); + emit(";\n"); + + emit("}\n"); + } + void emitHLSLParameterGroup( EmitContext* ctx, IRGlobalVar* varDecl, UniformParameterGroupType* type) { + if(auto parameterBlockType = type->As()) + { + emitHLSLParameterBlock(ctx, varDecl, parameterBlockType); + return; + } + emit("cbuffer "); emit(getIRName(varDecl)); diff --git a/source/slang/ir-legalize-types.cpp b/source/slang/ir-legalize-types.cpp index 6e909106d5..fedec4f87f 100644 --- a/source/slang/ir-legalize-types.cpp +++ b/source/slang/ir-legalize-types.cpp @@ -828,7 +828,7 @@ static LegalVal declareSimpleVar( { auto globalVar = builder->createGlobalVar(type); globalVar->removeFromParent(); - globalVar->insertBefore(context->insertBeforeGlobal); + globalVar->insertBefore(context->insertBeforeGlobal, builder->getModule()); irVar = globalVar; legalVarVal = LegalVal::simple(irVar); diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index d35ffe2d1a..d4d2f0f512 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -956,16 +956,22 @@ namespace Slang IRInst* IRBuilder::emitLoad( IRValue* ptr) { - auto ptrType = ptr->getType()->As(); - if( !ptrType ) + RefPtr valueType; + if(auto ptrType = ptr->getType()->As()) + { + valueType = ptrType->getValueType(); + } + else if(auto ptrLikeType = ptr->getType()->As()) + { + valueType = ptrLikeType->getElementType(); + } + else { // Bad! SLANG_ASSERT(ptrType); return nullptr; } - auto valueType = ptrType->getValueType(); - auto inst = createInst( this, kIROp_Load, diff --git a/source/slang/legalize-types.cpp b/source/slang/legalize-types.cpp index 510b9acd3b..fb9c70c979 100644 --- a/source/slang/legalize-types.cpp +++ b/source/slang/legalize-types.cpp @@ -194,9 +194,23 @@ struct TupleTypeBuilder { // The field's type had both special and non-special parts auto pairType = legalLeafType.getPair(); - ordinaryType = pairType->ordinaryType; - specialType = pairType->specialType; - elementPairInfo = pairType->pairInfo; + + // If things originally started as a resource type, then + // we want to externalize all the fields that arose, even + // if there is (nominally) ordinary data. + // + // This is because the "ordinary" side of the legalization + // of `ConstantBuffer` will still be a resource type. + if(isResource) + { + specialType = legalFieldType; + } + else + { + ordinaryType = pairType->ordinaryType; + specialType = pairType->specialType; + elementPairInfo = pairType->pairInfo; + } } break; @@ -777,23 +791,21 @@ LegalType legalizeType( TypeLegalizationContext* context, Type* type) { - if (auto parameterBlockType = type->As()) - { - // We basically legalize the `ParameterBlock` 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 (auto uniformBufferType = type->As()) + if (auto uniformBufferType = type->As()) { - // We have a `ConstantBuffer` or `TextureBuffer` or - // other pointer-like type that represents uniform parameters. - // We need to pull any resource-type fields out of it, but - // leave the non-resource fields where they are. + // We have one of: + // + // ConstantBuffer + // TextureBuffer + // ParameterBlock + // + // or some other pointer-like type that represents uniform + // parameters. We need to pull any resource-type fields out + // of it, but leave non-resource fields where they are. + // + // As a special case, if the type contains *no* uniform data, + // we'll want to completely eliminate the uniform/ordinary + // part. // Legalize the element type to see what we are working with. auto legalElementType = legalizeType(context, @@ -973,11 +985,23 @@ RefPtr getFieldLayout( if (!typeLayout) return nullptr; - while(auto arrayTypeLayout = dynamic_cast(typeLayout)) + for(;;) { - typeLayout = arrayTypeLayout->elementTypeLayout; + if(auto arrayTypeLayout = dynamic_cast(typeLayout)) + { + typeLayout = arrayTypeLayout->elementTypeLayout; + } + else if(auto parameterGroupTypeLayotu = dynamic_cast(typeLayout)) + { + typeLayout = parameterGroupTypeLayotu->elementTypeLayout; + } + else + { + break; + } } + if (auto structTypeLayout = dynamic_cast(typeLayout)) { RefPtr fieldLayout; diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp index a1ea3ff72c..5962bce96d 100644 --- a/source/slang/reflection.cpp +++ b/source/slang/reflection.cpp @@ -102,6 +102,10 @@ SLANG_API SlangTypeKind spReflectionType_GetKind(SlangReflectionType* inType) { return SLANG_TYPE_KIND_MATRIX; } + else if (auto parameterBlockType = type->As()) + { + return SLANG_TYPE_KIND_PARAMETER_BLOCK; + } else if (auto constantBufferType = type->As()) { return SLANG_TYPE_KIND_CONSTANT_BUFFER; diff --git a/source/slang/syntax.h b/source/slang/syntax.h index e6a010a4a4..749c5ae322 100644 --- a/source/slang/syntax.h +++ b/source/slang/syntax.h @@ -38,6 +38,11 @@ namespace Slang // No conversion at all kConversionCost_None = 0, + // Conversion from a buffer to the type it carries needs to add a minimal + // extra cost, just so we can distinguish an overload on `ConstantBuffer` + // from one on `Foo` + kConversionCost_ImplicitDereference = 10, + // Conversions based on explicit sub-typing relationships are the cheapest // // TODO(tfoley): We will eventually need a discipline for ranking diff --git a/source/slang/type-defs.h b/source/slang/type-defs.h index da6e27d174..d0b2ebac12 100644 --- a/source/slang/type-defs.h +++ b/source/slang/type-defs.h @@ -298,7 +298,7 @@ SIMPLE_SYNTAX_CLASS(GLSLOutputParameterGroupType, VaryingParameterGroupType) SIMPLE_SYNTAX_CLASS(GLSLShaderStorageBufferType, UniformParameterGroupType) // type for Slang `ParameterBlock` type -SIMPLE_SYNTAX_CLASS(ParameterBlockType, ParameterGroupType) +SIMPLE_SYNTAX_CLASS(ParameterBlockType, UniformParameterGroupType) SYNTAX_CLASS(ArrayExpressionType, Type) SYNTAX_FIELD(RefPtr, baseType) diff --git a/tests/compute/rewriter-parameter-block-complex.hlsl b/tests/compute/rewriter-parameter-block-complex.hlsl new file mode 100644 index 0000000000..4dc312f95c --- /dev/null +++ b/tests/compute/rewriter-parameter-block-complex.hlsl @@ -0,0 +1,32 @@ +//TEST(compute):HLSL_COMPUTE:-xslang -no-checking +//TEST(compute):COMPARE_COMPUTE:-xslang -use-ir + +// Doesn't work with IR yet. +//DISABLED_TEST(compute):HLSL_COMPUTE:-xslang -no-checking -xslang -use-ir + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out + +//TEST_INPUT:cbuffer(data=[256]):dxbinding(0),glbinding(0) +//TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):dxbinding(1),glbinding(1) + +//TEST_INPUT:cbuffer(data=[4096]):dxbinding(1),glbinding(1) +//TEST_INPUT:ubuffer(data=[16 32 48 64], stride=4):dxbinding(2),glbinding(2) + +// Test that we can declare a `ParameterBlock<...>` type as a shader +// parameter (potentially nested inside a `cbuffer`) and use it in +// shader code processed by the "rewriter" + +import rewriter_parameter_block_complex; + +RWStructuredBuffer outputBuffer : register(u0); + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + int inVal = tid; + + int outVal = test(gA, inVal) + test(gB, inVal); + + outputBuffer[tid] = outVal; +} \ No newline at end of file diff --git a/tests/compute/rewriter-parameter-block-complex.hlsl.1.expected.txt b/tests/compute/rewriter-parameter-block-complex.hlsl.1.expected.txt new file mode 100644 index 0000000000..883f727163 --- /dev/null +++ b/tests/compute/rewriter-parameter-block-complex.hlsl.1.expected.txt @@ -0,0 +1,4 @@ +1110 +1121 +1132 +1143 diff --git a/tests/compute/rewriter-parameter-block-complex.hlsl.expected.txt b/tests/compute/rewriter-parameter-block-complex.hlsl.expected.txt new file mode 100644 index 0000000000..883f727163 --- /dev/null +++ b/tests/compute/rewriter-parameter-block-complex.hlsl.expected.txt @@ -0,0 +1,4 @@ +1110 +1121 +1132 +1143 diff --git a/tests/compute/rewriter-parameter-block-complex.slang b/tests/compute/rewriter-parameter-block-complex.slang new file mode 100644 index 0000000000..826dd5047d --- /dev/null +++ b/tests/compute/rewriter-parameter-block-complex.slang @@ -0,0 +1,21 @@ +// rewriter-parameter-block.slang +//TEST_IGNORE_FILE: + +// A type that mixes uniform and resource fields +struct Data +{ + int val; + RWStructuredBuffer buf; +}; + +// A function that uses that type +int test(Data data, int val) +{ + return data.val + data.buf[val]; +} + +cbuffer C +{ + ParameterBlock gA; + Data gB; +}; diff --git a/tests/compute/rewriter-parameter-block.hlsl b/tests/compute/rewriter-parameter-block.hlsl new file mode 100644 index 0000000000..0cc06cc101 --- /dev/null +++ b/tests/compute/rewriter-parameter-block.hlsl @@ -0,0 +1,34 @@ +//TEST(compute):HLSL_COMPUTE:-xslang -no-checking +//TEST(compute):COMPARE_COMPUTE:-xslang -use-ir + +// Doesn't work with rewriter + IR yet. +//DISABLED_TEST(compute):HLSL_COMPUTE:-xslang -no-checking -xslang -use-ir + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out + +//TEST_INPUT:cbuffer(data=[256]):dxbinding(0),glbinding(0) +//TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):dxbinding(1),glbinding(1) + +//TEST_INPUT:cbuffer(data=[65536]):dxbinding(1),glbinding(1) + +//TEST_INPUT:cbuffer(data=[4096]):dxbinding(2),glbinding(2) +//TEST_INPUT:ubuffer(data=[16 32 48 64], stride=4):dxbinding(2),glbinding(2) + +// Test that we can declare a `ParameterBlock<...>` type as a shader +// parameter (potentially nested inside a `cbuffer`) and use it in +// shader code processed by the "rewriter" + +import rewriter_parameter_block; + +RWStructuredBuffer outputBuffer : register(u0); + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + int inVal = tid; + + int outVal = test(gA, inVal) + test(gB, inVal) + extra; + + outputBuffer[tid] = outVal; +} \ No newline at end of file diff --git a/tests/compute/rewriter-parameter-block.hlsl.1.expected.txt b/tests/compute/rewriter-parameter-block.hlsl.1.expected.txt new file mode 100644 index 0000000000..479b9e0dac --- /dev/null +++ b/tests/compute/rewriter-parameter-block.hlsl.1.expected.txt @@ -0,0 +1,4 @@ +11110 +11121 +11132 +11143 diff --git a/tests/compute/rewriter-parameter-block.hlsl.expected.txt b/tests/compute/rewriter-parameter-block.hlsl.expected.txt new file mode 100644 index 0000000000..479b9e0dac --- /dev/null +++ b/tests/compute/rewriter-parameter-block.hlsl.expected.txt @@ -0,0 +1,4 @@ +11110 +11121 +11132 +11143 diff --git a/tests/compute/rewriter-parameter-block.slang b/tests/compute/rewriter-parameter-block.slang new file mode 100644 index 0000000000..830f22f4e7 --- /dev/null +++ b/tests/compute/rewriter-parameter-block.slang @@ -0,0 +1,25 @@ +// rewriter-parameter-block.slang +//TEST_IGNORE_FILE: + +// A type that mixes uniform and resource fields +struct Data +{ + int val; + RWStructuredBuffer buf; +}; + +// A function that uses that type +int test(Data data, int val) +{ + return data.val + data.buf[val]; +} + +// A global-scope parameter block of the mixed type +ParameterBlock gA; + +// A constant buffer declaration containing the mixed type +cbuffer C +{ + int extra; + ParameterBlock gB; +}; diff --git a/tools/slang-reflection-test/main.cpp b/tools/slang-reflection-test/main.cpp index 219acbc761..057bcd1279 100644 --- a/tools/slang-reflection-test/main.cpp +++ b/tools/slang-reflection-test/main.cpp @@ -356,6 +356,15 @@ static void emitReflectionTypeInfoJSON( type->getElementType()); break; + case slang::TypeReflection::Kind::ParameterBlock: + write(writer, "\"kind\": \"parameterBlock\""); + write(writer, ",\n"); + write(writer, "\"elementType\": "); + emitReflectionTypeJSON( + writer, + type->getElementType()); + break; + case slang::TypeReflection::Kind::TextureBuffer: write(writer, "\"kind\": \"textureBuffer\""); write(writer, ",\n"); @@ -515,6 +524,15 @@ static void emitReflectionTypeLayoutInfoJSON( typeLayout->getElementTypeLayout()); break; + case slang::TypeReflection::Kind::ParameterBlock: + write(writer, "\"kind\": \"parameterBlock\""); + write(writer, ",\n"); + write(writer, "\"elementType\": "); + emitReflectionTypeLayoutJSON( + writer, + typeLayout->getElementTypeLayout()); + break; + case slang::TypeReflection::Kind::TextureBuffer: write(writer, "\"kind\": \"textureBuffer\""); write(writer, ",\n");