From d9d0b4f03277027690a909a76b78d1622ac13498 Mon Sep 17 00:00:00 2001 From: Yong He Date: Wed, 15 Jan 2025 15:49:29 -0800 Subject: [PATCH 01/23] Fix optix varying legalization. (#6089) * Fix optix varying legalization. * Add test. --- source/slang/slang-ir-legalize-varying-params.cpp | 2 ++ tests/cuda/optix-hit-attributes.slang | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/cuda/optix-hit-attributes.slang diff --git a/source/slang/slang-ir-legalize-varying-params.cpp b/source/slang/slang-ir-legalize-varying-params.cpp index 5a18b533ab..a98290545c 100644 --- a/source/slang/slang-ir-legalize-varying-params.cpp +++ b/source/slang/slang-ir-legalize-varying-params.cpp @@ -1053,6 +1053,8 @@ struct CUDAEntryPointVaryingParamLegalizeContext : EntryPointVaryingParamLegaliz IRType* typeToFetch, IRBuilder* builder) { + if (auto ptrValType = tryGetPointedToType(builder, typeToFetch)) + typeToFetch = ptrValType; if (auto structType = as(typeToFetch)) { List fieldVals; diff --git a/tests/cuda/optix-hit-attributes.slang b/tests/cuda/optix-hit-attributes.slang new file mode 100644 index 0000000000..3474018930 --- /dev/null +++ b/tests/cuda/optix-hit-attributes.slang @@ -0,0 +1,15 @@ +//TEST:SIMPLE(filecheck=CHECK): -target cuda +//CHECK: __global__ void __closesthit__closestHitShaderA +struct RayPayload +{ + float4 color; +}; + +[shader("closesthit")] +void closestHitShaderA(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attr) +{ + uint primitiveIndex = PrimitiveIndex(); + float4 color = float4(0, 0, 0, 1); + color[primitiveIndex] = 1; + payload.color = color; +} From 9b977e59cf786bbb000b3b868b126c2b9a17d3f3 Mon Sep 17 00:00:00 2001 From: Darren Wihandi <65404740+fairywreath@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:50:56 -0500 Subject: [PATCH 02/23] Reuse code for Metal and WGSL entry point legalization (#6063) * Refactor to reuse common for metal and wgsl entry point legalization * refactor system val work item * refactor simplify user names * clean up fix semantic field of struct * improve code layout * split wgsl/metal to seperate classes and cleanup * remove extra includes * remove dead code comments * minor cleanup * squash merge from master and resolve conflict * apply metal spec const thread count changes * Revert "apply metal spec const thread count changes" This reverts commit c42d707fd25ee0328598650d3235cd2322810ccc. * Revert "squash merge from master and resolve conflict" This reverts commit 06db88ef7001bdfe93fb23af35af0d026b255dee. * Merge remote-tracking branch 'origin/master' * apply metal spec const thread count changes * Revert "apply metal spec const thread count changes" This reverts commit 3b9e6f53cee2e6076ac2b7a0d015a1ed2cbbd627. * Revert "Merge remote-tracking branch 'origin/master'" This reverts commit 99869d573a46dadeb24445405f5a1e37a8e03d0d. * apply metal spec const thread count changes --------- Co-authored-by: Yong He --- .../slang-ir-legalize-varying-params.cpp | 2389 +++++++++++++++++ .../slang/slang-ir-legalize-varying-params.h | 22 +- source/slang/slang-ir-metal-legalize.cpp | 1959 +------------- source/slang/slang-ir-wgsl-legalize.cpp | 1637 +---------- 4 files changed, 2538 insertions(+), 3469 deletions(-) diff --git a/source/slang/slang-ir-legalize-varying-params.cpp b/source/slang/slang-ir-legalize-varying-params.cpp index a98290545c..69d62c8bf8 100644 --- a/source/slang/slang-ir-legalize-varying-params.cpp +++ b/source/slang/slang-ir-legalize-varying-params.cpp @@ -6,6 +6,8 @@ #include "slang-ir-util.h" #include "slang-parameter-binding.h" +#include + namespace Slang { // Convert semantic name (ignores case) into equivlent `SystemValueSemanticName` @@ -1560,4 +1562,2391 @@ void depointerizeInputParams(IRFunc* entryPointFunc) } } + +class LegalizeShaderEntryPointContext +{ +public: + void legalizeEntryPoints(List& entryPoints) + { + for (auto entryPoint : entryPoints) + legalizeEntryPoint(entryPoint); + removeSemanticLayoutsFromLegalizedStructs(); + } + +protected: + LegalizeShaderEntryPointContext(IRModule* module, DiagnosticSink* sink, bool hoistParameters) + : m_module(module), m_sink(sink), hoistParameters(hoistParameters) + { + } + + IRModule* m_module; + DiagnosticSink* m_sink; + + struct SystemValueInfo + { + String systemValueName; + SystemValueSemanticName systemValueNameEnum; + ShortList permittedTypes; + + bool isUnsupported = false; + bool isSpecial = false; + }; + + struct SystemValLegalizationWorkItem + { + IRInst* var; + IRType* varType; + + String attrName; + UInt attrIndex; + }; + + virtual SystemValueInfo getSystemValueInfo( + String inSemanticName, + String* optionalSemanticIndex, + IRInst* parentVar) const = 0; + + virtual List collectSystemValFromEntryPoint( + EntryPointInfo entryPoint) const = 0; + + virtual void flattenNestedStructsTransferKeyDecorations(IRInst* newKey, IRInst* oldKey) + const = 0; + + virtual UnownedStringSlice getUserSemanticNameSlice(String& loweredName, bool isUserSemantic) + const = 0; + + virtual void addFragmentShaderReturnValueDecoration( + IRBuilder& builder, + IRInst* returnValueStructKey) const = 0; + + + virtual IRVarLayout* handleGeometryStageParameterVarLayout( + IRBuilder& builder, + IRVarLayout* paramVarLayout) const + { + SLANG_UNUSED(builder); + return paramVarLayout; + } + + virtual void handleSpecialSystemValue( + const EntryPointInfo& entryPoint, + SystemValLegalizationWorkItem& workItem, + const SystemValueInfo& info, + IRBuilder& builder) + { + SLANG_UNUSED(entryPoint); + SLANG_UNUSED(workItem); + SLANG_UNUSED(info); + SLANG_UNUSED(builder); + } + + virtual void legalizeAmplificationStageEntryPoint(const EntryPointInfo& entryPoint) const + { + SLANG_UNUSED(entryPoint); + } + + virtual void legalizeMeshStageEntryPoint(const EntryPointInfo& entryPoint) const + { + SLANG_UNUSED(entryPoint); + } + + + std::optional tryToMakeSystemValWorkItem( + IRInst* var, + IRType* varType) const + { + if (auto semanticDecoration = var->findDecoration()) + { + if (semanticDecoration->getSemanticName().startsWithCaseInsensitive(toSlice("sv_"))) + { + return { + {var, + varType, + String(semanticDecoration->getSemanticName()).toLower(), + (UInt)semanticDecoration->getSemanticIndex()}}; + } + } + + auto layoutDecor = var->findDecoration(); + if (!layoutDecor) + return {}; + auto sysValAttr = layoutDecor->findAttr(); + if (!sysValAttr) + return {}; + auto semanticName = String(sysValAttr->getName()); + auto sysAttrIndex = sysValAttr->getIndex(); + + return {{var, varType, semanticName, sysAttrIndex}}; + } + + void legalizeSystemValue(EntryPointInfo entryPoint, SystemValLegalizationWorkItem& workItem) + { + IRBuilder builder(entryPoint.entryPointFunc); + + auto var = workItem.var; + auto varType = workItem.varType; + auto semanticName = workItem.attrName; + + auto indexAsString = String(workItem.attrIndex); + SystemValueInfo info = getSystemValueInfo(semanticName, &indexAsString, var); + if (info.isSpecial) + { + handleSpecialSystemValue(entryPoint, workItem, info, builder); + } + + if (info.isUnsupported) + { + reportUnsupportedSystemAttribute(var, semanticName); + return; + } + if (!info.permittedTypes.getCount()) + return; + + builder.addTargetSystemValueDecoration(var, info.systemValueName.getUnownedSlice()); + + bool varTypeIsPermitted = false; + for (auto& permittedType : info.permittedTypes) + { + varTypeIsPermitted = varTypeIsPermitted || permittedType == varType; + } + + if (!varTypeIsPermitted) + { + // Note: we do not currently prefer any conversion + // example: + // * allowed types for semantic: `float4`, `uint4`, `int4` + // * user used, `float2` + // * Slang will equally prefer `float4` to `uint4` to `int4`. + // This means the type may lose data if slang selects `uint4` or `int4`. + bool foundAConversion = false; + for (auto permittedType : info.permittedTypes) + { + var->setFullType(permittedType); + builder.setInsertBefore( + entryPoint.entryPointFunc->getFirstBlock()->getFirstOrdinaryInst()); + + // get uses before we `tryConvertValue` since this creates a new use + List uses; + for (auto use = var->firstUse; use; use = use->nextUse) + uses.add(use); + + auto convertedValue = tryConvertValue(builder, var, varType); + if (convertedValue == nullptr) + continue; + + foundAConversion = true; + copyNameHintAndDebugDecorations(convertedValue, var); + + for (auto use : uses) + builder.replaceOperand(use, convertedValue); + } + if (!foundAConversion) + { + // If we can't convert the value, report an error. + for (auto permittedType : info.permittedTypes) + { + StringBuilder typeNameSB; + getTypeNameHint(typeNameSB, permittedType); + m_sink->diagnose( + var->sourceLoc, + Diagnostics::systemValueTypeIncompatible, + semanticName, + typeNameSB.produceString()); + } + } + } + } + +private: + const bool hoistParameters; + HashSet semanticInfoToRemove; + + void removeSemanticLayoutsFromLegalizedStructs() + { + // Metal and WGSL does not allow duplicate attributes to appear in the same shader. + // If we emit our own struct with `[[color(0)]`, all existing uses of `[[color(0)]]` + // must be removed. + for (auto field : semanticInfoToRemove) + { + auto key = field->getKey(); + // Some decorations appear twice, destroy all found + for (;;) + { + if (auto semanticDecor = key->findDecoration()) + { + semanticDecor->removeAndDeallocate(); + continue; + } + else if (auto layoutDecor = key->findDecoration()) + { + layoutDecor->removeAndDeallocate(); + continue; + } + break; + } + } + } + + void hoistEntryPointParameterFromStruct(EntryPointInfo entryPoint) + { + // If an entry point has a input parameter with a struct type, we want to hoist out + // all the fields of the struct type to be individual parameters of the entry point. + // This will canonicalize the entry point signature, so we can handle all cases uniformly. + + // For example, given an entry point: + // ``` + // struct VertexInput { float3 pos; float 2 uv; int vertexId : SV_VertexID}; + // void main(VertexInput vin) { ... } + // ``` + // We will transform it to: + // ``` + // void main(float3 pos, float2 uv, int vertexId : SV_VertexID) { + // VertexInput vin = {pos,uv,vertexId}; + // ... + // } + // ``` + + auto func = entryPoint.entryPointFunc; + List paramsToProcess; + for (auto param : func->getParams()) + { + if (as(param->getDataType())) + { + paramsToProcess.add(param); + } + } + + IRBuilder builder(func); + builder.setInsertBefore(func); + for (auto param : paramsToProcess) + { + auto structType = as(param->getDataType()); + builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); + auto varLayout = findVarLayout(param); + + // If `param` already has a semantic, we don't want to hoist its fields out. + if (varLayout->findSystemValueSemanticAttr() != nullptr || + param->findDecoration()) + continue; + + IRStructTypeLayout* structTypeLayout = nullptr; + if (varLayout) + structTypeLayout = as(varLayout->getTypeLayout()); + Index fieldIndex = 0; + List fieldParams; + for (auto field : structType->getFields()) + { + auto fieldParam = builder.emitParam(field->getFieldType()); + IRCloneEnv cloneEnv; + cloneInstDecorationsAndChildren( + &cloneEnv, + builder.getModule(), + field->getKey(), + fieldParam); + + IRVarLayout* fieldLayout = + structTypeLayout ? structTypeLayout->getFieldLayout(fieldIndex) : nullptr; + if (varLayout) + { + IRVarLayout::Builder varLayoutBuilder(&builder, fieldLayout->getTypeLayout()); + varLayoutBuilder.cloneEverythingButOffsetsFrom(fieldLayout); + for (auto offsetAttr : fieldLayout->getOffsetAttrs()) + { + auto parentOffsetAttr = + varLayout->findOffsetAttr(offsetAttr->getResourceKind()); + UInt parentOffset = parentOffsetAttr ? parentOffsetAttr->getOffset() : 0; + UInt parentSpace = parentOffsetAttr ? parentOffsetAttr->getSpace() : 0; + auto resInfo = + varLayoutBuilder.findOrAddResourceInfo(offsetAttr->getResourceKind()); + resInfo->offset = parentOffset + offsetAttr->getOffset(); + resInfo->space = parentSpace + offsetAttr->getSpace(); + } + builder.addLayoutDecoration(fieldParam, varLayoutBuilder.build()); + } + param->insertBefore(fieldParam); + fieldParams.add(fieldParam); + fieldIndex++; + } + builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); + auto reconstructedParam = + builder.emitMakeStruct(structType, fieldParams.getCount(), fieldParams.getBuffer()); + param->replaceUsesWith(reconstructedParam); + param->removeFromParent(); + } + fixUpFuncType(func); + } + + // Flattens all struct parameters of an entryPoint to ensure parameters are a flat struct + void flattenInputParameters(EntryPointInfo entryPoint) + { + // Goal is to ensure we have a flattened IRParam (0 nested IRStructType members). + /* + // Assume the following code + struct NestedFragment + { + float2 p3; + }; + struct Fragment + { + float4 p1; + float3 p2; + NestedFragment p3_nested; + }; + + // Fragment flattens into + struct Fragment + { + float4 p1; + float3 p2; + float2 p3; + }; + */ + + // This is important since Metal and WGSL does not allow semantic's on a struct + /* + // Assume the following code + struct NestedFragment1 + { + float2 p3; + }; + struct Fragment1 + { + float4 p1 : SV_TARGET0; + float3 p2 : SV_TARGET1; + NestedFragment p3_nested : SV_TARGET2; // error, semantic on struct + }; + + */ + + // Metal does allow semantics on members of a nested struct but we are avoiding this + // approach since there are senarios where legalization (and verification) is + // hard/expensive without creating a flat struct: + // 1. Entry points may share structs, semantics may be inconsistent across entry points + // 2. Multiple of the same struct may be used in a param list + // + // WGSL does NOT allow semantics on members of a nested struct. + /* + // Assume the following code + struct NestedFragment + { + float2 p3; + }; + struct Fragment + { + float4 p1 : SV_TARGET0; + NestedFragment p2 : SV_TARGET1; + NestedFragment p3 : SV_TARGET2; + }; + + // Legalized without flattening -- abandoned + struct NestedFragment1 + { + float2 p3 : SV_TARGET1; + }; + struct NestedFragment2 + { + float2 p3 : SV_TARGET2; + }; + struct Fragment + { + float4 p1 : SV_TARGET0; + NestedFragment1 p2; + NestedFragment2 p3; + }; + + // Legalized with flattening -- current approach + struct Fragment + { + float4 p1 : SV_TARGET0; + float2 p2 : SV_TARGET1; + float2 p3 : SV_TARGET2; + }; + */ + + auto func = entryPoint.entryPointFunc; + bool modified = false; + for (auto param : func->getParams()) + { + auto layout = findVarLayout(param); + if (!layout) + continue; + if (!layout->findOffsetAttr(LayoutResourceKind::VaryingInput)) + continue; + if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) + continue; + // If we find a IRParam with a IRStructType member, we need to flatten the entire + // IRParam + if (auto structType = as(param->getDataType())) + { + IRBuilder builder(func); + MapStructToFlatStruct mapOldFieldToNewField; + + // Flatten struct if we have nested IRStructType + auto flattenedStruct = maybeFlattenNestedStructs( + builder, + structType, + mapOldFieldToNewField, + semanticInfoToRemove); + + // Validate/rearange all semantics which overlap in our flat struct. + fixFieldSemanticsOfFlatStruct(flattenedStruct); + ensureStructHasUserSemantic( + flattenedStruct, + layout); + if (flattenedStruct != structType) + { + // Replace the 'old IRParam type' with a 'new IRParam type' + param->setFullType(flattenedStruct); + + // Emit a new variable at EntryPoint of 'old IRParam type' + builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); + auto dstVal = builder.emitVar(structType); + auto dstLoad = builder.emitLoad(dstVal); + param->replaceUsesWith(dstLoad); + builder.setInsertBefore(dstLoad); + // Copy the 'new IRParam type' to our 'old IRParam type' + mapOldFieldToNewField + .emitCopy<(int)MapStructToFlatStruct::CopyOptions::FlatStructIntoStruct>( + builder, + dstVal, + param); + + modified = true; + } + } + } + if (modified) + fixUpFuncType(func); + } + + void packStageInParameters(EntryPointInfo entryPoint) + { + // If the entry point has any parameters whose layout contains VaryingInput, + // we need to pack those parameters into a single `struct` type, and decorate + // the fields with the appropriate `[[attribute]]` decorations. + // For other parameters that are not `VaryingInput`, we need to leave them as is. + // + // For example, given this code after `hoistEntryPointParameterFromStruct`: + // ``` + // void main(float3 pos, float2 uv, int vertexId : SV_VertexID) { + // VertexInput vin = {pos,uv,vertexId}; + // ... + // } + // ``` + // We are going to transform it into: + // ``` + // struct VertexInput { + // float3 pos [[attribute(0)]]; + // float2 uv [[attribute(1)]]; + // }; + // void main(VertexInput vin, int vertexId : SV_VertexID) { + // let pos = vin.pos; + // let uv = vin.uv; + // ... + // } + + auto func = entryPoint.entryPointFunc; + + bool isGeometryStage = false; + switch (entryPoint.entryPointDecor->getProfile().getStage()) + { + case Stage::Vertex: + case Stage::Amplification: + case Stage::Mesh: + case Stage::Geometry: + case Stage::Domain: + case Stage::Hull: + isGeometryStage = true; + break; + } + + List paramsToPack; + for (auto param : func->getParams()) + { + auto layout = findVarLayout(param); + if (!layout) + continue; + if (!layout->findOffsetAttr(LayoutResourceKind::VaryingInput)) + continue; + if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) + continue; + paramsToPack.add(param); + } + + if (paramsToPack.getCount() == 0) + return; + + IRBuilder builder(func); + builder.setInsertBefore(func); + IRStructType* structType = builder.createStructType(); + auto stageText = getStageText(entryPoint.entryPointDecor->getProfile().getStage()); + builder.addNameHintDecoration( + structType, + (String(stageText) + toSlice("Input")).getUnownedSlice()); + List keys; + IRStructTypeLayout::Builder layoutBuilder(&builder); + for (auto param : paramsToPack) + { + auto paramVarLayout = findVarLayout(param); + auto key = builder.createStructKey(); + param->transferDecorationsTo(key); + builder.createStructField(structType, key, param->getDataType()); + if (auto varyingInOffsetAttr = + paramVarLayout->findOffsetAttr(LayoutResourceKind::VaryingInput)) + { + if (!key->findDecoration() && + !paramVarLayout->findAttr()) + { + // If the parameter doesn't have a semantic, we need to add one for semantic + // matching. + builder.addSemanticDecoration( + key, + toSlice("_slang_attr"), + (int)varyingInOffsetAttr->getOffset()); + } + } + + if (isGeometryStage) + { + paramVarLayout = handleGeometryStageParameterVarLayout(builder, paramVarLayout); + } + + layoutBuilder.addField(key, paramVarLayout); + builder.addLayoutDecoration(key, paramVarLayout); + keys.add(key); + } + builder.setInsertInto(func->getFirstBlock()); + auto packedParam = builder.emitParamAtHead(structType); + auto typeLayout = layoutBuilder.build(); + IRVarLayout::Builder varLayoutBuilder(&builder, typeLayout); + + // Add a VaryingInput resource info to the packed parameter layout, so that we can emit + // the needed `[[stage_in]]` attribute in Metal emitter. + varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::VaryingInput); + auto paramVarLayout = varLayoutBuilder.build(); + builder.addLayoutDecoration(packedParam, paramVarLayout); + + // Replace the original parameters with the packed parameter + builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); + for (Index paramIndex = 0; paramIndex < paramsToPack.getCount(); paramIndex++) + { + auto param = paramsToPack[paramIndex]; + auto key = keys[paramIndex]; + auto paramField = builder.emitFieldExtract(param->getDataType(), packedParam, key); + param->replaceUsesWith(paramField); + param->removeFromParent(); + } + fixUpFuncType(func); + } + + + void reportUnsupportedSystemAttribute(IRInst* param, String semanticName) + { + m_sink->diagnose( + param->sourceLoc, + Diagnostics::systemValueAttributeNotSupported, + semanticName); + } + + template + void ensureStructHasUserSemantic(IRStructType* structType, IRVarLayout* varLayout) + { + // Ensure each field in an output struct type has either a system semantic or a user + // semantic, so that signature matching can happen correctly. + auto typeLayout = as(varLayout->getTypeLayout()); + Index index = 0; + IRBuilder builder(structType); + for (auto field : structType->getFields()) + { + auto key = field->getKey(); + if (auto semanticDecor = key->findDecoration()) + { + if (semanticDecor->getSemanticName().startsWithCaseInsensitive(toSlice("sv_"))) + { + auto indexAsString = String(UInt(semanticDecor->getSemanticIndex())); + auto sysValInfo = + getSystemValueInfo(semanticDecor->getSemanticName(), &indexAsString, field); + if (sysValInfo.isUnsupported) + { + reportUnsupportedSystemAttribute(field, semanticDecor->getSemanticName()); + } + else + { + builder.addTargetSystemValueDecoration( + key, + sysValInfo.systemValueName.getUnownedSlice()); + semanticDecor->removeAndDeallocate(); + } + } + index++; + continue; + } + typeLayout->getFieldLayout(index); + auto fieldLayout = typeLayout->getFieldLayout(index); + if (auto offsetAttr = fieldLayout->findOffsetAttr(K)) + { + UInt varOffset = 0; + if (auto varOffsetAttr = varLayout->findOffsetAttr(K)) + varOffset = varOffsetAttr->getOffset(); + varOffset += offsetAttr->getOffset(); + builder.addSemanticDecoration(key, toSlice("_slang_attr"), (int)varOffset); + } + index++; + } + } + + // Stores a hicharchy of members and children which map 'oldStruct->member' to + // 'flatStruct->member' Note: this map assumes we map to FlatStruct since it is easier/faster to + // process + struct MapStructToFlatStruct + { + /* + We need a hicharchy map to resolve dependencies for mapping + oldStruct to newStruct efficently. Example: + + MyStruct + | + / | \ + / | \ + / | \ + M0 M1 M2 + | | | + A_0 A_0 B_0 + + Without storing hicharchy information, there will be no way to tell apart + `myStruct.M0.A0` from `myStruct.M1.A0` since IRStructKey/IRStructField + only has 1 instance of `A::A0` + */ + + enum CopyOptions : int + { + // Copy a flattened-struct into a struct + FlatStructIntoStruct = 0, + + // Copy a struct into a flattened-struct + StructIntoFlatStruct = 1, + }; + + private: + // Children of member if applicable. + Dictionary members; + + // Field correlating to MapStructToFlatStruct Node. + IRInst* node; + IRStructKey* getKey() + { + SLANG_ASSERT(as(node)); + return as(node)->getKey(); + } + IRInst* getNode() { return node; } + IRType* getFieldType() + { + SLANG_ASSERT(as(node)); + return as(node)->getFieldType(); + } + + // Whom node maps to inside target flatStruct + IRStructField* targetMapping; + + auto begin() { return members.begin(); } + auto end() { return members.end(); } + + // Copies members of oldStruct to/from newFlatStruct. Assumes members of val1 maps to + // members in val2 using `MapStructToFlatStruct` + template + static void _emitCopy( + IRBuilder& builder, + IRInst* val1, + IRStructType* type1, + IRInst* val2, + IRStructType* type2, + MapStructToFlatStruct& node) + { + for (auto& field1Pair : node) + { + auto& field1 = field1Pair.second; + + // Get member of val1 + IRInst* fieldAddr1 = nullptr; + if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) + { + fieldAddr1 = builder.emitFieldAddress(type1, val1, field1.getKey()); + } + else + { + if (as(val1)) + val1 = builder.emitLoad(val1); + fieldAddr1 = builder.emitFieldExtract(type1, val1, field1.getKey()); + } + + // If val1 is a struct, recurse + if (auto fieldAsStruct1 = as(field1.getFieldType())) + { + _emitCopy( + builder, + fieldAddr1, + fieldAsStruct1, + val2, + type2, + field1); + continue; + } + + // Get member of val2 which maps to val1.member + auto field2 = field1.getMapping(); + SLANG_ASSERT(field2); + IRInst* fieldAddr2 = nullptr; + if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) + { + if (as(val2)) + val2 = builder.emitLoad(val1); + fieldAddr2 = builder.emitFieldExtract(type2, val2, field2->getKey()); + } + else + { + fieldAddr2 = builder.emitFieldAddress(type2, val2, field2->getKey()); + } + + // Copy val2/val1 member into val1/val2 member + if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) + { + builder.emitStore(fieldAddr1, fieldAddr2); + } + else + { + builder.emitStore(fieldAddr2, fieldAddr1); + } + } + } + + public: + void setNode(IRInst* newNode) { node = newNode; } + // Get 'MapStructToFlatStruct' that is a child of 'parent'. + // Make 'MapStructToFlatStruct' if no 'member' is currently mapped to 'parent'. + MapStructToFlatStruct& getMember(IRStructField* member) { return members[member]; } + MapStructToFlatStruct& operator[](IRStructField* member) { return getMember(member); } + + void setMapping(IRStructField* newTargetMapping) { targetMapping = newTargetMapping; } + // Get 'MapStructToFlatStruct' that is a child of 'parent'. + // Return nullptr if no member is mapped to 'parent' + IRStructField* getMapping() { return targetMapping; } + + // Copies srcVal into dstVal using hicharchy map. + template + void emitCopy(IRBuilder& builder, IRInst* dstVal, IRInst* srcVal) + { + auto dstType = dstVal->getDataType(); + if (auto dstPtrType = as(dstType)) + dstType = dstPtrType->getValueType(); + auto dstStructType = as(dstType); + SLANG_ASSERT(dstStructType); + + auto srcType = srcVal->getDataType(); + if (auto srcPtrType = as(srcType)) + srcType = srcPtrType->getValueType(); + auto srcStructType = as(srcType); + SLANG_ASSERT(srcStructType); + + if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) + { + // CopyOptions::FlatStructIntoStruct copy a flattened-struct (mapped member) into a + // struct + SLANG_ASSERT(node == dstStructType); + _emitCopy( + builder, + dstVal, + dstStructType, + srcVal, + srcStructType, + *this); + } + else + { + // CopyOptions::StructIntoFlatStruct copy a struct into a flattened-struct + SLANG_ASSERT(node == srcStructType); + _emitCopy( + builder, + srcVal, + srcStructType, + dstVal, + dstStructType, + *this); + } + } + }; + + IRStructType* _flattenNestedStructs( + IRBuilder& builder, + IRStructType* dst, + IRStructType* src, + IRSemanticDecoration* parentSemanticDecoration, + IRLayoutDecoration* parentLayout, + MapStructToFlatStruct& mapFieldToField, + HashSet& varsWithSemanticInfo) + { + // For all fields ('oldField') of a struct do the following: + // 1. Check for 'decorations which carry semantic info' (IRSemanticDecoration, + // IRLayoutDecoration), store these if found. + // * Do not propagate semantic info if the current node has *any* form of semantic + // information. + // Update varsWithSemanticInfo. + // 2. If IRStructType: + // 2a. Recurse this function with 'decorations that carry semantic info' from parent. + // 3. If not IRStructType: + // 3a Metal. Emit 'newField' equal to 'oldField', add 'decorations which carry semantic + // info'. + // + // 3a WGSL. Emit 'newField' with 'newKey' equal to 'oldField' and 'oldKey', respectively, + // where 'oldKey' is the key corresponding to 'oldField'. + // Add 'decorations which carry semantic info' to 'newField', and move all decorations + // of 'oldKey' to 'newKey'. + // 3b. Store a mapping from 'oldField' to 'newField' in 'mapFieldToField'. This info is + // needed to copy between types. + for (auto oldField : src->getFields()) + { + auto& fieldMappingNode = mapFieldToField[oldField]; + fieldMappingNode.setNode(oldField); + + // step 1 + bool foundSemanticDecor = false; + auto oldKey = oldField->getKey(); + IRSemanticDecoration* fieldSemanticDecoration = parentSemanticDecoration; + if (auto oldSemanticDecoration = oldKey->findDecoration()) + { + foundSemanticDecor = true; + fieldSemanticDecoration = oldSemanticDecoration; + parentLayout = nullptr; + } + + IRLayoutDecoration* fieldLayout = parentLayout; + if (auto oldLayout = oldKey->findDecoration()) + { + fieldLayout = oldLayout; + if (!foundSemanticDecor) + fieldSemanticDecoration = nullptr; + } + if (fieldSemanticDecoration != parentSemanticDecoration || parentLayout != fieldLayout) + varsWithSemanticInfo.add(oldField); + + // step 2a + if (auto structFieldType = as(oldField->getFieldType())) + { + _flattenNestedStructs( + builder, + dst, + structFieldType, + fieldSemanticDecoration, + fieldLayout, + fieldMappingNode, + varsWithSemanticInfo); + continue; + } + + // step 3a + auto newKey = builder.createStructKey(); + flattenNestedStructsTransferKeyDecorations(newKey, oldKey); + + auto newField = builder.createStructField(dst, newKey, oldField->getFieldType()); + copyNameHintAndDebugDecorations(newField, oldField); + + if (fieldSemanticDecoration) + builder.addSemanticDecoration( + newKey, + fieldSemanticDecoration->getSemanticName(), + fieldSemanticDecoration->getSemanticIndex()); + + if (fieldLayout) + { + IRLayout* oldLayout = fieldLayout->getLayout(); + List instToCopy; + // Only copy certain decorations needed for resolving system semantics + for (UInt i = 0; i < oldLayout->getOperandCount(); i++) + { + auto operand = oldLayout->getOperand(i); + if (as(operand) || as(operand) || + as(operand) || as(operand)) + instToCopy.add(operand); + } + IRVarLayout* newLayout = builder.getVarLayout(instToCopy); + builder.addLayoutDecoration(newKey, newLayout); + } + // step 3b + fieldMappingNode.setMapping(newField); + } + + return dst; + } + + // Returns a `IRStructType*` without any `IRStructType*` members. `src` may be returned if there + // was no struct flattening. + // @param mapFieldToField Behavior maps all `IRStructField` of `src` to the new struct + // `IRStructFields`s + IRStructType* maybeFlattenNestedStructs( + IRBuilder& builder, + IRStructType* src, + MapStructToFlatStruct& mapFieldToField, + HashSet& varsWithSemanticInfo) + { + // Find all values inside struct that need flattening and legalization. + bool hasStructTypeMembers = false; + for (auto field : src->getFields()) + { + if (as(field->getFieldType())) + { + hasStructTypeMembers = true; + break; + } + } + if (!hasStructTypeMembers) + return src; + + // We need to: + // 1. Make new struct 1:1 with old struct but without nestested structs (flatten) + // 2. Ensure semantic attributes propegate. This will create overlapping semantics (can be + // handled later). + // 3. Store the mapping from old to new struct fields to allow copying a old-struct to + // new-struct. + builder.setInsertAfter(src); + auto newStruct = builder.createStructType(); + copyNameHintAndDebugDecorations(newStruct, src); + mapFieldToField.setNode(src); + return _flattenNestedStructs( + builder, + newStruct, + src, + nullptr, + nullptr, + mapFieldToField, + varsWithSemanticInfo); + } + + // Replaces all 'IRReturn' by copying the current 'IRReturn' to a new var of type 'newType'. + // Copying logic from 'IRReturn' to 'newType' is controlled by 'copyLogicFunc' function. + template + void _replaceAllReturnInst( + IRBuilder& builder, + IRFunc* targetFunc, + IRStructType* newType, + CopyLogicFunc copyLogicFunc) + { + for (auto block : targetFunc->getBlocks()) + { + if (auto returnInst = as(block->getTerminator())) + { + builder.setInsertBefore(returnInst); + auto returnVal = returnInst->getVal(); + returnInst->setOperand(0, copyLogicFunc(builder, newType, returnVal)); + } + } + } + + UInt _returnNonOverlappingAttributeIndex(std::set& usedSemanticIndex) + { + // Find first unused semantic index of equal semantic type + // to fill any gaps in user set semantic bindings + UInt prev = 0; + for (auto i : usedSemanticIndex) + { + if (i > prev + 1) + { + break; + } + prev = i; + } + usedSemanticIndex.insert(prev + 1); + return prev + 1; + } + + template + struct AttributeParentPair + { + IRLayoutDecoration* layoutDecor; + T* attr; + }; + + IRLayoutDecoration* _replaceAttributeOfLayout( + IRBuilder& builder, + IRLayoutDecoration* parentLayoutDecor, + IRInst* instToReplace, + IRInst* instToReplaceWith) + { + // Replace `instToReplace` with a `instToReplaceWith` + + auto layout = parentLayoutDecor->getLayout(); + // Find the exact same decoration `instToReplace` in-case multiple of the same type exist + List opList; + opList.add(instToReplaceWith); + for (UInt i = 0; i < layout->getOperandCount(); i++) + { + if (layout->getOperand(i) != instToReplace) + opList.add(layout->getOperand(i)); + } + auto newLayoutDecor = builder.addLayoutDecoration( + parentLayoutDecor->getParent(), + builder.getVarLayout(opList)); + parentLayoutDecor->removeAndDeallocate(); + return newLayoutDecor; + } + + IRLayoutDecoration* _simplifyUserSemanticNames( + IRBuilder& builder, + IRLayoutDecoration* layoutDecor) + { + // Ensure all 'ExplicitIndex' semantics such as "SV_TARGET0" are simplified into + // ("SV_TARGET", 0) using 'IRUserSemanticAttr' This is done to ensure we can check semantic + // groups using 'IRUserSemanticAttr1->getName() == IRUserSemanticAttr2->getName()' + SLANG_ASSERT(layoutDecor); + auto layout = layoutDecor->getLayout(); + List layoutOps; + layoutOps.reserve(3); + bool changed = false; + for (auto attr : layout->getAllAttrs()) + { + if (auto userSemantic = as(attr)) + { + UnownedStringSlice outName; + UnownedStringSlice outIndex; + bool hasStringIndex = splitNameAndIndex(userSemantic->getName(), outName, outIndex); + if (hasStringIndex) + { + changed = true; + auto loweredName = String(outName).toLower(); + auto loweredNameSlice = loweredName.getUnownedSlice(); + auto newDecoration = + builder.getUserSemanticAttr(loweredNameSlice, stringToInt(outIndex)); + userSemantic->replaceUsesWith(newDecoration); + userSemantic->removeAndDeallocate(); + userSemantic = newDecoration; + } + layoutOps.add(userSemantic); + continue; + } + layoutOps.add(attr); + } + if (changed) + { + auto parent = layoutDecor->parent; + layoutDecor->removeAndDeallocate(); + builder.addLayoutDecoration(parent, builder.getVarLayout(layoutOps)); + } + return layoutDecor; + } + + // Find overlapping field semantics and legalize them + void fixFieldSemanticsOfFlatStruct(IRStructType* structType) + { + // Goal is to ensure we do not have overlapping semantics for the user defined semantics: + // Note that in WGSL, the semantics can be either `builtin` without index or `location` with + // index. + /* + // Assume the following code + struct Fragment + { + float4 p0 : SV_POSITION; + float2 p1 : TEXCOORD0; + float2 p2 : TEXCOORD1; + float3 p3 : COLOR0; + float3 p4 : COLOR1; + }; + + // Translates into + struct Fragment + { + float4 p0 : BUILTIN_POSITION; + float2 p1 : LOCATION_0; + float2 p2 : LOCATION_1; + float3 p3 : LOCATION_2; + float3 p4 : LOCATION_3; + }; + */ + + // For Multi-Render-Target, the semantic index must be translated to `location` with + // the same index. Assume the following code + /* + struct Fragment + { + float4 p0 : SV_TARGET1; + float4 p1 : SV_TARGET0; + }; + + // Translates into + struct Fragment + { + float4 p0 : LOCATION_1; + float4 p1 : LOCATION_0; + }; + */ + + IRBuilder builder(this->m_module); + + List overlappingSemanticsDecor; + Dictionary>> + usedSemanticIndexSemanticDecor; + + List> overlappingVarOffset; + Dictionary>> usedSemanticIndexVarOffset; + + List> overlappingUserSemantic; + Dictionary>> + usedSemanticIndexUserSemantic; + + // We store a map from old `IRLayoutDecoration*` to new `IRLayoutDecoration*` since when + // legalizing we may destroy and remake a `IRLayoutDecoration*` + Dictionary oldLayoutDecorToNew; + + // Collect all "semantic info carrying decorations". Any collected decoration will + // fill up their respective 'Dictionary>' + // to keep track of in-use offsets for a semantic type. + // Example: IRSemanticDecoration with name of "SV_TARGET1". + // * This will have SEMANTIC_TYPE of "sv_target". + // * This will use up index '1' + // + // Now if a second equal semantic "SV_TARGET1" is found, we add this decoration to + // a list of 'overlapping semantic info decorations' so we can legalize this + // 'semantic info decoration' later. + // + // NOTE: this is a flat struct, all members are children of the initial + // IRStructType. + for (auto field : structType->getFields()) + { + auto key = field->getKey(); + if (auto semanticDecoration = key->findDecoration()) + { + auto semanticName = semanticDecoration->getSemanticName(); + + // sv_target is treated as a user-semantic because it should be emitted with + // @location like how the user semantics are emitted. + // For fragment shader, only sv_target will user @location, and for non-fragment + // shaders, sv_target is not valid. + bool isUserSemantic = + (semanticName.startsWithCaseInsensitive(toSlice("sv_target")) || + !semanticName.startsWithCaseInsensitive(toSlice("sv_"))); + + // Ensure names are in a uniform lowercase format so we can bunch together simmilar + // semantics. + UnownedStringSlice outName; + UnownedStringSlice outIndex; + bool hasStringIndex = splitNameAndIndex(semanticName, outName, outIndex); + + auto loweredName = String(outName).toLower(); + auto loweredNameSlice = getUserSemanticNameSlice(loweredName, isUserSemantic); + auto semanticIndex = + hasStringIndex ? stringToInt(outIndex) : semanticDecoration->getSemanticIndex(); + auto newDecoration = + builder.addSemanticDecoration(key, loweredNameSlice, semanticIndex); + + semanticDecoration->replaceUsesWith(newDecoration); + semanticDecoration->removeAndDeallocate(); + semanticDecoration = newDecoration; + + auto& semanticUse = + usedSemanticIndexSemanticDecor[semanticDecoration->getSemanticName()]; + if (semanticUse.find(semanticDecoration->getSemanticIndex()) != semanticUse.end()) + overlappingSemanticsDecor.add(semanticDecoration); + else + semanticUse.insert(semanticDecoration->getSemanticIndex()); + } + if (auto layoutDecor = key->findDecoration()) + { + // Ensure names are in a uniform lowercase format so we can bunch together simmilar + // semantics + layoutDecor = _simplifyUserSemanticNames(builder, layoutDecor); + oldLayoutDecorToNew[layoutDecor] = layoutDecor; + auto layout = layoutDecor->getLayout(); + for (auto attr : layout->getAllAttrs()) + { + if (auto offset = as(attr)) + { + auto& semanticUse = usedSemanticIndexVarOffset[offset->getResourceKind()]; + if (semanticUse.find(offset->getOffset()) != semanticUse.end()) + overlappingVarOffset.add({layoutDecor, offset}); + else + semanticUse.insert(offset->getOffset()); + } + else if (auto userSemantic = as(attr)) + { + auto& semanticUse = usedSemanticIndexUserSemantic[userSemantic->getName()]; + if (semanticUse.find(userSemantic->getIndex()) != semanticUse.end()) + overlappingUserSemantic.add({layoutDecor, userSemantic}); + else + semanticUse.insert(userSemantic->getIndex()); + } + } + } + } + + // Legalize all overlapping 'semantic info decorations' + for (auto decor : overlappingSemanticsDecor) + { + auto newOffset = _returnNonOverlappingAttributeIndex( + usedSemanticIndexSemanticDecor[decor->getSemanticName()]); + builder.addSemanticDecoration( + decor->getParent(), + decor->getSemanticName(), + (int)newOffset); + decor->removeAndDeallocate(); + } + for (auto& varOffset : overlappingVarOffset) + { + auto newOffset = _returnNonOverlappingAttributeIndex( + usedSemanticIndexVarOffset[varOffset.attr->getResourceKind()]); + auto newVarOffset = builder.getVarOffsetAttr( + varOffset.attr->getResourceKind(), + newOffset, + varOffset.attr->getSpace()); + oldLayoutDecorToNew[varOffset.layoutDecor] = _replaceAttributeOfLayout( + builder, + oldLayoutDecorToNew[varOffset.layoutDecor], + varOffset.attr, + newVarOffset); + } + for (auto& userSemantic : overlappingUserSemantic) + { + auto newOffset = _returnNonOverlappingAttributeIndex( + usedSemanticIndexUserSemantic[userSemantic.attr->getName()]); + auto newUserSemantic = + builder.getUserSemanticAttr(userSemantic.attr->getName(), newOffset); + oldLayoutDecorToNew[userSemantic.layoutDecor] = _replaceAttributeOfLayout( + builder, + oldLayoutDecorToNew[userSemantic.layoutDecor], + userSemantic.attr, + newUserSemantic); + } + } + + void wrapReturnValueInStruct(EntryPointInfo entryPoint) + { + // Wrap return value into a struct if it is not already a struct. + // For example, given this entry point: + // ``` + // float4 main() : SV_Target { return float3(1,2,3); } + // ``` + // We are going to transform it into: + // ``` + // struct Output { + // float4 value : SV_Target; + // }; + // Output main() { return {float3(1,2,3)}; } + + auto func = entryPoint.entryPointFunc; + + auto returnType = func->getResultType(); + if (as(returnType)) + return; + auto entryPointLayoutDecor = func->findDecoration(); + if (!entryPointLayoutDecor) + return; + auto entryPointLayout = as(entryPointLayoutDecor->getLayout()); + if (!entryPointLayout) + return; + auto resultLayout = entryPointLayout->getResultLayout(); + + // If return type is already a struct, just make sure every field has a semantic. + if (auto returnStructType = as(returnType)) + { + IRBuilder builder(func); + MapStructToFlatStruct mapOldFieldToNewField; + // Flatten result struct type to ensure we do not have nested semantics + auto flattenedStruct = maybeFlattenNestedStructs( + builder, + returnStructType, + mapOldFieldToNewField, + semanticInfoToRemove); + if (returnStructType != flattenedStruct) + { + // Replace all return-values with the flattenedStruct we made. + _replaceAllReturnInst( + builder, + func, + flattenedStruct, + [&](IRBuilder& copyBuilder, IRStructType* dstType, IRInst* srcVal) -> IRInst* + { + auto srcStructType = as(srcVal->getDataType()); + SLANG_ASSERT(srcStructType); + auto dstVal = copyBuilder.emitVar(dstType); + mapOldFieldToNewField.emitCopy<( + int)MapStructToFlatStruct::CopyOptions::StructIntoFlatStruct>( + copyBuilder, + dstVal, + srcVal); + return builder.emitLoad(dstVal); + }); + fixUpFuncType(func, flattenedStruct); + } + // Ensure non-overlapping semantics + fixFieldSemanticsOfFlatStruct(flattenedStruct); + ensureStructHasUserSemantic( + flattenedStruct, + resultLayout); + return; + } + + IRBuilder builder(func); + builder.setInsertBefore(func); + IRStructType* structType = builder.createStructType(); + auto stageText = getStageText(entryPoint.entryPointDecor->getProfile().getStage()); + builder.addNameHintDecoration( + structType, + (String(stageText) + toSlice("Output")).getUnownedSlice()); + auto key = builder.createStructKey(); + builder.addNameHintDecoration(key, toSlice("output")); + builder.addLayoutDecoration(key, resultLayout); + builder.createStructField(structType, key, returnType); + IRStructTypeLayout::Builder structTypeLayoutBuilder(&builder); + structTypeLayoutBuilder.addField(key, resultLayout); + auto typeLayout = structTypeLayoutBuilder.build(); + IRVarLayout::Builder varLayoutBuilder(&builder, typeLayout); + auto varLayout = varLayoutBuilder.build(); + ensureStructHasUserSemantic(structType, varLayout); + + _replaceAllReturnInst( + builder, + func, + structType, + [](IRBuilder& copyBuilder, IRStructType* dstType, IRInst* srcVal) -> IRInst* + { return copyBuilder.emitMakeStruct(dstType, 1, &srcVal); }); + + // Assign an appropriate system value semantic for stage output + auto stage = entryPoint.entryPointDecor->getProfile().getStage(); + switch (stage) + { + case Stage::Compute: + case Stage::Fragment: + { + addFragmentShaderReturnValueDecoration(builder, key); + break; + } + case Stage::Vertex: + { + builder.addTargetSystemValueDecoration(key, toSlice("position")); + break; + } + default: + SLANG_ASSERT(false); + return; + } + + fixUpFuncType(func, structType); + } + + IRInst* tryConvertValue(IRBuilder& builder, IRInst* val, IRType* toType) + { + auto fromType = val->getFullType(); + if (auto fromVector = as(fromType)) + { + if (auto toVector = as(toType)) + { + if (fromVector->getElementCount() != toVector->getElementCount()) + { + fromType = builder.getVectorType( + fromVector->getElementType(), + toVector->getElementCount()); + val = builder.emitVectorReshape(fromType, val); + } + } + else if (as(toType)) + { + UInt index = 0; + val = builder.emitSwizzle(fromVector->getElementType(), val, 1, &index); + if (toType->getOp() == kIROp_VoidType) + return nullptr; + } + } + else if (auto fromBasicType = as(fromType)) + { + if (fromBasicType->getOp() == kIROp_VoidType) + return nullptr; + if (!as(toType)) + return nullptr; + if (toType->getOp() == kIROp_VoidType) + return nullptr; + } + else + { + return nullptr; + } + return builder.emitCast(toType, val); + } + + void legalizeSystemValueParameters(EntryPointInfo entryPoint) + { + List systemValWorkItems = + collectSystemValFromEntryPoint(entryPoint); + + for (auto index = 0; index < systemValWorkItems.getCount(); index++) + { + legalizeSystemValue(entryPoint, systemValWorkItems[index]); + } + fixUpFuncType(entryPoint.entryPointFunc); + } + + void legalizeEntryPoint(EntryPointInfo entryPoint) + { + // If the entrypoint is receiving varying inputs as a pointer, turn it into a value. + depointerizeInputParams(entryPoint.entryPointFunc); + + // TODO FIXME: Enable these for WGSL and remove the `hoistParemeters` member field. + // WGSL entry point legalization currently only applies attributes to struct parameters, + // apply the same hoisting from Metal to WGSL to fix it. + if (hoistParameters) + { + hoistEntryPointParameterFromStruct(entryPoint); + packStageInParameters(entryPoint); + } + + // Input Parameter Legalize + flattenInputParameters(entryPoint); + + // System Value Legalize + legalizeSystemValueParameters(entryPoint); + + // Output Value Legalize + wrapReturnValueInStruct(entryPoint); + + + // Other Legalize + switch (entryPoint.entryPointDecor->getProfile().getStage()) + { + case Stage::Amplification: + legalizeAmplificationStageEntryPoint(entryPoint); + break; + case Stage::Mesh: + legalizeMeshStageEntryPoint(entryPoint); + break; + default: + break; + } + } +}; + +class LegalizeMetalEntryPointContext : public LegalizeShaderEntryPointContext +{ +public: + LegalizeMetalEntryPointContext(IRModule* module, DiagnosticSink* sink) + : LegalizeShaderEntryPointContext(module, sink, true) + { + generatePermittedTypes_sv_target(); + } + +protected: + SystemValueInfo getSystemValueInfo( + String inSemanticName, + String* optionalSemanticIndex, + IRInst* parentVar) const SLANG_OVERRIDE + { + IRBuilder builder(m_module); + SystemValueInfo result = {}; + UnownedStringSlice semanticName; + UnownedStringSlice semanticIndex; + + auto hasExplicitIndex = + splitNameAndIndex(inSemanticName.getUnownedSlice(), semanticName, semanticIndex); + if (!hasExplicitIndex && optionalSemanticIndex) + semanticIndex = optionalSemanticIndex->getUnownedSlice(); + + result.systemValueNameEnum = convertSystemValueSemanticNameToEnum(semanticName); + + switch (result.systemValueNameEnum) + { + case SystemValueSemanticName::Position: + { + result.systemValueName = toSlice("position"); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::Float), + builder.getIntValue(builder.getIntType(), 4))); + break; + } + case SystemValueSemanticName::ClipDistance: + { + result.isSpecial = true; + break; + } + case SystemValueSemanticName::CullDistance: + { + result.isSpecial = true; + break; + } + case SystemValueSemanticName::Coverage: + { + result.systemValueName = toSlice("sample_mask"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + break; + } + case SystemValueSemanticName::InnerCoverage: + { + result.isSpecial = true; + break; + } + case SystemValueSemanticName::Depth: + { + result.systemValueName = toSlice("depth(any)"); + result.permittedTypes.add(builder.getBasicType(BaseType::Float)); + break; + } + case SystemValueSemanticName::DepthGreaterEqual: + { + result.systemValueName = toSlice("depth(greater)"); + result.permittedTypes.add(builder.getBasicType(BaseType::Float)); + break; + } + case SystemValueSemanticName::DepthLessEqual: + { + result.systemValueName = toSlice("depth(less)"); + result.permittedTypes.add(builder.getBasicType(BaseType::Float)); + break; + } + case SystemValueSemanticName::DispatchThreadID: + { + result.systemValueName = toSlice("thread_position_in_grid"); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::UInt), + builder.getIntValue(builder.getIntType(), 3))); + break; + } + case SystemValueSemanticName::DomainLocation: + { + result.systemValueName = toSlice("position_in_patch"); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::Float), + builder.getIntValue(builder.getIntType(), 3))); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::Float), + builder.getIntValue(builder.getIntType(), 2))); + break; + } + case SystemValueSemanticName::GroupID: + { + result.systemValueName = toSlice("threadgroup_position_in_grid"); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::UInt), + builder.getIntValue(builder.getIntType(), 3))); + break; + } + case SystemValueSemanticName::GroupIndex: + { + result.isSpecial = true; + break; + } + case SystemValueSemanticName::GroupThreadID: + { + result.systemValueName = toSlice("thread_position_in_threadgroup"); + result.permittedTypes.add(getGroupThreadIdType(builder)); + break; + } + case SystemValueSemanticName::GSInstanceID: + { + result.isUnsupported = true; + break; + } + case SystemValueSemanticName::InstanceID: + { + result.systemValueName = toSlice("instance_id"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + break; + } + case SystemValueSemanticName::IsFrontFace: + { + result.systemValueName = toSlice("front_facing"); + result.permittedTypes.add(builder.getBasicType(BaseType::Bool)); + break; + } + case SystemValueSemanticName::OutputControlPointID: + { + // In metal, a hull shader is just a compute shader. + // This needs to be handled separately, by lowering into an ordinary buffer. + break; + } + case SystemValueSemanticName::PointSize: + { + result.systemValueName = toSlice("point_size"); + result.permittedTypes.add(builder.getBasicType(BaseType::Float)); + break; + } + case SystemValueSemanticName::PrimitiveID: + { + result.systemValueName = toSlice("primitive_id"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt16)); + break; + } + case SystemValueSemanticName::RenderTargetArrayIndex: + { + result.systemValueName = toSlice("render_target_array_index"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt16)); + break; + } + case SystemValueSemanticName::SampleIndex: + { + result.systemValueName = toSlice("sample_id"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + break; + } + case SystemValueSemanticName::StencilRef: + { + result.systemValueName = toSlice("stencil"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + break; + } + case SystemValueSemanticName::TessFactor: + { + // Tessellation factor outputs should be lowered into a write into a normal buffer. + break; + } + case SystemValueSemanticName::VertexID: + { + result.systemValueName = toSlice("vertex_id"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + break; + } + case SystemValueSemanticName::ViewID: + { + result.isUnsupported = true; + break; + } + case SystemValueSemanticName::ViewportArrayIndex: + { + result.systemValueName = toSlice("viewport_array_index"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt16)); + break; + } + case SystemValueSemanticName::Target: + { + result.systemValueName = + (StringBuilder() + << "color(" << (semanticIndex.getLength() != 0 ? semanticIndex : toSlice("0")) + << ")") + .produceString(); + result.permittedTypes = permittedTypes_sv_target; + + break; + } + case SystemValueSemanticName::StartVertexLocation: + { + result.systemValueName = toSlice("base_vertex"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + break; + } + case SystemValueSemanticName::StartInstanceLocation: + { + result.systemValueName = toSlice("base_instance"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + break; + } + default: + m_sink->diagnose( + parentVar, + Diagnostics::unimplementedSystemValueSemantic, + semanticName); + return result; + } + return result; + } + + + List collectSystemValFromEntryPoint( + EntryPointInfo entryPoint) const SLANG_OVERRIDE + { + List systemValWorkItems; + for (auto param : entryPoint.entryPointFunc->getParams()) + { + auto maybeWorkItem = tryToMakeSystemValWorkItem(param, param->getFullType()); + if (maybeWorkItem.has_value()) + systemValWorkItems.add(std::move(maybeWorkItem.value())); + } + + return systemValWorkItems; + } + + void flattenNestedStructsTransferKeyDecorations(IRInst* newKey, IRInst* oldKey) const + SLANG_OVERRIDE + { + copyNameHintAndDebugDecorations(newKey, oldKey); + } + + UnownedStringSlice getUserSemanticNameSlice(String& loweredName, bool isUserSemantic) const + SLANG_OVERRIDE + { + SLANG_UNUSED(isUserSemantic); + return loweredName.getUnownedSlice(); + }; + + void addFragmentShaderReturnValueDecoration(IRBuilder& builder, IRInst* returnValueStructKey) + const SLANG_OVERRIDE + { + builder.addTargetSystemValueDecoration(returnValueStructKey, toSlice("color(0)")); + } + + IRVarLayout* handleGeometryStageParameterVarLayout( + IRBuilder& builder, + IRVarLayout* paramVarLayout) const SLANG_OVERRIDE + { + // For Metal geometric stages, we need to translate VaryingInput offsets to + // MetalAttribute offsets. + IRVarLayout::Builder elementVarLayoutBuilder(&builder, paramVarLayout->getTypeLayout()); + elementVarLayoutBuilder.cloneEverythingButOffsetsFrom(paramVarLayout); + for (auto offsetAttr : paramVarLayout->getOffsetAttrs()) + { + auto resourceKind = offsetAttr->getResourceKind(); + if (resourceKind == LayoutResourceKind::VaryingInput) + { + resourceKind = LayoutResourceKind::MetalAttribute; + } + auto resInfo = elementVarLayoutBuilder.findOrAddResourceInfo(resourceKind); + resInfo->offset = offsetAttr->getOffset(); + resInfo->space = offsetAttr->getSpace(); + } + + return elementVarLayoutBuilder.build(); + } + + void handleSpecialSystemValue( + const EntryPointInfo& entryPoint, + SystemValLegalizationWorkItem& workItem, + const SystemValueInfo& info, + IRBuilder& builder) SLANG_OVERRIDE + { + const auto var = workItem.var; + + if (info.systemValueNameEnum == SystemValueSemanticName::InnerCoverage) + { + // Metal does not support conservative rasterization, so this is always false. + auto val = builder.getBoolValue(false); + var->replaceUsesWith(val); + var->removeAndDeallocate(); + } + else if (info.systemValueNameEnum == SystemValueSemanticName::GroupIndex) + { + // Ensure we have a cached "sv_groupthreadid" in our entry point + if (!entryPointToGroupThreadId.containsKey(entryPoint.entryPointFunc)) + { + auto systemValWorkItems = collectSystemValFromEntryPoint(entryPoint); + for (auto i : systemValWorkItems) + { + auto indexAsStringGroupThreadId = String(i.attrIndex); + if (getSystemValueInfo(i.attrName, &indexAsStringGroupThreadId, i.var) + .systemValueNameEnum == SystemValueSemanticName::GroupThreadID) + { + entryPointToGroupThreadId[entryPoint.entryPointFunc] = i.var; + } + } + if (!entryPointToGroupThreadId.containsKey(entryPoint.entryPointFunc)) + { + // Add the missing groupthreadid needed to compute sv_groupindex + IRBuilder groupThreadIdBuilder(builder); + groupThreadIdBuilder.setInsertInto(entryPoint.entryPointFunc->getFirstBlock()); + auto groupThreadId = groupThreadIdBuilder.emitParamAtHead( + getGroupThreadIdType(groupThreadIdBuilder)); + entryPointToGroupThreadId[entryPoint.entryPointFunc] = groupThreadId; + groupThreadIdBuilder.addNameHintDecoration(groupThreadId, groupThreadIDString); + + // Since "sv_groupindex" will be translated out to a global var and no + // longer be considered a system value we can reuse its layout and + // semantic info + Index foundRequiredDecorations = 0; + IRLayoutDecoration* layoutDecoration = nullptr; + UInt semanticIndex = 0; + for (auto decoration : var->getDecorations()) + { + if (auto layoutDecorationTmp = as(decoration)) + { + layoutDecoration = layoutDecorationTmp; + foundRequiredDecorations++; + } + else if (auto semanticDecoration = as(decoration)) + { + semanticIndex = semanticDecoration->getSemanticIndex(); + groupThreadIdBuilder.addSemanticDecoration( + groupThreadId, + groupThreadIDString, + (int)semanticIndex); + foundRequiredDecorations++; + } + if (foundRequiredDecorations >= 2) + break; + } + SLANG_ASSERT(layoutDecoration); + layoutDecoration->removeFromParent(); + layoutDecoration->insertAtStart(groupThreadId); + SystemValLegalizationWorkItem newWorkItem = { + groupThreadId, + groupThreadId->getFullType(), + groupThreadIDString, + semanticIndex}; + legalizeSystemValue(entryPoint, newWorkItem); + } + } + + IRBuilder svBuilder(builder.getModule()); + svBuilder.setInsertBefore(entryPoint.entryPointFunc->getFirstOrdinaryInst()); + auto uint3Type = builder.getVectorType( + builder.getUIntType(), + builder.getIntValue(builder.getIntType(), 3)); + auto computeExtent = + emitCalcGroupExtents(svBuilder, entryPoint.entryPointFunc, uint3Type); + if (!computeExtent) + { + m_sink->diagnose( + entryPoint.entryPointFunc, + Diagnostics::unsupportedSpecializationConstantForNumThreads); + + // Fill in placeholder values. + static const int kAxisCount = 3; + IRInst* groupExtentAlongAxis[kAxisCount] = {}; + for (int axis = 0; axis < kAxisCount; axis++) + groupExtentAlongAxis[axis] = + builder.getIntValue(uint3Type->getElementType(), 1); + computeExtent = builder.emitMakeVector(uint3Type, kAxisCount, groupExtentAlongAxis); + } + auto groupIndexCalc = emitCalcGroupIndex( + svBuilder, + entryPointToGroupThreadId[entryPoint.entryPointFunc], + computeExtent); + svBuilder.addNameHintDecoration(groupIndexCalc, UnownedStringSlice("sv_groupindex")); + + var->replaceUsesWith(groupIndexCalc); + var->removeAndDeallocate(); + } + } + + void legalizeAmplificationStageEntryPoint(const EntryPointInfo& entryPoint) const SLANG_OVERRIDE + { + // Find out DispatchMesh function + IRGlobalValueWithCode* dispatchMeshFunc = nullptr; + for (const auto globalInst : entryPoint.entryPointFunc->getModule()->getGlobalInsts()) + { + if (const auto func = as(globalInst)) + { + if (const auto dec = func->findDecoration()) + { + if (dec->getName() == "DispatchMesh") + { + SLANG_ASSERT(!dispatchMeshFunc && "Multiple DispatchMesh functions found"); + dispatchMeshFunc = func; + } + } + } + } + + if (!dispatchMeshFunc) + return; + + IRBuilder builder{entryPoint.entryPointFunc->getModule()}; + + // We'll rewrite the call to use mesh_grid_properties.set_threadgroups_per_grid + traverseUses( + dispatchMeshFunc, + [&](const IRUse* use) + { + if (const auto call = as(use->getUser())) + { + SLANG_ASSERT(call->getArgCount() == 4); + const auto payload = call->getArg(3); + + const auto payloadPtrType = + composeGetters(payload, &IRInst::getDataType); + SLANG_ASSERT(payloadPtrType); + const auto payloadType = payloadPtrType->getValueType(); + SLANG_ASSERT(payloadType); + + builder.setInsertBefore( + entryPoint.entryPointFunc->getFirstBlock()->getFirstOrdinaryInst()); + const auto annotatedPayloadType = builder.getPtrType( + kIROp_RefType, + payloadPtrType->getValueType(), + AddressSpace::MetalObjectData); + auto packedParam = builder.emitParam(annotatedPayloadType); + builder.addExternCppDecoration(packedParam, toSlice("_slang_mesh_payload")); + IRVarLayout::Builder varLayoutBuilder( + &builder, + IRTypeLayout::Builder{&builder}.build()); + + // Add the MetalPayload resource info, so we can emit [[payload]] + varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::MetalPayload); + auto paramVarLayout = varLayoutBuilder.build(); + builder.addLayoutDecoration(packedParam, paramVarLayout); + + // Now we replace the call to DispatchMesh with a call to the mesh grid + // properties But first we need to create the parameter + const auto meshGridPropertiesType = builder.getMetalMeshGridPropertiesType(); + auto mgp = builder.emitParam(meshGridPropertiesType); + builder.addExternCppDecoration(mgp, toSlice("_slang_mgp")); + } + }); + } + + void legalizeMeshStageEntryPoint(const EntryPointInfo& entryPoint) const SLANG_OVERRIDE + { + auto func = entryPoint.entryPointFunc; + + IRBuilder builder{func->getModule()}; + for (auto param : func->getParams()) + { + if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) + { + IRVarLayout::Builder varLayoutBuilder( + &builder, + IRTypeLayout::Builder{&builder}.build()); + + varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::MetalPayload); + auto paramVarLayout = varLayoutBuilder.build(); + builder.addLayoutDecoration(param, paramVarLayout); + + IRPtrTypeBase* type = as(param->getDataType()); + + const auto annotatedPayloadType = builder.getPtrType( + kIROp_ConstRefType, + type->getValueType(), + AddressSpace::MetalObjectData); + + param->setFullType(annotatedPayloadType); + } + } + IROutputTopologyDecoration* outputDeco = + entryPoint.entryPointFunc->findDecoration(); + if (outputDeco == nullptr) + { + SLANG_UNEXPECTED("Mesh shader output decoration missing"); + return; + } + const auto topology = outputDeco->getTopology(); + const auto topStr = topology->getStringSlice(); + UInt topologyEnum = 0; + if (topStr.caseInsensitiveEquals(toSlice("point"))) + { + topologyEnum = 1; + } + else if (topStr.caseInsensitiveEquals(toSlice("line"))) + { + topologyEnum = 2; + } + else if (topStr.caseInsensitiveEquals(toSlice("triangle"))) + { + topologyEnum = 3; + } + else + { + SLANG_UNEXPECTED("unknown topology"); + return; + } + + IRInst* topologyConst = builder.getIntValue(builder.getIntType(), topologyEnum); + + IRType* vertexType = nullptr; + IRType* indicesType = nullptr; + IRType* primitiveType = nullptr; + + IRInst* maxVertices = nullptr; + IRInst* maxPrimitives = nullptr; + + IRInst* verticesParam = nullptr; + IRInst* indicesParam = nullptr; + IRInst* primitivesParam = nullptr; + for (auto param : func->getParams()) + { + if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) + { + IRVarLayout::Builder varLayoutBuilder( + &builder, + IRTypeLayout::Builder{&builder}.build()); + + varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::MetalPayload); + auto paramVarLayout = varLayoutBuilder.build(); + builder.addLayoutDecoration(param, paramVarLayout); + } + if (param->findDecorationImpl(kIROp_VerticesDecoration)) + { + auto vertexRefType = as(param->getDataType()); + auto vertexOutputType = as(vertexRefType->getValueType()); + vertexType = vertexOutputType->getElementType(); + maxVertices = vertexOutputType->getMaxElementCount(); + SLANG_ASSERT(vertexType); + + verticesParam = param; + auto vertStruct = as(vertexType); + for (auto field : vertStruct->getFields()) + { + auto key = field->getKey(); + if (auto deco = key->findDecoration()) + { + if (deco->getSemanticName().caseInsensitiveEquals(toSlice("sv_position"))) + { + builder.addTargetSystemValueDecoration(key, toSlice("position")); + } + } + } + } + if (param->findDecorationImpl(kIROp_IndicesDecoration)) + { + auto indicesRefType = (IRConstRefType*)param->getDataType(); + auto indicesOutputType = (IRIndicesType*)indicesRefType->getValueType(); + indicesType = indicesOutputType->getElementType(); + maxPrimitives = indicesOutputType->getMaxElementCount(); + SLANG_ASSERT(indicesType); + + indicesParam = param; + } + if (param->findDecorationImpl(kIROp_PrimitivesDecoration)) + { + auto primitivesRefType = (IRConstRefType*)param->getDataType(); + auto primitivesOutputType = (IRPrimitivesType*)primitivesRefType->getValueType(); + primitiveType = primitivesOutputType->getElementType(); + SLANG_ASSERT(primitiveType); + + primitivesParam = param; + auto primStruct = as(primitiveType); + for (auto field : primStruct->getFields()) + { + auto key = field->getKey(); + if (auto deco = key->findDecoration()) + { + if (deco->getSemanticName().caseInsensitiveEquals( + toSlice("sv_primitiveid"))) + { + builder.addTargetSystemValueDecoration(key, toSlice("primitive_id")); + } + } + } + } + } + if (primitiveType == nullptr) + { + primitiveType = builder.getVoidType(); + } + builder.setInsertBefore(entryPoint.entryPointFunc->getFirstBlock()->getFirstOrdinaryInst()); + + auto meshParam = builder.emitParam(builder.getMetalMeshType( + vertexType, + primitiveType, + maxVertices, + maxPrimitives, + topologyConst)); + builder.addExternCppDecoration(meshParam, toSlice("_slang_mesh")); + + + verticesParam->removeFromParent(); + verticesParam->removeAndDeallocate(); + + indicesParam->removeFromParent(); + indicesParam->removeAndDeallocate(); + + if (primitivesParam != nullptr) + { + primitivesParam->removeFromParent(); + primitivesParam->removeAndDeallocate(); + } + } + +private: + ShortList permittedTypes_sv_target; + Dictionary entryPointToGroupThreadId; + const UnownedStringSlice groupThreadIDString = UnownedStringSlice("sv_groupthreadid"); + + static IRType* getGroupThreadIdType(IRBuilder& builder) + { + return builder.getVectorType( + builder.getBasicType(BaseType::UInt), + builder.getIntValue(builder.getIntType(), 3)); + } + + void generatePermittedTypes_sv_target() + { + IRBuilder builder(m_module); + permittedTypes_sv_target.reserveOverflowBuffer(5 * 4); + if (permittedTypes_sv_target.getCount() == 0) + { + for (auto baseType : + {BaseType::Float, + BaseType::Half, + BaseType::Int, + BaseType::UInt, + BaseType::Int16, + BaseType::UInt16}) + { + for (IRIntegerValue i = 1; i <= 4; i++) + { + permittedTypes_sv_target.add( + builder.getVectorType(builder.getBasicType(baseType), i)); + } + } + } + } +}; + + +class LegalizeWGSLEntryPointContext : public LegalizeShaderEntryPointContext +{ +public: + LegalizeWGSLEntryPointContext(IRModule* module, DiagnosticSink* sink) + : LegalizeShaderEntryPointContext(module, sink, false) + { + } + +protected: + SystemValueInfo getSystemValueInfo( + String inSemanticName, + String* optionalSemanticIndex, + IRInst* parentVar) const SLANG_OVERRIDE + { + IRBuilder builder(m_module); + SystemValueInfo result = {}; + UnownedStringSlice semanticName; + UnownedStringSlice semanticIndex; + + auto hasExplicitIndex = + splitNameAndIndex(inSemanticName.getUnownedSlice(), semanticName, semanticIndex); + if (!hasExplicitIndex && optionalSemanticIndex) + semanticIndex = optionalSemanticIndex->getUnownedSlice(); + + result.systemValueNameEnum = convertSystemValueSemanticNameToEnum(semanticName); + + switch (result.systemValueNameEnum) + { + + case SystemValueSemanticName::CullDistance: + { + result.isUnsupported = true; + } + break; + + case SystemValueSemanticName::ClipDistance: + { + // TODO: Implement this based on the 'clip-distances' feature in WGSL + // https: // www.w3.org/TR/webgpu/#dom-gpufeaturename-clip-distances + result.isUnsupported = true; + } + break; + + case SystemValueSemanticName::Coverage: + { + result.systemValueName = toSlice("sample_mask"); + result.permittedTypes.add(builder.getUIntType()); + } + break; + + case SystemValueSemanticName::Depth: + { + result.systemValueName = toSlice("frag_depth"); + result.permittedTypes.add(builder.getBasicType(BaseType::Float)); + } + break; + + case SystemValueSemanticName::DepthGreaterEqual: + case SystemValueSemanticName::DepthLessEqual: + { + result.isUnsupported = true; + } + break; + + case SystemValueSemanticName::DispatchThreadID: + { + result.systemValueName = toSlice("global_invocation_id"); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::UInt), + builder.getIntValue(builder.getIntType(), 3))); + } + break; + + case SystemValueSemanticName::DomainLocation: + { + result.isUnsupported = true; + } + break; + + case SystemValueSemanticName::GroupID: + { + result.systemValueName = toSlice("workgroup_id"); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::UInt), + builder.getIntValue(builder.getIntType(), 3))); + } + break; + + case SystemValueSemanticName::GroupIndex: + { + result.systemValueName = toSlice("local_invocation_index"); + result.permittedTypes.add(builder.getUIntType()); + } + break; + + case SystemValueSemanticName::GroupThreadID: + { + result.systemValueName = toSlice("local_invocation_id"); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::UInt), + builder.getIntValue(builder.getIntType(), 3))); + } + break; + + case SystemValueSemanticName::GSInstanceID: + { + // No Geometry shaders in WGSL + result.isUnsupported = true; + } + break; + + case SystemValueSemanticName::InnerCoverage: + { + result.isUnsupported = true; + } + break; + + case SystemValueSemanticName::InstanceID: + { + result.systemValueName = toSlice("instance_index"); + result.permittedTypes.add(builder.getUIntType()); + } + break; + + case SystemValueSemanticName::IsFrontFace: + { + result.systemValueName = toSlice("front_facing"); + result.permittedTypes.add(builder.getBoolType()); + } + break; + + case SystemValueSemanticName::OutputControlPointID: + case SystemValueSemanticName::PointSize: + { + result.isUnsupported = true; + } + break; + + case SystemValueSemanticName::Position: + { + result.systemValueName = toSlice("position"); + result.permittedTypes.add(builder.getVectorType( + builder.getBasicType(BaseType::Float), + builder.getIntValue(builder.getIntType(), 4))); + break; + } + + case SystemValueSemanticName::PrimitiveID: + case SystemValueSemanticName::RenderTargetArrayIndex: + { + result.isUnsupported = true; + break; + } + + case SystemValueSemanticName::SampleIndex: + { + result.systemValueName = toSlice("sample_index"); + result.permittedTypes.add(builder.getUIntType()); + break; + } + + case SystemValueSemanticName::StencilRef: + case SystemValueSemanticName::Target: + case SystemValueSemanticName::TessFactor: + { + result.isUnsupported = true; + break; + } + + case SystemValueSemanticName::VertexID: + { + result.systemValueName = toSlice("vertex_index"); + result.permittedTypes.add(builder.getUIntType()); + break; + } + + case SystemValueSemanticName::ViewID: + case SystemValueSemanticName::ViewportArrayIndex: + case SystemValueSemanticName::StartVertexLocation: + case SystemValueSemanticName::StartInstanceLocation: + { + result.isUnsupported = true; + break; + } + + default: + { + m_sink->diagnose( + parentVar, + Diagnostics::unimplementedSystemValueSemantic, + semanticName); + return result; + } + } + + return result; + } + void flattenNestedStructsTransferKeyDecorations(IRInst* newKey, IRInst* oldKey) const + SLANG_OVERRIDE + { + oldKey->transferDecorationsTo(newKey); + } + + UnownedStringSlice getUserSemanticNameSlice(String& loweredName, bool isUserSemantic) const + SLANG_OVERRIDE + { + return isUserSemantic ? userSemanticName : loweredName.getUnownedSlice(); + } + + void addFragmentShaderReturnValueDecoration(IRBuilder& builder, IRInst* returnValueStructKey) + const SLANG_OVERRIDE + { + IRInst* operands[] = { + builder.getStringValue(userSemanticName), + builder.getIntValue(builder.getIntType(), 0)}; + builder.addDecoration( + returnValueStructKey, + kIROp_SemanticDecoration, + operands, + SLANG_COUNT_OF(operands)); + }; + + List collectSystemValFromEntryPoint( + EntryPointInfo entryPoint) const SLANG_OVERRIDE + { + List systemValWorkItems; + for (auto param : entryPoint.entryPointFunc->getParams()) + { + if (auto structType = as(param->getDataType())) + { + for (auto field : structType->getFields()) + { + // Nested struct-s are flattened already by flattenInputParameters(). + SLANG_ASSERT(!as(field->getFieldType())); + + auto key = field->getKey(); + auto fieldType = field->getFieldType(); + auto maybeWorkItem = tryToMakeSystemValWorkItem(key, fieldType); + if (maybeWorkItem.has_value()) + systemValWorkItems.add(std::move(maybeWorkItem.value())); + } + continue; + } + + auto maybeWorkItem = tryToMakeSystemValWorkItem(param, param->getFullType()); + if (maybeWorkItem.has_value()) + systemValWorkItems.add(std::move(maybeWorkItem.value())); + } + + return systemValWorkItems; + } + +private: + const UnownedStringSlice userSemanticName = toSlice("user_semantic"); +}; + +void legalizeEntryPointVaryingParamsForMetal( + IRModule* module, + DiagnosticSink* sink, + List& entryPoints) +{ + LegalizeMetalEntryPointContext context(module, sink); + context.legalizeEntryPoints(entryPoints); +} + +void legalizeEntryPointVaryingParamsForWGSL( + IRModule* module, + DiagnosticSink* sink, + List& entryPoints) +{ + LegalizeWGSLEntryPointContext context(module, sink); + context.legalizeEntryPoints(entryPoints); +} + } // namespace Slang diff --git a/source/slang/slang-ir-legalize-varying-params.h b/source/slang/slang-ir-legalize-varying-params.h index efd61e87cf..e742f30936 100644 --- a/source/slang/slang-ir-legalize-varying-params.h +++ b/source/slang/slang-ir-legalize-varying-params.h @@ -14,19 +14,27 @@ struct IRVectorType; struct IRBuilder; struct IREntryPointDecoration; +struct EntryPointInfo +{ + IRFunc* entryPointFunc; + IREntryPointDecoration* entryPointDecor; +}; + void legalizeEntryPointVaryingParamsForCPU(IRModule* module, DiagnosticSink* sink); void legalizeEntryPointVaryingParamsForCUDA(IRModule* module, DiagnosticSink* sink); -void depointerizeInputParams(IRFunc* entryPoint); - -// (#4375) Once `slang-ir-metal-legalize.cpp` is merged with -// `slang-ir-legalize-varying-params.cpp`, move the following -// below into `slang-ir-legalize-varying-params.cpp` as well +void legalizeEntryPointVaryingParamsForMetal( + IRModule* module, + DiagnosticSink* sink, + List& entryPoints); -IRInst* emitCalcGroupExtents(IRBuilder& builder, IRFunc* entryPoint, IRVectorType* type); +void legalizeEntryPointVaryingParamsForWGSL( + IRModule* module, + DiagnosticSink* sink, + List& entryPoints); -IRInst* emitCalcGroupIndex(IRBuilder& builder, IRInst* groupThreadID, IRInst* groupExtents); +void depointerizeInputParams(IRFunc* entryPoint); // SystemValueSemanticName member definition macro #define SYSTEM_VALUE_SEMANTIC_NAMES(M) \ diff --git a/source/slang/slang-ir-metal-legalize.cpp b/source/slang/slang-ir-metal-legalize.cpp index 3b47bd59ef..0d58bdd14c 100644 --- a/source/slang/slang-ir-metal-legalize.cpp +++ b/source/slang/slang-ir-metal-legalize.cpp @@ -7,1964 +7,10 @@ #include "slang-ir-specialize-address-space.h" #include "slang-ir-util.h" #include "slang-ir.h" -#include "slang-parameter-binding.h" - -#include namespace Slang { -struct EntryPointInfo -{ - IRFunc* entryPointFunc; - IREntryPointDecoration* entryPointDecor; -}; - -const UnownedStringSlice groupThreadIDString = UnownedStringSlice("sv_groupthreadid"); -struct LegalizeMetalEntryPointContext -{ - ShortList permittedTypes_sv_target; - Dictionary entryPointToGroupThreadId; - HashSet semanticInfoToRemove; - - DiagnosticSink* m_sink; - IRModule* m_module; - - LegalizeMetalEntryPointContext(DiagnosticSink* sink, IRModule* module) - : m_sink(sink), m_module(module) - { - } - - void removeSemanticLayoutsFromLegalizedStructs() - { - // Metal does not allow duplicate attributes to appear in the same shader. - // If we emit our own struct with `[[color(0)]`, all existing uses of `[[color(0)]]` - // must be removed. - for (auto field : semanticInfoToRemove) - { - auto key = field->getKey(); - // Some decorations appear twice, destroy all found - for (;;) - { - if (auto semanticDecor = key->findDecoration()) - { - semanticDecor->removeAndDeallocate(); - continue; - } - else if (auto layoutDecor = key->findDecoration()) - { - layoutDecor->removeAndDeallocate(); - continue; - } - break; - } - } - } - - void hoistEntryPointParameterFromStruct(EntryPointInfo entryPoint) - { - // If an entry point has a input parameter with a struct type, we want to hoist out - // all the fields of the struct type to be individual parameters of the entry point. - // This will canonicalize the entry point signature, so we can handle all cases uniformly. - - // For example, given an entry point: - // ``` - // struct VertexInput { float3 pos; float 2 uv; int vertexId : SV_VertexID}; - // void main(VertexInput vin) { ... } - // ``` - // We will transform it to: - // ``` - // void main(float3 pos, float2 uv, int vertexId : SV_VertexID) { - // VertexInput vin = {pos,uv,vertexId}; - // ... - // } - // ``` - - auto func = entryPoint.entryPointFunc; - List paramsToProcess; - for (auto param : func->getParams()) - { - if (as(param->getDataType())) - { - paramsToProcess.add(param); - } - } - - IRBuilder builder(func); - builder.setInsertBefore(func); - for (auto param : paramsToProcess) - { - auto structType = as(param->getDataType()); - builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); - auto varLayout = findVarLayout(param); - - // If `param` already has a semantic, we don't want to hoist its fields out. - if (varLayout->findSystemValueSemanticAttr() != nullptr || - param->findDecoration()) - continue; - - IRStructTypeLayout* structTypeLayout = nullptr; - if (varLayout) - structTypeLayout = as(varLayout->getTypeLayout()); - Index fieldIndex = 0; - List fieldParams; - for (auto field : structType->getFields()) - { - auto fieldParam = builder.emitParam(field->getFieldType()); - IRCloneEnv cloneEnv; - cloneInstDecorationsAndChildren( - &cloneEnv, - builder.getModule(), - field->getKey(), - fieldParam); - - IRVarLayout* fieldLayout = - structTypeLayout ? structTypeLayout->getFieldLayout(fieldIndex) : nullptr; - if (varLayout) - { - IRVarLayout::Builder varLayoutBuilder(&builder, fieldLayout->getTypeLayout()); - varLayoutBuilder.cloneEverythingButOffsetsFrom(fieldLayout); - for (auto offsetAttr : fieldLayout->getOffsetAttrs()) - { - auto parentOffsetAttr = - varLayout->findOffsetAttr(offsetAttr->getResourceKind()); - UInt parentOffset = parentOffsetAttr ? parentOffsetAttr->getOffset() : 0; - UInt parentSpace = parentOffsetAttr ? parentOffsetAttr->getSpace() : 0; - auto resInfo = - varLayoutBuilder.findOrAddResourceInfo(offsetAttr->getResourceKind()); - resInfo->offset = parentOffset + offsetAttr->getOffset(); - resInfo->space = parentSpace + offsetAttr->getSpace(); - } - builder.addLayoutDecoration(fieldParam, varLayoutBuilder.build()); - } - param->insertBefore(fieldParam); - fieldParams.add(fieldParam); - fieldIndex++; - } - builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); - auto reconstructedParam = - builder.emitMakeStruct(structType, fieldParams.getCount(), fieldParams.getBuffer()); - param->replaceUsesWith(reconstructedParam); - param->removeFromParent(); - } - fixUpFuncType(func); - } - - // Flattens all struct parameters of an entryPoint to ensure parameters are a flat struct - void flattenInputParameters(EntryPointInfo entryPoint) - { - // Goal is to ensure we have a flattened IRParam (0 nested IRStructType members). - /* - // Assume the following code - struct NestedFragment - { - float2 p3; - }; - struct Fragment - { - float4 p1; - float3 p2; - NestedFragment p3_nested; - }; - - // Fragment flattens into - struct Fragment - { - float4 p1; - float3 p2; - float2 p3; - }; - */ - - // This is important since Metal does not allow semantic's on a struct - /* - // Assume the following code - struct NestedFragment1 - { - float2 p3; - }; - struct Fragment1 - { - float4 p1 : SV_TARGET0; - float3 p2 : SV_TARGET1; - NestedFragment p3_nested : SV_TARGET2; // error, semantic on struct - }; - - */ - - // Metal does allow semantics on members of a nested struct but we are avoiding this - // approach since there are senarios where legalization (and verification) is - // hard/expensive without creating a flat struct: - // 1. Entry points may share structs, semantics may be inconsistent across entry points - // 2. Multiple of the same struct may be used in a param list - /* - // Assume the following code - struct NestedFragment - { - float2 p3; - }; - struct Fragment - { - float4 p1 : SV_TARGET0; - NestedFragment p2 : SV_TARGET1; - NestedFragment p3 : SV_TARGET2; - }; - - // Legalized without flattening -- abandoned - struct NestedFragment1 - { - float2 p3 : SV_TARGET1; - }; - struct NestedFragment2 - { - float2 p3 : SV_TARGET2; - }; - struct Fragment - { - float4 p1 : SV_TARGET0; - NestedFragment1 p2; - NestedFragment2 p3; - }; - - // Legalized with flattening -- current approach - struct Fragment - { - float4 p1 : SV_TARGET0; - float2 p2 : SV_TARGET1; - float2 p3 : SV_TARGET2; - }; - */ - - auto func = entryPoint.entryPointFunc; - bool modified = false; - for (auto param : func->getParams()) - { - auto layout = findVarLayout(param); - if (!layout) - continue; - if (!layout->findOffsetAttr(LayoutResourceKind::VaryingInput)) - continue; - if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) - continue; - // If we find a IRParam with a IRStructType member, we need to flatten the entire - // IRParam - if (auto structType = as(param->getDataType())) - { - IRBuilder builder(func); - MapStructToFlatStruct mapOldFieldToNewField; - - // Flatten struct if we have nested IRStructType - auto flattenedStruct = maybeFlattenNestedStructs( - builder, - structType, - mapOldFieldToNewField, - semanticInfoToRemove); - if (flattenedStruct != structType) - { - // Validate/rearange all semantics which overlap in our flat struct - fixFieldSemanticsOfFlatStruct(flattenedStruct); - - // Replace the 'old IRParam type' with a 'new IRParam type' - param->setFullType(flattenedStruct); - - // Emit a new variable at EntryPoint of 'old IRParam type' - builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); - auto dstVal = builder.emitVar(structType); - auto dstLoad = builder.emitLoad(dstVal); - param->replaceUsesWith(dstLoad); - builder.setInsertBefore(dstLoad); - // Copy the 'new IRParam type' to our 'old IRParam type' - mapOldFieldToNewField - .emitCopy<(int)MapStructToFlatStruct::CopyOptions::FlatStructIntoStruct>( - builder, - dstVal, - param); - - modified = true; - } - } - } - if (modified) - fixUpFuncType(func); - } - - void packStageInParameters(EntryPointInfo entryPoint) - { - // If the entry point has any parameters whose layout contains VaryingInput, - // we need to pack those parameters into a single `struct` type, and decorate - // the fields with the appropriate `[[attribute]]` decorations. - // For other parameters that are not `VaryingInput`, we need to leave them as is. - // - // For example, given this code after `hoistEntryPointParameterFromStruct`: - // ``` - // void main(float3 pos, float2 uv, int vertexId : SV_VertexID) { - // VertexInput vin = {pos,uv,vertexId}; - // ... - // } - // ``` - // We are going to transform it into: - // ``` - // struct VertexInput { - // float3 pos [[attribute(0)]]; - // float2 uv [[attribute(1)]]; - // }; - // void main(VertexInput vin, int vertexId : SV_VertexID) { - // let pos = vin.pos; - // let uv = vin.uv; - // ... - // } - - auto func = entryPoint.entryPointFunc; - - bool isGeometryStage = false; - switch (entryPoint.entryPointDecor->getProfile().getStage()) - { - case Stage::Vertex: - case Stage::Amplification: - case Stage::Mesh: - case Stage::Geometry: - case Stage::Domain: - case Stage::Hull: - isGeometryStage = true; - break; - } - - List paramsToPack; - for (auto param : func->getParams()) - { - auto layout = findVarLayout(param); - if (!layout) - continue; - if (!layout->findOffsetAttr(LayoutResourceKind::VaryingInput)) - continue; - if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) - continue; - paramsToPack.add(param); - } - - if (paramsToPack.getCount() == 0) - return; - - IRBuilder builder(func); - builder.setInsertBefore(func); - IRStructType* structType = builder.createStructType(); - auto stageText = getStageText(entryPoint.entryPointDecor->getProfile().getStage()); - builder.addNameHintDecoration( - structType, - (String(stageText) + toSlice("Input")).getUnownedSlice()); - List keys; - IRStructTypeLayout::Builder layoutBuilder(&builder); - for (auto param : paramsToPack) - { - auto paramVarLayout = findVarLayout(param); - auto key = builder.createStructKey(); - param->transferDecorationsTo(key); - builder.createStructField(structType, key, param->getDataType()); - if (auto varyingInOffsetAttr = - paramVarLayout->findOffsetAttr(LayoutResourceKind::VaryingInput)) - { - if (!key->findDecoration() && - !paramVarLayout->findAttr()) - { - // If the parameter doesn't have a semantic, we need to add one for semantic - // matching. - builder.addSemanticDecoration( - key, - toSlice("_slang_attr"), - (int)varyingInOffsetAttr->getOffset()); - } - } - if (isGeometryStage) - { - // For geometric stages, we need to translate VaryingInput offsets to MetalAttribute - // offsets. - IRVarLayout::Builder elementVarLayoutBuilder( - &builder, - paramVarLayout->getTypeLayout()); - elementVarLayoutBuilder.cloneEverythingButOffsetsFrom(paramVarLayout); - for (auto offsetAttr : paramVarLayout->getOffsetAttrs()) - { - auto resourceKind = offsetAttr->getResourceKind(); - if (resourceKind == LayoutResourceKind::VaryingInput) - { - resourceKind = LayoutResourceKind::MetalAttribute; - } - auto resInfo = elementVarLayoutBuilder.findOrAddResourceInfo(resourceKind); - resInfo->offset = offsetAttr->getOffset(); - resInfo->space = offsetAttr->getSpace(); - } - paramVarLayout = elementVarLayoutBuilder.build(); - } - layoutBuilder.addField(key, paramVarLayout); - builder.addLayoutDecoration(key, paramVarLayout); - keys.add(key); - } - builder.setInsertInto(func->getFirstBlock()); - auto packedParam = builder.emitParamAtHead(structType); - auto typeLayout = layoutBuilder.build(); - IRVarLayout::Builder varLayoutBuilder(&builder, typeLayout); - - // Add a VaryingInput resource info to the packed parameter layout, so that we can emit - // the needed `[[stage_in]]` attribute in Metal emitter. - varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::VaryingInput); - auto paramVarLayout = varLayoutBuilder.build(); - builder.addLayoutDecoration(packedParam, paramVarLayout); - - // Replace the original parameters with the packed parameter - builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); - for (Index paramIndex = 0; paramIndex < paramsToPack.getCount(); paramIndex++) - { - auto param = paramsToPack[paramIndex]; - auto key = keys[paramIndex]; - auto paramField = builder.emitFieldExtract(param->getDataType(), packedParam, key); - param->replaceUsesWith(paramField); - param->removeFromParent(); - } - fixUpFuncType(func); - } - - struct MetalSystemValueInfo - { - String metalSystemValueName; - SystemValueSemanticName metalSystemValueNameEnum; - ShortList permittedTypes; - bool isUnsupported = false; - bool isSpecial = false; - MetalSystemValueInfo() - { - // most commonly need 2 - permittedTypes.reserveOverflowBuffer(2); - } - }; - - IRType* getGroupThreadIdType(IRBuilder& builder) - { - return builder.getVectorType( - builder.getBasicType(BaseType::UInt), - builder.getIntValue(builder.getIntType(), 3)); - } - - // Get all permitted types of "sv_target" for Metal - ShortList& getPermittedTypes_sv_target(IRBuilder& builder) - { - permittedTypes_sv_target.reserveOverflowBuffer(5 * 4); - if (permittedTypes_sv_target.getCount() == 0) - { - for (auto baseType : - {BaseType::Float, - BaseType::Half, - BaseType::Int, - BaseType::UInt, - BaseType::Int16, - BaseType::UInt16}) - { - for (IRIntegerValue i = 1; i <= 4; i++) - { - permittedTypes_sv_target.add( - builder.getVectorType(builder.getBasicType(baseType), i)); - } - } - } - return permittedTypes_sv_target; - } - - MetalSystemValueInfo getSystemValueInfo( - String inSemanticName, - String* optionalSemanticIndex, - IRInst* parentVar) - { - IRBuilder builder(m_module); - MetalSystemValueInfo result = {}; - UnownedStringSlice semanticName; - UnownedStringSlice semanticIndex; - - auto hasExplicitIndex = - splitNameAndIndex(inSemanticName.getUnownedSlice(), semanticName, semanticIndex); - if (!hasExplicitIndex && optionalSemanticIndex) - semanticIndex = optionalSemanticIndex->getUnownedSlice(); - - result.metalSystemValueNameEnum = convertSystemValueSemanticNameToEnum(semanticName); - - switch (result.metalSystemValueNameEnum) - { - case SystemValueSemanticName::Position: - { - result.metalSystemValueName = toSlice("position"); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::Float), - builder.getIntValue(builder.getIntType(), 4))); - break; - } - case SystemValueSemanticName::ClipDistance: - { - result.isSpecial = true; - break; - } - case SystemValueSemanticName::CullDistance: - { - result.isSpecial = true; - break; - } - case SystemValueSemanticName::Coverage: - { - result.metalSystemValueName = toSlice("sample_mask"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - break; - } - case SystemValueSemanticName::InnerCoverage: - { - result.isSpecial = true; - break; - } - case SystemValueSemanticName::Depth: - { - result.metalSystemValueName = toSlice("depth(any)"); - result.permittedTypes.add(builder.getBasicType(BaseType::Float)); - break; - } - case SystemValueSemanticName::DepthGreaterEqual: - { - result.metalSystemValueName = toSlice("depth(greater)"); - result.permittedTypes.add(builder.getBasicType(BaseType::Float)); - break; - } - case SystemValueSemanticName::DepthLessEqual: - { - result.metalSystemValueName = toSlice("depth(less)"); - result.permittedTypes.add(builder.getBasicType(BaseType::Float)); - break; - } - case SystemValueSemanticName::DispatchThreadID: - { - result.metalSystemValueName = toSlice("thread_position_in_grid"); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::UInt), - builder.getIntValue(builder.getIntType(), 3))); - break; - } - case SystemValueSemanticName::DomainLocation: - { - result.metalSystemValueName = toSlice("position_in_patch"); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::Float), - builder.getIntValue(builder.getIntType(), 3))); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::Float), - builder.getIntValue(builder.getIntType(), 2))); - break; - } - case SystemValueSemanticName::GroupID: - { - result.metalSystemValueName = toSlice("threadgroup_position_in_grid"); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::UInt), - builder.getIntValue(builder.getIntType(), 3))); - break; - } - case SystemValueSemanticName::GroupIndex: - { - result.isSpecial = true; - break; - } - case SystemValueSemanticName::GroupThreadID: - { - result.metalSystemValueName = toSlice("thread_position_in_threadgroup"); - result.permittedTypes.add(getGroupThreadIdType(builder)); - break; - } - case SystemValueSemanticName::GSInstanceID: - { - result.isUnsupported = true; - break; - } - case SystemValueSemanticName::InstanceID: - { - result.metalSystemValueName = toSlice("instance_id"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - break; - } - case SystemValueSemanticName::IsFrontFace: - { - result.metalSystemValueName = toSlice("front_facing"); - result.permittedTypes.add(builder.getBasicType(BaseType::Bool)); - break; - } - case SystemValueSemanticName::OutputControlPointID: - { - // In metal, a hull shader is just a compute shader. - // This needs to be handled separately, by lowering into an ordinary buffer. - break; - } - case SystemValueSemanticName::PointSize: - { - result.metalSystemValueName = toSlice("point_size"); - result.permittedTypes.add(builder.getBasicType(BaseType::Float)); - break; - } - case SystemValueSemanticName::PrimitiveID: - { - result.metalSystemValueName = toSlice("primitive_id"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt16)); - break; - } - case SystemValueSemanticName::RenderTargetArrayIndex: - { - result.metalSystemValueName = toSlice("render_target_array_index"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt16)); - break; - } - case SystemValueSemanticName::SampleIndex: - { - result.metalSystemValueName = toSlice("sample_id"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - break; - } - case SystemValueSemanticName::StencilRef: - { - result.metalSystemValueName = toSlice("stencil"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - break; - } - case SystemValueSemanticName::TessFactor: - { - // Tessellation factor outputs should be lowered into a write into a normal buffer. - break; - } - case SystemValueSemanticName::VertexID: - { - result.metalSystemValueName = toSlice("vertex_id"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - break; - } - case SystemValueSemanticName::ViewID: - { - result.isUnsupported = true; - break; - } - case SystemValueSemanticName::ViewportArrayIndex: - { - result.metalSystemValueName = toSlice("viewport_array_index"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt16)); - break; - } - case SystemValueSemanticName::Target: - { - result.metalSystemValueName = - (StringBuilder() - << "color(" << (semanticIndex.getLength() != 0 ? semanticIndex : toSlice("0")) - << ")") - .produceString(); - result.permittedTypes = getPermittedTypes_sv_target(builder); - - break; - } - case SystemValueSemanticName::StartVertexLocation: - { - result.metalSystemValueName = toSlice("base_vertex"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - break; - } - case SystemValueSemanticName::StartInstanceLocation: - { - result.metalSystemValueName = toSlice("base_instance"); - result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); - break; - } - default: - m_sink->diagnose( - parentVar, - Diagnostics::unimplementedSystemValueSemantic, - semanticName); - return result; - } - return result; - } - - void reportUnsupportedSystemAttribute(IRInst* param, String semanticName) - { - m_sink->diagnose( - param->sourceLoc, - Diagnostics::systemValueAttributeNotSupported, - semanticName); - } - - void ensureResultStructHasUserSemantic(IRStructType* structType, IRVarLayout* varLayout) - { - // Ensure each field in an output struct type has either a system semantic or a user - // semantic, so that signature matching can happen correctly. - auto typeLayout = as(varLayout->getTypeLayout()); - Index index = 0; - IRBuilder builder(structType); - for (auto field : structType->getFields()) - { - auto key = field->getKey(); - if (auto semanticDecor = key->findDecoration()) - { - if (semanticDecor->getSemanticName().startsWithCaseInsensitive(toSlice("sv_"))) - { - auto indexAsString = String(UInt(semanticDecor->getSemanticIndex())); - auto sysValInfo = - getSystemValueInfo(semanticDecor->getSemanticName(), &indexAsString, field); - if (sysValInfo.isUnsupported || sysValInfo.isSpecial) - { - reportUnsupportedSystemAttribute(field, semanticDecor->getSemanticName()); - } - else - { - builder.addTargetSystemValueDecoration( - key, - sysValInfo.metalSystemValueName.getUnownedSlice()); - semanticDecor->removeAndDeallocate(); - } - } - index++; - continue; - } - typeLayout->getFieldLayout(index); - auto fieldLayout = typeLayout->getFieldLayout(index); - if (auto offsetAttr = fieldLayout->findOffsetAttr(LayoutResourceKind::VaryingOutput)) - { - UInt varOffset = 0; - if (auto varOffsetAttr = - varLayout->findOffsetAttr(LayoutResourceKind::VaryingOutput)) - varOffset = varOffsetAttr->getOffset(); - varOffset += offsetAttr->getOffset(); - builder.addSemanticDecoration(key, toSlice("_slang_attr"), (int)varOffset); - } - index++; - } - } - - // Stores a hicharchy of members and children which map 'oldStruct->member' to - // 'flatStruct->member' Note: this map assumes we map to FlatStruct since it is easier/faster to - // process - struct MapStructToFlatStruct - { - /* - We need a hicharchy map to resolve dependencies for mapping - oldStruct to newStruct efficently. Example: - - MyStruct - | - / | \ - / | \ - / | \ - M0 M1 M2 - | | | - A_0 A_0 B_0 - - Without storing hicharchy information, there will be no way to tell apart - `myStruct.M0.A0` from `myStruct.M1.A0` since IRStructKey/IRStructField - only has 1 instance of `A::A0` - */ - - enum CopyOptions : int - { - // Copy a flattened-struct into a struct - FlatStructIntoStruct = 0, - - // Copy a struct into a flattened-struct - StructIntoFlatStruct = 1, - }; - - private: - // Children of member if applicable. - Dictionary members; - - // Field correlating to MapStructToFlatStruct Node. - IRInst* node; - IRStructKey* getKey() - { - SLANG_ASSERT(as(node)); - return as(node)->getKey(); - } - IRInst* getNode() { return node; } - IRType* getFieldType() - { - SLANG_ASSERT(as(node)); - return as(node)->getFieldType(); - } - - // Whom node maps to inside target flatStruct - IRStructField* targetMapping; - - auto begin() { return members.begin(); } - auto end() { return members.end(); } - - // Copies members of oldStruct to/from newFlatStruct. Assumes members of val1 maps to - // members in val2 using `MapStructToFlatStruct` - template - static void _emitCopy( - IRBuilder& builder, - IRInst* val1, - IRStructType* type1, - IRInst* val2, - IRStructType* type2, - MapStructToFlatStruct& node) - { - for (auto& field1Pair : node) - { - auto& field1 = field1Pair.second; - - // Get member of val1 - IRInst* fieldAddr1 = nullptr; - if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) - { - fieldAddr1 = builder.emitFieldAddress(type1, val1, field1.getKey()); - } - else - { - if (as(val1)) - val1 = builder.emitLoad(val1); - fieldAddr1 = builder.emitFieldExtract(type1, val1, field1.getKey()); - } - - // If val1 is a struct, recurse - if (auto fieldAsStruct1 = as(field1.getFieldType())) - { - _emitCopy( - builder, - fieldAddr1, - fieldAsStruct1, - val2, - type2, - field1); - continue; - } - - // Get member of val2 which maps to val1.member - auto field2 = field1.getMapping(); - SLANG_ASSERT(field2); - IRInst* fieldAddr2 = nullptr; - if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) - { - if (as(val2)) - val2 = builder.emitLoad(val1); - fieldAddr2 = builder.emitFieldExtract(type2, val2, field2->getKey()); - } - else - { - fieldAddr2 = builder.emitFieldAddress(type2, val2, field2->getKey()); - } - - // Copy val2/val1 member into val1/val2 member - if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) - { - builder.emitStore(fieldAddr1, fieldAddr2); - } - else - { - builder.emitStore(fieldAddr2, fieldAddr1); - } - } - } - - public: - void setNode(IRInst* newNode) { node = newNode; } - // Get 'MapStructToFlatStruct' that is a child of 'parent'. - // Make 'MapStructToFlatStruct' if no 'member' is currently mapped to 'parent'. - MapStructToFlatStruct& getMember(IRStructField* member) { return members[member]; } - MapStructToFlatStruct& operator[](IRStructField* member) { return getMember(member); } - - void setMapping(IRStructField* newTargetMapping) { targetMapping = newTargetMapping; } - // Get 'MapStructToFlatStruct' that is a child of 'parent'. - // Return nullptr if no member is mapped to 'parent' - IRStructField* getMapping() { return targetMapping; } - - // Copies srcVal into dstVal using hicharchy map. - template - void emitCopy(IRBuilder& builder, IRInst* dstVal, IRInst* srcVal) - { - auto dstType = dstVal->getDataType(); - if (auto dstPtrType = as(dstType)) - dstType = dstPtrType->getValueType(); - auto dstStructType = as(dstType); - SLANG_ASSERT(dstStructType); - - auto srcType = srcVal->getDataType(); - if (auto srcPtrType = as(srcType)) - srcType = srcPtrType->getValueType(); - auto srcStructType = as(srcType); - SLANG_ASSERT(srcStructType); - - if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) - { - // CopyOptions::FlatStructIntoStruct copy a flattened-struct (mapped member) into a - // struct - SLANG_ASSERT(node == dstStructType); - _emitCopy( - builder, - dstVal, - dstStructType, - srcVal, - srcStructType, - *this); - } - else - { - // CopyOptions::StructIntoFlatStruct copy a struct into a flattened-struct - SLANG_ASSERT(node == srcStructType); - _emitCopy( - builder, - srcVal, - srcStructType, - dstVal, - dstStructType, - *this); - } - } - }; - - IRStructType* _flattenNestedStructs( - IRBuilder& builder, - IRStructType* dst, - IRStructType* src, - IRSemanticDecoration* parentSemanticDecoration, - IRLayoutDecoration* parentLayout, - MapStructToFlatStruct& mapFieldToField, - HashSet& varsWithSemanticInfo) - { - // For all fields ('oldField') of a struct do the following: - // 1. Check for 'decorations which carry semantic info' (IRSemanticDecoration, - // IRLayoutDecoration), store these if found. - // * Do not propagate semantic info if the current node has *any* form of semantic - // information. - // Update varsWithSemanticInfo. - // 2. If IRStructType: - // 2a. Recurse this function with 'decorations that carry semantic info' from parent. - // 3. If not IRStructType: - // 3a. Emit 'newField' equal to 'oldField', add 'decorations which carry semantic info'. - // 3b. Store a mapping from 'oldField' to 'newField' in 'mapFieldToField'. This info is - // needed to copy between types. - for (auto oldField : src->getFields()) - { - auto& fieldMappingNode = mapFieldToField[oldField]; - fieldMappingNode.setNode(oldField); - - // step 1 - bool foundSemanticDecor = false; - auto oldKey = oldField->getKey(); - IRSemanticDecoration* fieldSemanticDecoration = parentSemanticDecoration; - if (auto oldSemanticDecoration = oldKey->findDecoration()) - { - foundSemanticDecor = true; - fieldSemanticDecoration = oldSemanticDecoration; - parentLayout = nullptr; - } - - IRLayoutDecoration* fieldLayout = parentLayout; - if (auto oldLayout = oldKey->findDecoration()) - { - fieldLayout = oldLayout; - if (!foundSemanticDecor) - fieldSemanticDecoration = nullptr; - } - if (fieldSemanticDecoration != parentSemanticDecoration || parentLayout != fieldLayout) - varsWithSemanticInfo.add(oldField); - - // step 2a - if (auto structFieldType = as(oldField->getFieldType())) - { - _flattenNestedStructs( - builder, - dst, - structFieldType, - fieldSemanticDecoration, - fieldLayout, - fieldMappingNode, - varsWithSemanticInfo); - continue; - } - - // step 3a - auto newKey = builder.createStructKey(); - copyNameHintAndDebugDecorations(newKey, oldKey); - - auto newField = builder.createStructField(dst, newKey, oldField->getFieldType()); - copyNameHintAndDebugDecorations(newField, oldField); - - if (fieldSemanticDecoration) - builder.addSemanticDecoration( - newKey, - fieldSemanticDecoration->getSemanticName(), - fieldSemanticDecoration->getSemanticIndex()); - - if (fieldLayout) - { - IRLayout* oldLayout = fieldLayout->getLayout(); - List instToCopy; - // Only copy certain decorations needed for resolving system semantics - for (UInt i = 0; i < oldLayout->getOperandCount(); i++) - { - auto operand = oldLayout->getOperand(i); - if (as(operand) || as(operand) || - as(operand) || as(operand)) - instToCopy.add(operand); - } - IRVarLayout* newLayout = builder.getVarLayout(instToCopy); - builder.addLayoutDecoration(newKey, newLayout); - } - // step 3b - fieldMappingNode.setMapping(newField); - } - - return dst; - } - - // Returns a `IRStructType*` without any `IRStructType*` members. `src` may be returned if there - // was no struct flattening. - // @param mapFieldToField Behavior maps all `IRStructField` of `src` to the new struct - // `IRStructFields`s - IRStructType* maybeFlattenNestedStructs( - IRBuilder& builder, - IRStructType* src, - MapStructToFlatStruct& mapFieldToField, - HashSet& varsWithSemanticInfo) - { - // Find all values inside struct that need flattening and legalization. - bool hasStructTypeMembers = false; - for (auto field : src->getFields()) - { - if (as(field->getFieldType())) - { - hasStructTypeMembers = true; - break; - } - } - if (!hasStructTypeMembers) - return src; - - // We need to: - // 1. Make new struct 1:1 with old struct but without nestested structs (flatten) - // 2. Ensure semantic attributes propegate. This will create overlapping semantics (can be - // handled later). - // 3. Store the mapping from old to new struct fields to allow copying a old-struct to - // new-struct. - builder.setInsertAfter(src); - auto newStruct = builder.createStructType(); - copyNameHintAndDebugDecorations(newStruct, src); - mapFieldToField.setNode(src); - return _flattenNestedStructs( - builder, - newStruct, - src, - nullptr, - nullptr, - mapFieldToField, - varsWithSemanticInfo); - } - - // Replaces all 'IRReturn' by copying the current 'IRReturn' to a new var of type 'newType'. - // Copying logic from 'IRReturn' to 'newType' is controlled by 'copyLogicFunc' function. - template - void _replaceAllReturnInst( - IRBuilder& builder, - IRFunc* targetFunc, - IRStructType* newType, - CopyLogicFunc copyLogicFunc) - { - for (auto block : targetFunc->getBlocks()) - { - if (auto returnInst = as(block->getTerminator())) - { - builder.setInsertBefore(returnInst); - auto returnVal = returnInst->getVal(); - returnInst->setOperand(0, copyLogicFunc(builder, newType, returnVal)); - } - } - } - - UInt _returnNonOverlappingAttributeIndex(std::set& usedSemanticIndex) - { - // Find first unused semantic index of equal semantic type - // to fill any gaps in user set semantic bindings - UInt prev = 0; - for (auto i : usedSemanticIndex) - { - if (i > prev + 1) - { - break; - } - prev = i; - } - usedSemanticIndex.insert(prev + 1); - return prev + 1; - } - - template - struct AttributeParentPair - { - IRLayoutDecoration* layoutDecor; - T* attr; - }; - - IRLayoutDecoration* _replaceAttributeOfLayout( - IRBuilder& builder, - IRLayoutDecoration* parentLayoutDecor, - IRInst* instToReplace, - IRInst* instToReplaceWith) - { - // Replace `instToReplace` with a `instToReplaceWith` - - auto layout = parentLayoutDecor->getLayout(); - // Find the exact same decoration `instToReplace` in-case multiple of the same type exist - List opList; - opList.add(instToReplaceWith); - for (UInt i = 0; i < layout->getOperandCount(); i++) - { - if (layout->getOperand(i) != instToReplace) - opList.add(layout->getOperand(i)); - } - auto newLayoutDecor = builder.addLayoutDecoration( - parentLayoutDecor->getParent(), - builder.getVarLayout(opList)); - parentLayoutDecor->removeAndDeallocate(); - return newLayoutDecor; - } - - IRLayoutDecoration* _simplifyUserSemanticNames( - IRBuilder& builder, - IRLayoutDecoration* layoutDecor) - { - // Ensure all 'ExplicitIndex' semantics such as "SV_TARGET0" are simplified into - // ("SV_TARGET", 0) using 'IRUserSemanticAttr' This is done to ensure we can check semantic - // groups using 'IRUserSemanticAttr1->getName() == IRUserSemanticAttr2->getName()' - SLANG_ASSERT(layoutDecor); - auto layout = layoutDecor->getLayout(); - List layoutOps; - layoutOps.reserve(3); - bool changed = false; - for (auto attr : layout->getAllAttrs()) - { - if (auto userSemantic = as(attr)) - { - UnownedStringSlice outName; - UnownedStringSlice outIndex; - bool hasStringIndex = splitNameAndIndex(userSemantic->getName(), outName, outIndex); - if (hasStringIndex) - { - changed = true; - auto loweredName = String(outName).toLower(); - auto loweredNameSlice = loweredName.getUnownedSlice(); - auto newDecoration = - builder.getUserSemanticAttr(loweredNameSlice, stringToInt(outIndex)); - userSemantic->replaceUsesWith(newDecoration); - userSemantic->removeAndDeallocate(); - userSemantic = newDecoration; - } - layoutOps.add(userSemantic); - continue; - } - layoutOps.add(attr); - } - if (changed) - { - auto parent = layoutDecor->parent; - layoutDecor->removeAndDeallocate(); - builder.addLayoutDecoration(parent, builder.getVarLayout(layoutOps)); - } - return layoutDecor; - } - // Find overlapping field semantics and legalize them - void fixFieldSemanticsOfFlatStruct(IRStructType* structType) - { - // Goal is to ensure we do not have overlapping semantics: - /* - // Assume the following code - struct Fragment - { - float4 p1 : SV_TARGET; - float3 p2 : SV_TARGET; - float2 p3 : SV_TARGET; - float2 p4 : SV_TARGET; - }; - - // Translates into - struct Fragment - { - float4 p1 : SV_TARGET0; - float3 p2 : SV_TARGET1; - float2 p3 : SV_TARGET2; - float2 p4 : SV_TARGET3; - }; - */ - - IRBuilder builder(this->m_module); - - List overlappingSemanticsDecor; - Dictionary>> - usedSemanticIndexSemanticDecor; - - List> overlappingVarOffset; - Dictionary>> usedSemanticIndexVarOffset; - - List> overlappingUserSemantic; - Dictionary>> - usedSemanticIndexUserSemantic; - - // We store a map from old `IRLayoutDecoration*` to new `IRLayoutDecoration*` since when - // legalizing we may destroy and remake a `IRLayoutDecoration*` - Dictionary oldLayoutDecorToNew; - - // Collect all "semantic info carrying decorations". Any collected decoration will - // fill up their respective 'Dictionary>' - // to keep track of in-use offsets for a semantic type. - // Example: IRSemanticDecoration with name of "SV_TARGET1". - // * This will have SEMANTIC_TYPE of "sv_target". - // * This will use up index '1' - // - // Now if a second equal semantic "SV_TARGET1" is found, we add this decoration to - // a list of 'overlapping semantic info decorations' so we can legalize this - // 'semantic info decoration' later. - // - // NOTE: this is a flat struct, all members are children of the initial - // IRStructType. - for (auto field : structType->getFields()) - { - auto key = field->getKey(); - if (auto semanticDecoration = key->findDecoration()) - { - // Ensure names are in a uniform lowercase format so we can bunch together simmilar - // semantics - UnownedStringSlice outName; - UnownedStringSlice outIndex; - bool hasStringIndex = - splitNameAndIndex(semanticDecoration->getSemanticName(), outName, outIndex); - if (hasStringIndex) - { - auto loweredName = String(outName).toLower(); - auto loweredNameSlice = loweredName.getUnownedSlice(); - auto newDecoration = - builder.addSemanticDecoration(key, loweredNameSlice, stringToInt(outIndex)); - semanticDecoration->replaceUsesWith(newDecoration); - semanticDecoration->removeAndDeallocate(); - semanticDecoration = newDecoration; - } - auto& semanticUse = - usedSemanticIndexSemanticDecor[semanticDecoration->getSemanticName()]; - if (semanticUse.find(semanticDecoration->getSemanticIndex()) != semanticUse.end()) - overlappingSemanticsDecor.add(semanticDecoration); - else - semanticUse.insert(semanticDecoration->getSemanticIndex()); - } - if (auto layoutDecor = key->findDecoration()) - { - // Ensure names are in a uniform lowercase format so we can bunch together simmilar - // semantics - layoutDecor = _simplifyUserSemanticNames(builder, layoutDecor); - oldLayoutDecorToNew[layoutDecor] = layoutDecor; - auto layout = layoutDecor->getLayout(); - for (auto attr : layout->getAllAttrs()) - { - if (auto offset = as(attr)) - { - auto& semanticUse = usedSemanticIndexVarOffset[offset->getResourceKind()]; - if (semanticUse.find(offset->getOffset()) != semanticUse.end()) - overlappingVarOffset.add({layoutDecor, offset}); - else - semanticUse.insert(offset->getOffset()); - } - else if (auto userSemantic = as(attr)) - { - auto& semanticUse = usedSemanticIndexUserSemantic[userSemantic->getName()]; - if (semanticUse.find(userSemantic->getIndex()) != semanticUse.end()) - overlappingUserSemantic.add({layoutDecor, userSemantic}); - else - semanticUse.insert(userSemantic->getIndex()); - } - } - } - } - - // Legalize all overlapping 'semantic info decorations' - for (auto decor : overlappingSemanticsDecor) - { - auto newOffset = _returnNonOverlappingAttributeIndex( - usedSemanticIndexSemanticDecor[decor->getSemanticName()]); - builder.addSemanticDecoration( - decor->getParent(), - decor->getSemanticName(), - (int)newOffset); - decor->removeAndDeallocate(); - } - for (auto& varOffset : overlappingVarOffset) - { - auto newOffset = _returnNonOverlappingAttributeIndex( - usedSemanticIndexVarOffset[varOffset.attr->getResourceKind()]); - auto newVarOffset = builder.getVarOffsetAttr( - varOffset.attr->getResourceKind(), - newOffset, - varOffset.attr->getSpace()); - oldLayoutDecorToNew[varOffset.layoutDecor] = _replaceAttributeOfLayout( - builder, - oldLayoutDecorToNew[varOffset.layoutDecor], - varOffset.attr, - newVarOffset); - } - for (auto& userSemantic : overlappingUserSemantic) - { - auto newOffset = _returnNonOverlappingAttributeIndex( - usedSemanticIndexUserSemantic[userSemantic.attr->getName()]); - auto newUserSemantic = - builder.getUserSemanticAttr(userSemantic.attr->getName(), newOffset); - oldLayoutDecorToNew[userSemantic.layoutDecor] = _replaceAttributeOfLayout( - builder, - oldLayoutDecorToNew[userSemantic.layoutDecor], - userSemantic.attr, - newUserSemantic); - } - } - - void wrapReturnValueInStruct(EntryPointInfo entryPoint) - { - // Wrap return value into a struct if it is not already a struct. - // For example, given this entry point: - // ``` - // float4 main() : SV_Target { return float3(1,2,3); } - // ``` - // We are going to transform it into: - // ``` - // struct Output { - // float4 value : SV_Target; - // }; - // Output main() { return {float3(1,2,3)}; } - - auto func = entryPoint.entryPointFunc; - - auto returnType = func->getResultType(); - if (as(returnType)) - return; - auto entryPointLayoutDecor = func->findDecoration(); - if (!entryPointLayoutDecor) - return; - auto entryPointLayout = as(entryPointLayoutDecor->getLayout()); - if (!entryPointLayout) - return; - auto resultLayout = entryPointLayout->getResultLayout(); - - // If return type is already a struct, just make sure every field has a semantic. - if (auto returnStructType = as(returnType)) - { - IRBuilder builder(func); - MapStructToFlatStruct mapOldFieldToNewField; - // Flatten result struct type to ensure we do not have nested semantics - auto flattenedStruct = maybeFlattenNestedStructs( - builder, - returnStructType, - mapOldFieldToNewField, - semanticInfoToRemove); - if (returnStructType != flattenedStruct) - { - // Replace all return-values with the flattenedStruct we made. - _replaceAllReturnInst( - builder, - func, - flattenedStruct, - [&](IRBuilder& copyBuilder, IRStructType* dstType, IRInst* srcVal) -> IRInst* - { - auto srcStructType = as(srcVal->getDataType()); - SLANG_ASSERT(srcStructType); - auto dstVal = copyBuilder.emitVar(dstType); - mapOldFieldToNewField.emitCopy<( - int)MapStructToFlatStruct::CopyOptions::StructIntoFlatStruct>( - copyBuilder, - dstVal, - srcVal); - return builder.emitLoad(dstVal); - }); - fixUpFuncType(func, flattenedStruct); - } - // Ensure non-overlapping semantics - fixFieldSemanticsOfFlatStruct(flattenedStruct); - ensureResultStructHasUserSemantic(flattenedStruct, resultLayout); - return; - } - - IRBuilder builder(func); - builder.setInsertBefore(func); - IRStructType* structType = builder.createStructType(); - auto stageText = getStageText(entryPoint.entryPointDecor->getProfile().getStage()); - builder.addNameHintDecoration( - structType, - (String(stageText) + toSlice("Output")).getUnownedSlice()); - auto key = builder.createStructKey(); - builder.addNameHintDecoration(key, toSlice("output")); - builder.addLayoutDecoration(key, resultLayout); - builder.createStructField(structType, key, returnType); - IRStructTypeLayout::Builder structTypeLayoutBuilder(&builder); - structTypeLayoutBuilder.addField(key, resultLayout); - auto typeLayout = structTypeLayoutBuilder.build(); - IRVarLayout::Builder varLayoutBuilder(&builder, typeLayout); - auto varLayout = varLayoutBuilder.build(); - ensureResultStructHasUserSemantic(structType, varLayout); - - _replaceAllReturnInst( - builder, - func, - structType, - [](IRBuilder& copyBuilder, IRStructType* dstType, IRInst* srcVal) -> IRInst* - { return copyBuilder.emitMakeStruct(dstType, 1, &srcVal); }); - - // Assign an appropriate system value semantic for stage output - auto stage = entryPoint.entryPointDecor->getProfile().getStage(); - switch (stage) - { - case Stage::Compute: - case Stage::Fragment: - { - builder.addTargetSystemValueDecoration(key, toSlice("color(0)")); - break; - } - case Stage::Vertex: - { - builder.addTargetSystemValueDecoration(key, toSlice("position")); - break; - } - default: - SLANG_ASSERT(false); - return; - } - - fixUpFuncType(func, structType); - } - - void legalizeMeshEntryPoint(EntryPointInfo entryPoint) - { - auto func = entryPoint.entryPointFunc; - - IRBuilder builder{func->getModule()}; - for (auto param : func->getParams()) - { - if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) - { - IRVarLayout::Builder varLayoutBuilder( - &builder, - IRTypeLayout::Builder{&builder}.build()); - - varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::MetalPayload); - auto paramVarLayout = varLayoutBuilder.build(); - builder.addLayoutDecoration(param, paramVarLayout); - - IRPtrTypeBase* type = as(param->getDataType()); - - const auto annotatedPayloadType = builder.getPtrType( - kIROp_ConstRefType, - type->getValueType(), - AddressSpace::MetalObjectData); - - param->setFullType(annotatedPayloadType); - } - } - IROutputTopologyDecoration* outputDeco = - entryPoint.entryPointFunc->findDecoration(); - if (outputDeco == nullptr) - { - SLANG_UNEXPECTED("Mesh shader output decoration missing"); - return; - } - const auto topology = outputDeco->getTopology(); - const auto topStr = topology->getStringSlice(); - UInt topologyEnum = 0; - if (topStr.caseInsensitiveEquals(toSlice("point"))) - { - topologyEnum = 1; - } - else if (topStr.caseInsensitiveEquals(toSlice("line"))) - { - topologyEnum = 2; - } - else if (topStr.caseInsensitiveEquals(toSlice("triangle"))) - { - topologyEnum = 3; - } - else - { - SLANG_UNEXPECTED("unknown topology"); - return; - } - - IRInst* topologyConst = builder.getIntValue(builder.getIntType(), topologyEnum); - - IRType* vertexType = nullptr; - IRType* indicesType = nullptr; - IRType* primitiveType = nullptr; - - IRInst* maxVertices = nullptr; - IRInst* maxPrimitives = nullptr; - - IRInst* verticesParam = nullptr; - IRInst* indicesParam = nullptr; - IRInst* primitivesParam = nullptr; - for (auto param : func->getParams()) - { - if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) - { - IRVarLayout::Builder varLayoutBuilder( - &builder, - IRTypeLayout::Builder{&builder}.build()); - - varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::MetalPayload); - auto paramVarLayout = varLayoutBuilder.build(); - builder.addLayoutDecoration(param, paramVarLayout); - } - if (param->findDecorationImpl(kIROp_VerticesDecoration)) - { - auto vertexRefType = as(param->getDataType()); - auto vertexOutputType = as(vertexRefType->getValueType()); - vertexType = vertexOutputType->getElementType(); - maxVertices = vertexOutputType->getMaxElementCount(); - SLANG_ASSERT(vertexType); - - verticesParam = param; - auto vertStruct = as(vertexType); - for (auto field : vertStruct->getFields()) - { - auto key = field->getKey(); - if (auto deco = key->findDecoration()) - { - if (deco->getSemanticName().caseInsensitiveEquals(toSlice("sv_position"))) - { - builder.addTargetSystemValueDecoration(key, toSlice("position")); - } - } - } - } - if (param->findDecorationImpl(kIROp_IndicesDecoration)) - { - auto indicesRefType = (IRConstRefType*)param->getDataType(); - auto indicesOutputType = (IRIndicesType*)indicesRefType->getValueType(); - indicesType = indicesOutputType->getElementType(); - maxPrimitives = indicesOutputType->getMaxElementCount(); - SLANG_ASSERT(indicesType); - - indicesParam = param; - } - if (param->findDecorationImpl(kIROp_PrimitivesDecoration)) - { - auto primitivesRefType = (IRConstRefType*)param->getDataType(); - auto primitivesOutputType = (IRPrimitivesType*)primitivesRefType->getValueType(); - primitiveType = primitivesOutputType->getElementType(); - SLANG_ASSERT(primitiveType); - - primitivesParam = param; - auto primStruct = as(primitiveType); - for (auto field : primStruct->getFields()) - { - auto key = field->getKey(); - if (auto deco = key->findDecoration()) - { - if (deco->getSemanticName().caseInsensitiveEquals( - toSlice("sv_primitiveid"))) - { - builder.addTargetSystemValueDecoration(key, toSlice("primitive_id")); - } - } - } - } - } - if (primitiveType == nullptr) - { - primitiveType = builder.getVoidType(); - } - builder.setInsertBefore(entryPoint.entryPointFunc->getFirstBlock()->getFirstOrdinaryInst()); - - auto meshParam = builder.emitParam(builder.getMetalMeshType( - vertexType, - primitiveType, - maxVertices, - maxPrimitives, - topologyConst)); - builder.addExternCppDecoration(meshParam, toSlice("_slang_mesh")); - - - verticesParam->removeFromParent(); - verticesParam->removeAndDeallocate(); - - indicesParam->removeFromParent(); - indicesParam->removeAndDeallocate(); - - if (primitivesParam != nullptr) - { - primitivesParam->removeFromParent(); - primitivesParam->removeAndDeallocate(); - } - } - - void legalizeDispatchMeshPayloadForMetal(EntryPointInfo entryPoint) - { - // Find out DispatchMesh function - IRGlobalValueWithCode* dispatchMeshFunc = nullptr; - for (const auto globalInst : entryPoint.entryPointFunc->getModule()->getGlobalInsts()) - { - if (const auto func = as(globalInst)) - { - if (const auto dec = func->findDecoration()) - { - if (dec->getName() == "DispatchMesh") - { - SLANG_ASSERT(!dispatchMeshFunc && "Multiple DispatchMesh functions found"); - dispatchMeshFunc = func; - } - } - } - } - - if (!dispatchMeshFunc) - return; - - IRBuilder builder{entryPoint.entryPointFunc->getModule()}; - - // We'll rewrite the call to use mesh_grid_properties.set_threadgroups_per_grid - traverseUses( - dispatchMeshFunc, - [&](const IRUse* use) - { - if (const auto call = as(use->getUser())) - { - SLANG_ASSERT(call->getArgCount() == 4); - const auto payload = call->getArg(3); - - const auto payloadPtrType = - composeGetters(payload, &IRInst::getDataType); - SLANG_ASSERT(payloadPtrType); - const auto payloadType = payloadPtrType->getValueType(); - SLANG_ASSERT(payloadType); - - builder.setInsertBefore( - entryPoint.entryPointFunc->getFirstBlock()->getFirstOrdinaryInst()); - const auto annotatedPayloadType = builder.getPtrType( - kIROp_RefType, - payloadPtrType->getValueType(), - AddressSpace::MetalObjectData); - auto packedParam = builder.emitParam(annotatedPayloadType); - builder.addExternCppDecoration(packedParam, toSlice("_slang_mesh_payload")); - IRVarLayout::Builder varLayoutBuilder( - &builder, - IRTypeLayout::Builder{&builder}.build()); - - // Add the MetalPayload resource info, so we can emit [[payload]] - varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::MetalPayload); - auto paramVarLayout = varLayoutBuilder.build(); - builder.addLayoutDecoration(packedParam, paramVarLayout); - - // Now we replace the call to DispatchMesh with a call to the mesh grid - // properties But first we need to create the parameter - const auto meshGridPropertiesType = builder.getMetalMeshGridPropertiesType(); - auto mgp = builder.emitParam(meshGridPropertiesType); - builder.addExternCppDecoration(mgp, toSlice("_slang_mgp")); - } - }); - } - - IRInst* tryConvertValue(IRBuilder& builder, IRInst* val, IRType* toType) - { - auto fromType = val->getFullType(); - if (auto fromVector = as(fromType)) - { - if (auto toVector = as(toType)) - { - if (fromVector->getElementCount() != toVector->getElementCount()) - { - fromType = builder.getVectorType( - fromVector->getElementType(), - toVector->getElementCount()); - val = builder.emitVectorReshape(fromType, val); - } - } - else if (as(toType)) - { - UInt index = 0; - val = builder.emitSwizzle(fromVector->getElementType(), val, 1, &index); - if (toType->getOp() == kIROp_VoidType) - return nullptr; - } - } - else if (auto fromBasicType = as(fromType)) - { - if (fromBasicType->getOp() == kIROp_VoidType) - return nullptr; - if (!as(toType)) - return nullptr; - if (toType->getOp() == kIROp_VoidType) - return nullptr; - } - else - { - return nullptr; - } - return builder.emitCast(toType, val); - } - - struct SystemValLegalizationWorkItem - { - IRInst* var; - String attrName; - UInt attrIndex; - }; - - std::optional tryToMakeSystemValWorkItem(IRInst* var) - { - if (auto semanticDecoration = var->findDecoration()) - { - if (semanticDecoration->getSemanticName().startsWithCaseInsensitive(toSlice("sv_"))) - { - return { - {var, - String(semanticDecoration->getSemanticName()).toLower(), - (UInt)semanticDecoration->getSemanticIndex()}}; - } - } - - auto layoutDecor = var->findDecoration(); - if (!layoutDecor) - return {}; - auto sysValAttr = layoutDecor->findAttr(); - if (!sysValAttr) - return {}; - auto semanticName = String(sysValAttr->getName()); - auto sysAttrIndex = sysValAttr->getIndex(); - - return {{var, semanticName, sysAttrIndex}}; - } - - - List collectSystemValFromEntryPoint(EntryPointInfo entryPoint) - { - List systemValWorkItems; - for (auto param : entryPoint.entryPointFunc->getParams()) - { - auto maybeWorkItem = tryToMakeSystemValWorkItem(param); - if (maybeWorkItem.has_value()) - systemValWorkItems.add(std::move(maybeWorkItem.value())); - } - return systemValWorkItems; - } - - void legalizeSystemValue(EntryPointInfo entryPoint, SystemValLegalizationWorkItem& workItem) - { - IRBuilder builder(entryPoint.entryPointFunc); - - auto var = workItem.var; - auto semanticName = workItem.attrName; - - auto indexAsString = String(workItem.attrIndex); - auto info = getSystemValueInfo(semanticName, &indexAsString, var); - - if (info.isSpecial) - { - if (info.metalSystemValueNameEnum == SystemValueSemanticName::InnerCoverage) - { - // Metal does not support conservative rasterization, so this is always false. - auto val = builder.getBoolValue(false); - var->replaceUsesWith(val); - var->removeAndDeallocate(); - } - else if (info.metalSystemValueNameEnum == SystemValueSemanticName::GroupIndex) - { - // Ensure we have a cached "sv_groupthreadid" in our entry point - if (!entryPointToGroupThreadId.containsKey(entryPoint.entryPointFunc)) - { - auto systemValWorkItems = collectSystemValFromEntryPoint(entryPoint); - for (auto i : systemValWorkItems) - { - auto indexAsStringGroupThreadId = String(i.attrIndex); - if (getSystemValueInfo(i.attrName, &indexAsStringGroupThreadId, i.var) - .metalSystemValueNameEnum == SystemValueSemanticName::GroupThreadID) - { - entryPointToGroupThreadId[entryPoint.entryPointFunc] = i.var; - } - } - if (!entryPointToGroupThreadId.containsKey(entryPoint.entryPointFunc)) - { - // Add the missing groupthreadid needed to compute sv_groupindex - IRBuilder groupThreadIdBuilder(builder); - groupThreadIdBuilder.setInsertInto( - entryPoint.entryPointFunc->getFirstBlock()); - auto groupThreadId = groupThreadIdBuilder.emitParamAtHead( - getGroupThreadIdType(groupThreadIdBuilder)); - entryPointToGroupThreadId[entryPoint.entryPointFunc] = groupThreadId; - groupThreadIdBuilder.addNameHintDecoration( - groupThreadId, - groupThreadIDString); - - // Since "sv_groupindex" will be translated out to a global var and no - // longer be considered a system value we can reuse its layout and semantic - // info - Index foundRequiredDecorations = 0; - IRLayoutDecoration* layoutDecoration = nullptr; - UInt semanticIndex = 0; - for (auto decoration : var->getDecorations()) - { - if (auto layoutDecorationTmp = as(decoration)) - { - layoutDecoration = layoutDecorationTmp; - foundRequiredDecorations++; - } - else if (auto semanticDecoration = as(decoration)) - { - semanticIndex = semanticDecoration->getSemanticIndex(); - groupThreadIdBuilder.addSemanticDecoration( - groupThreadId, - groupThreadIDString, - (int)semanticIndex); - foundRequiredDecorations++; - } - if (foundRequiredDecorations >= 2) - break; - } - SLANG_ASSERT(layoutDecoration); - layoutDecoration->removeFromParent(); - layoutDecoration->insertAtStart(groupThreadId); - SystemValLegalizationWorkItem newWorkItem = { - groupThreadId, - groupThreadIDString, - semanticIndex}; - legalizeSystemValue(entryPoint, newWorkItem); - } - } - - IRBuilder svBuilder(builder.getModule()); - svBuilder.setInsertBefore(entryPoint.entryPointFunc->getFirstOrdinaryInst()); - auto uint3Type = builder.getVectorType( - builder.getUIntType(), - builder.getIntValue(builder.getIntType(), 3)); - auto computeExtent = - emitCalcGroupExtents(svBuilder, entryPoint.entryPointFunc, uint3Type); - if (!computeExtent) - { - m_sink->diagnose( - entryPoint.entryPointFunc, - Diagnostics::unsupportedSpecializationConstantForNumThreads); - - // Fill in placeholder values. - static const int kAxisCount = 3; - IRInst* groupExtentAlongAxis[kAxisCount] = {}; - for (int axis = 0; axis < kAxisCount; axis++) - groupExtentAlongAxis[axis] = - builder.getIntValue(uint3Type->getElementType(), 1); - computeExtent = - builder.emitMakeVector(uint3Type, kAxisCount, groupExtentAlongAxis); - } - auto groupIndexCalc = emitCalcGroupIndex( - svBuilder, - entryPointToGroupThreadId[entryPoint.entryPointFunc], - computeExtent); - svBuilder.addNameHintDecoration( - groupIndexCalc, - UnownedStringSlice("sv_groupindex")); - - var->replaceUsesWith(groupIndexCalc); - var->removeAndDeallocate(); - } - } - if (info.isUnsupported) - { - reportUnsupportedSystemAttribute(var, semanticName); - return; - } - if (!info.permittedTypes.getCount()) - return; - - builder.addTargetSystemValueDecoration(var, info.metalSystemValueName.getUnownedSlice()); - - bool varTypeIsPermitted = false; - auto varType = var->getFullType(); - for (auto& permittedType : info.permittedTypes) - { - varTypeIsPermitted = varTypeIsPermitted || permittedType == varType; - } - - if (!varTypeIsPermitted) - { - // Note: we do not currently prefer any conversion - // example: - // * allowed types for semantic: `float4`, `uint4`, `int4` - // * user used, `float2` - // * Slang will equally prefer `float4` to `uint4` to `int4`. - // This means the type may lose data if slang selects `uint4` or `int4`. - bool foundAConversion = false; - for (auto permittedType : info.permittedTypes) - { - var->setFullType(permittedType); - builder.setInsertBefore( - entryPoint.entryPointFunc->getFirstBlock()->getFirstOrdinaryInst()); - - // get uses before we `tryConvertValue` since this creates a new use - List uses; - for (auto use = var->firstUse; use; use = use->nextUse) - uses.add(use); - - auto convertedValue = tryConvertValue(builder, var, varType); - if (convertedValue == nullptr) - continue; - - foundAConversion = true; - copyNameHintAndDebugDecorations(convertedValue, var); - - for (auto use : uses) - builder.replaceOperand(use, convertedValue); - } - if (!foundAConversion) - { - // If we can't convert the value, report an error. - for (auto permittedType : info.permittedTypes) - { - StringBuilder typeNameSB; - getTypeNameHint(typeNameSB, permittedType); - m_sink->diagnose( - var->sourceLoc, - Diagnostics::systemValueTypeIncompatible, - semanticName, - typeNameSB.produceString()); - } - } - } - } - - void legalizeSystemValueParameters(EntryPointInfo entryPoint) - { - List systemValWorkItems = - collectSystemValFromEntryPoint(entryPoint); - - for (auto index = 0; index < systemValWorkItems.getCount(); index++) - { - legalizeSystemValue(entryPoint, systemValWorkItems[index]); - } - fixUpFuncType(entryPoint.entryPointFunc); - } - - void legalizeEntryPointForMetal(EntryPointInfo entryPoint) - { - // Input Parameter Legalize - depointerizeInputParams(entryPoint.entryPointFunc); - hoistEntryPointParameterFromStruct(entryPoint); - packStageInParameters(entryPoint); - flattenInputParameters(entryPoint); - - // System Value Legalize - legalizeSystemValueParameters(entryPoint); - - // Output Value Legalize - wrapReturnValueInStruct(entryPoint); - - // Other Legalize - switch (entryPoint.entryPointDecor->getProfile().getStage()) - { - case Stage::Amplification: - legalizeDispatchMeshPayloadForMetal(entryPoint); - break; - case Stage::Mesh: - legalizeMeshEntryPoint(entryPoint); - break; - default: - break; - } - } -}; - // metal textures only support writing 4-component values, even if the texture is only 1, 2, or // 3-component in this case the other channels get ignored, but the signature still doesnt match so // now we have to replace the value being written with a 4-component vector where the new components @@ -2187,10 +233,7 @@ void legalizeIRForMetal(IRModule* module, DiagnosticSink* sink) } } - LegalizeMetalEntryPointContext context(sink, module); - for (auto entryPoint : entryPoints) - context.legalizeEntryPointForMetal(entryPoint); - context.removeSemanticLayoutsFromLegalizedStructs(); + legalizeEntryPointVaryingParamsForMetal(module, sink, entryPoints); MetalAddressSpaceAssigner metalAddressSpaceAssigner; specializeAddressSpace(module, &metalAddressSpaceAssigner); diff --git a/source/slang/slang-ir-wgsl-legalize.cpp b/source/slang/slang-ir-wgsl-legalize.cpp index effc06f3ef..efa028703c 100644 --- a/source/slang/slang-ir-wgsl-legalize.cpp +++ b/source/slang/slang-ir-wgsl-legalize.cpp @@ -4,1537 +4,169 @@ #include "slang-ir-legalize-binary-operator.h" #include "slang-ir-legalize-global-values.h" #include "slang-ir-legalize-varying-params.h" -#include "slang-ir-util.h" #include "slang-ir.h" -#include "slang-parameter-binding.h" - -#include namespace Slang { -struct EntryPointInfo -{ - IRFunc* entryPointFunc; - IREntryPointDecoration* entryPointDecor; -}; - -struct LegalizeWGSLEntryPointContext +static void legalizeCall(IRCall* call) { - HashSet semanticInfoToRemove; - UnownedStringSlice userSemanticName = toSlice("user_semantic"); - - DiagnosticSink* m_sink; - IRModule* m_module; - - LegalizeWGSLEntryPointContext(DiagnosticSink* sink, IRModule* module) - : m_sink(sink), m_module(module) - { - } - - void removeSemanticLayoutsFromLegalizedStructs() - { - // WGSL does not allow duplicate attributes to appear in the same shader. - // If we emit our own struct with `[[color(0)]`, all existing uses of `[[color(0)]]` - // must be removed. - for (auto field : semanticInfoToRemove) - { - auto key = field->getKey(); - // Some decorations appear twice, destroy all found - for (;;) - { - if (auto semanticDecor = key->findDecoration()) - { - semanticDecor->removeAndDeallocate(); - continue; - } - else if (auto layoutDecor = key->findDecoration()) - { - layoutDecor->removeAndDeallocate(); - continue; - } - break; - } - } - } - - // Flattens all struct parameters of an entryPoint to ensure parameters are a flat struct - void flattenInputParameters(EntryPointInfo entryPoint) - { - // Goal is to ensure we have a flattened IRParam (0 nested IRStructType members). - /* - // Assume the following code - struct NestedFragment - { - float2 p3; - }; - struct Fragment - { - float4 p1; - float3 p2; - NestedFragment p3_nested; - }; - - // Fragment flattens into - struct Fragment - { - float4 p1; - float3 p2; - float2 p3; - }; - */ - - // This is important since WGSL does not allow semantic's on a struct - /* - // Assume the following code - struct NestedFragment1 - { - float2 p3; - }; - struct Fragment1 - { - float4 p1 : SV_TARGET0; - float3 p2 : SV_TARGET1; - NestedFragment p3_nested : SV_TARGET2; // error, semantic on struct - }; - - */ - - // Unlike Metal, WGSL does NOT allow semantics on members of a nested struct. - /* - // Assume the following code - struct NestedFragment - { - float2 p3; - }; - struct Fragment - { - float4 p1 : SV_TARGET0; - NestedFragment p2 : SV_TARGET1; - NestedFragment p3 : SV_TARGET2; - }; - - // Legalized with flattening - struct Fragment - { - float4 p1 : SV_TARGET0; - float2 p2 : SV_TARGET1; - float2 p3 : SV_TARGET2; - }; - */ - - auto func = entryPoint.entryPointFunc; - bool modified = false; - for (auto param : func->getParams()) - { - auto layout = findVarLayout(param); - if (!layout) - continue; - if (!layout->findOffsetAttr(LayoutResourceKind::VaryingInput)) - continue; - if (param->findDecorationImpl(kIROp_HLSLMeshPayloadDecoration)) - continue; - // If we find a IRParam with a IRStructType member, we need to flatten the entire - // IRParam - if (auto structType = as(param->getDataType())) - { - IRBuilder builder(func); - MapStructToFlatStruct mapOldFieldToNewField; - - // Flatten struct if we have nested IRStructType - auto flattenedStruct = maybeFlattenNestedStructs( - builder, - structType, - mapOldFieldToNewField, - semanticInfoToRemove); - // Validate/rearange all semantics which overlap in our flat struct. - fixFieldSemanticsOfFlatStruct(flattenedStruct); - ensureStructHasUserSemantic( - flattenedStruct, - layout); - if (flattenedStruct != structType) - { - // Replace the 'old IRParam type' with a 'new IRParam type' - param->setFullType(flattenedStruct); - - // Emit a new variable at EntryPoint of 'old IRParam type' - builder.setInsertBefore(func->getFirstBlock()->getFirstOrdinaryInst()); - auto dstVal = builder.emitVar(structType); - auto dstLoad = builder.emitLoad(dstVal); - param->replaceUsesWith(dstLoad); - builder.setInsertBefore(dstLoad); - // Copy the 'new IRParam type' to our 'old IRParam type' - mapOldFieldToNewField - .emitCopy<(int)MapStructToFlatStruct::CopyOptions::FlatStructIntoStruct>( - builder, - dstVal, - param); - - modified = true; - } - } - } - if (modified) - fixUpFuncType(func); - } - - struct WGSLSystemValueInfo - { - String wgslSystemValueName; - SystemValueSemanticName wgslSystemValueNameEnum; - ShortList permittedTypes; - bool isUnsupported = false; - WGSLSystemValueInfo() - { - // most commonly need 2 - permittedTypes.reserveOverflowBuffer(2); - } + // WGSL does not allow forming a pointer to a sub part of a composite value. + // For example, if we have + // ``` + // struct S { float x; float y; }; + // void foo(inout float v) { v = 1.0f; } + // void main() { S s; foo(s.x); } + // ``` + // The call to `foo(s.x)` is illegal in WGSL because `s.x` is a sub part of `s`. + // And trying to form `&s.x` in WGSL is illegal. + // To work around this, we will create a local variable to hold the sub part of + // the composite value. + // And then pass the local variable to the function. + // After the call, we will write back the local variable to the sub part of the + // composite value. + // + IRBuilder builder(call); + builder.setInsertBefore(call); + struct WritebackPair + { + IRInst* dest; + IRInst* value; }; + ShortList pendingWritebacks; - WGSLSystemValueInfo getSystemValueInfo( - String inSemanticName, - String* optionalSemanticIndex, - IRInst* parentVar) + for (UInt i = 0; i < call->getArgCount(); i++) { - IRBuilder builder(m_module); - WGSLSystemValueInfo result = {}; - UnownedStringSlice semanticName; - UnownedStringSlice semanticIndex; - - auto hasExplicitIndex = - splitNameAndIndex(inSemanticName.getUnownedSlice(), semanticName, semanticIndex); - if (!hasExplicitIndex && optionalSemanticIndex) - semanticIndex = optionalSemanticIndex->getUnownedSlice(); - - result.wgslSystemValueNameEnum = convertSystemValueSemanticNameToEnum(semanticName); - - switch (result.wgslSystemValueNameEnum) + auto arg = call->getArg(i); + auto ptrType = as(arg->getDataType()); + if (!ptrType) + continue; + switch (arg->getOp()) { - - case SystemValueSemanticName::CullDistance: - { - result.isUnsupported = true; - } - break; - - case SystemValueSemanticName::ClipDistance: - { - // TODO: Implement this based on the 'clip-distances' feature in WGSL - // https: // www.w3.org/TR/webgpu/#dom-gpufeaturename-clip-distances - result.isUnsupported = true; - } - break; - - case SystemValueSemanticName::Coverage: - { - result.wgslSystemValueName = toSlice("sample_mask"); - result.permittedTypes.add(builder.getUIntType()); - } - break; - - case SystemValueSemanticName::Depth: - { - result.wgslSystemValueName = toSlice("frag_depth"); - result.permittedTypes.add(builder.getBasicType(BaseType::Float)); - } - break; - - case SystemValueSemanticName::DepthGreaterEqual: - case SystemValueSemanticName::DepthLessEqual: - { - result.isUnsupported = true; - } - break; - - case SystemValueSemanticName::DispatchThreadID: - { - result.wgslSystemValueName = toSlice("global_invocation_id"); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::UInt), - builder.getIntValue(builder.getIntType(), 3))); - } - break; - - case SystemValueSemanticName::DomainLocation: - { - result.isUnsupported = true; - } - break; - - case SystemValueSemanticName::GroupID: - { - result.wgslSystemValueName = toSlice("workgroup_id"); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::UInt), - builder.getIntValue(builder.getIntType(), 3))); - } - break; - - case SystemValueSemanticName::GroupIndex: - { - result.wgslSystemValueName = toSlice("local_invocation_index"); - result.permittedTypes.add(builder.getUIntType()); - } - break; - - case SystemValueSemanticName::GroupThreadID: - { - result.wgslSystemValueName = toSlice("local_invocation_id"); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::UInt), - builder.getIntValue(builder.getIntType(), 3))); - } - break; - - case SystemValueSemanticName::GSInstanceID: - { - // No Geometry shaders in WGSL - result.isUnsupported = true; - } - break; - - case SystemValueSemanticName::InnerCoverage: - { - result.isUnsupported = true; - } - break; - - case SystemValueSemanticName::InstanceID: - { - result.wgslSystemValueName = toSlice("instance_index"); - result.permittedTypes.add(builder.getUIntType()); - } - break; - - case SystemValueSemanticName::IsFrontFace: - { - result.wgslSystemValueName = toSlice("front_facing"); - result.permittedTypes.add(builder.getBoolType()); - } - break; - - case SystemValueSemanticName::OutputControlPointID: - case SystemValueSemanticName::PointSize: - { - result.isUnsupported = true; - } - break; - - case SystemValueSemanticName::Position: - { - result.wgslSystemValueName = toSlice("position"); - result.permittedTypes.add(builder.getVectorType( - builder.getBasicType(BaseType::Float), - builder.getIntValue(builder.getIntType(), 4))); - break; - } - - case SystemValueSemanticName::PrimitiveID: - case SystemValueSemanticName::RenderTargetArrayIndex: - { - result.isUnsupported = true; - break; - } - - case SystemValueSemanticName::SampleIndex: - { - result.wgslSystemValueName = toSlice("sample_index"); - result.permittedTypes.add(builder.getUIntType()); - break; - } - - case SystemValueSemanticName::StencilRef: - case SystemValueSemanticName::Target: - case SystemValueSemanticName::TessFactor: - { - result.isUnsupported = true; - break; - } - - case SystemValueSemanticName::VertexID: - { - result.wgslSystemValueName = toSlice("vertex_index"); - result.permittedTypes.add(builder.getUIntType()); - break; - } - - case SystemValueSemanticName::ViewID: - case SystemValueSemanticName::ViewportArrayIndex: - case SystemValueSemanticName::StartVertexLocation: - case SystemValueSemanticName::StartInstanceLocation: - { - result.isUnsupported = true; - break; - } - + case kIROp_Var: + case kIROp_Param: + case kIROp_GlobalParam: + case kIROp_GlobalVar: + continue; default: - { - m_sink->diagnose( - parentVar, - Diagnostics::unimplementedSystemValueSemantic, - semanticName); - return result; - } + break; } - return result; - } + // Create a local variable to hold the input argument. + auto var = builder.emitVar(ptrType->getValueType(), AddressSpace::Function); - void reportUnsupportedSystemAttribute(IRInst* param, String semanticName) - { - m_sink->diagnose( - param->sourceLoc, - Diagnostics::systemValueAttributeNotSupported, - semanticName); + // Store the input argument into the local variable. + builder.emitStore(var, builder.emitLoad(arg)); + builder.replaceOperand(call->getArgs() + i, var); + pendingWritebacks.add({arg, var}); } - template - void ensureStructHasUserSemantic(IRStructType* structType, IRVarLayout* varLayout) + // Perform writebacks after the call. + builder.setInsertAfter(call); + for (auto& pair : pendingWritebacks) { - // Ensure each field in an output struct type has either a system semantic or a user - // semantic, so that signature matching can happen correctly. - auto typeLayout = as(varLayout->getTypeLayout()); - Index index = 0; - IRBuilder builder(structType); - for (auto field : structType->getFields()) - { - auto key = field->getKey(); - if (auto semanticDecor = key->findDecoration()) - { - if (semanticDecor->getSemanticName().startsWithCaseInsensitive(toSlice("sv_"))) - { - auto indexAsString = String(UInt(semanticDecor->getSemanticIndex())); - auto sysValInfo = - getSystemValueInfo(semanticDecor->getSemanticName(), &indexAsString, field); - if (sysValInfo.isUnsupported) - { - reportUnsupportedSystemAttribute(field, semanticDecor->getSemanticName()); - } - else - { - builder.addTargetSystemValueDecoration( - key, - sysValInfo.wgslSystemValueName.getUnownedSlice()); - semanticDecor->removeAndDeallocate(); - } - } - index++; - continue; - } - typeLayout->getFieldLayout(index); - auto fieldLayout = typeLayout->getFieldLayout(index); - if (auto offsetAttr = fieldLayout->findOffsetAttr(K)) - { - UInt varOffset = 0; - if (auto varOffsetAttr = varLayout->findOffsetAttr(K)) - varOffset = varOffsetAttr->getOffset(); - varOffset += offsetAttr->getOffset(); - builder.addSemanticDecoration(key, toSlice("_slang_attr"), (int)varOffset); - } - index++; - } - } - - // Stores a hicharchy of members and children which map 'oldStruct->member' to - // 'flatStruct->member' Note: this map assumes we map to FlatStruct since it is easier/faster to - // process - struct MapStructToFlatStruct - { - /* - We need a hicharchy map to resolve dependencies for mapping - oldStruct to newStruct efficently. Example: - - MyStruct - | - / | \ - / | \ - / | \ - M0 M1 M2 - | | | - A_0 A_0 B_0 - - Without storing hicharchy information, there will be no way to tell apart - `myStruct.M0.A0` from `myStruct.M1.A0` since IRStructKey/IRStructField - only has 1 instance of `A::A0` - */ - - enum CopyOptions : int - { - // Copy a flattened-struct into a struct - FlatStructIntoStruct = 0, - - // Copy a struct into a flattened-struct - StructIntoFlatStruct = 1, - }; - - private: - // Children of member if applicable. - Dictionary members; - - // Field correlating to MapStructToFlatStruct Node. - IRInst* node; - IRStructKey* getKey() - { - SLANG_ASSERT(as(node)); - return as(node)->getKey(); - } - IRInst* getNode() { return node; } - IRType* getFieldType() - { - SLANG_ASSERT(as(node)); - return as(node)->getFieldType(); - } - - // Whom node maps to inside target flatStruct - IRStructField* targetMapping; - - auto begin() { return members.begin(); } - auto end() { return members.end(); } - - // Copies members of oldStruct to/from newFlatStruct. Assumes members of val1 maps to - // members in val2 using `MapStructToFlatStruct` - template - static void _emitCopy( - IRBuilder& builder, - IRInst* val1, - IRStructType* type1, - IRInst* val2, - IRStructType* type2, - MapStructToFlatStruct& node) - { - for (auto& field1Pair : node) - { - auto& field1 = field1Pair.second; - - // Get member of val1 - IRInst* fieldAddr1 = nullptr; - if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) - { - fieldAddr1 = builder.emitFieldAddress(type1, val1, field1.getKey()); - } - else - { - if (as(val1)) - val1 = builder.emitLoad(val1); - fieldAddr1 = builder.emitFieldExtract(type1, val1, field1.getKey()); - } - - // If val1 is a struct, recurse - if (auto fieldAsStruct1 = as(field1.getFieldType())) - { - _emitCopy( - builder, - fieldAddr1, - fieldAsStruct1, - val2, - type2, - field1); - continue; - } - - // Get member of val2 which maps to val1.member - auto field2 = field1.getMapping(); - SLANG_ASSERT(field2); - IRInst* fieldAddr2 = nullptr; - if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) - { - if (as(val2)) - val2 = builder.emitLoad(val1); - fieldAddr2 = builder.emitFieldExtract(type2, val2, field2->getKey()); - } - else - { - fieldAddr2 = builder.emitFieldAddress(type2, val2, field2->getKey()); - } - - // Copy val2/val1 member into val1/val2 member - if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) - { - builder.emitStore(fieldAddr1, fieldAddr2); - } - else - { - builder.emitStore(fieldAddr2, fieldAddr1); - } - } - } - - public: - void setNode(IRInst* newNode) { node = newNode; } - // Get 'MapStructToFlatStruct' that is a child of 'parent'. - // Make 'MapStructToFlatStruct' if no 'member' is currently mapped to 'parent'. - MapStructToFlatStruct& getMember(IRStructField* member) { return members[member]; } - MapStructToFlatStruct& operator[](IRStructField* member) { return getMember(member); } - - void setMapping(IRStructField* newTargetMapping) { targetMapping = newTargetMapping; } - // Get 'MapStructToFlatStruct' that is a child of 'parent'. - // Return nullptr if no member is mapped to 'parent' - IRStructField* getMapping() { return targetMapping; } - - // Copies srcVal into dstVal using hicharchy map. - template - void emitCopy(IRBuilder& builder, IRInst* dstVal, IRInst* srcVal) - { - auto dstType = dstVal->getDataType(); - if (auto dstPtrType = as(dstType)) - dstType = dstPtrType->getValueType(); - auto dstStructType = as(dstType); - SLANG_ASSERT(dstStructType); - - auto srcType = srcVal->getDataType(); - if (auto srcPtrType = as(srcType)) - srcType = srcPtrType->getValueType(); - auto srcStructType = as(srcType); - SLANG_ASSERT(srcStructType); - - if constexpr (copyOptions == (int)CopyOptions::FlatStructIntoStruct) - { - // CopyOptions::FlatStructIntoStruct copy a flattened-struct (mapped member) into a - // struct - SLANG_ASSERT(node == dstStructType); - _emitCopy( - builder, - dstVal, - dstStructType, - srcVal, - srcStructType, - *this); - } - else - { - // CopyOptions::StructIntoFlatStruct copy a struct into a flattened-struct - SLANG_ASSERT(node == srcStructType); - _emitCopy( - builder, - srcVal, - srcStructType, - dstVal, - dstStructType, - *this); - } - } - }; - - IRStructType* _flattenNestedStructs( - IRBuilder& builder, - IRStructType* dst, - IRStructType* src, - IRSemanticDecoration* parentSemanticDecoration, - IRLayoutDecoration* parentLayout, - MapStructToFlatStruct& mapFieldToField, - HashSet& varsWithSemanticInfo) - { - // For all fields ('oldField') of a struct do the following: - // 1. Check for 'decorations which carry semantic info' (IRSemanticDecoration, - // IRLayoutDecoration), store these if found. - // * Do not propagate semantic info if the current node has *any* form of semantic - // information. - // Update varsWithSemanticInfo. - // 2. If IRStructType: - // 2a. Recurse this function with 'decorations that carry semantic info' from parent. - // 3. If not IRStructType: - // 3a. Emit 'newField' with 'newKey' equal to 'oldField' and 'oldKey', respectively, - // where 'oldKey' is the key corresponding to 'oldField'. - // Add 'decorations which carry semantic info' to 'newField', and move all decorations - // of 'oldKey' to 'newKey'. - // 3b. Store a mapping from 'oldField' to 'newField' in 'mapFieldToField'. This info is - // needed to copy between types. - for (auto oldField : src->getFields()) - { - auto& fieldMappingNode = mapFieldToField[oldField]; - fieldMappingNode.setNode(oldField); - - // step 1 - bool foundSemanticDecor = false; - auto oldKey = oldField->getKey(); - IRSemanticDecoration* fieldSemanticDecoration = parentSemanticDecoration; - if (auto oldSemanticDecoration = oldKey->findDecoration()) - { - foundSemanticDecor = true; - fieldSemanticDecoration = oldSemanticDecoration; - parentLayout = nullptr; - } - - IRLayoutDecoration* fieldLayout = parentLayout; - if (auto oldLayout = oldKey->findDecoration()) - { - fieldLayout = oldLayout; - if (!foundSemanticDecor) - fieldSemanticDecoration = nullptr; - } - if (fieldSemanticDecoration != parentSemanticDecoration || parentLayout != fieldLayout) - varsWithSemanticInfo.add(oldField); - - // step 2a - if (auto structFieldType = as(oldField->getFieldType())) - { - _flattenNestedStructs( - builder, - dst, - structFieldType, - fieldSemanticDecoration, - fieldLayout, - fieldMappingNode, - varsWithSemanticInfo); - continue; - } - - // step 3a - auto newKey = builder.createStructKey(); - oldKey->transferDecorationsTo(newKey); - - auto newField = builder.createStructField(dst, newKey, oldField->getFieldType()); - copyNameHintAndDebugDecorations(newField, oldField); - - if (fieldSemanticDecoration) - builder.addSemanticDecoration( - newKey, - fieldSemanticDecoration->getSemanticName(), - fieldSemanticDecoration->getSemanticIndex()); - - if (fieldLayout) - { - IRLayout* oldLayout = fieldLayout->getLayout(); - List instToCopy; - // Only copy certain decorations needed for resolving system semantics - for (UInt i = 0; i < oldLayout->getOperandCount(); i++) - { - auto operand = oldLayout->getOperand(i); - if (as(operand) || as(operand) || - as(operand) || as(operand)) - instToCopy.add(operand); - } - IRVarLayout* newLayout = builder.getVarLayout(instToCopy); - builder.addLayoutDecoration(newKey, newLayout); - } - // step 3b - fieldMappingNode.setMapping(newField); - } - - return dst; - } - - // Returns a `IRStructType*` without any `IRStructType*` members. `src` may be returned if there - // was no struct flattening. - // @param mapFieldToField Behavior maps all `IRStructField` of `src` to the new struct - // `IRStructFields`s - IRStructType* maybeFlattenNestedStructs( - IRBuilder& builder, - IRStructType* src, - MapStructToFlatStruct& mapFieldToField, - HashSet& varsWithSemanticInfo) - { - // Find all values inside struct that need flattening and legalization. - bool hasStructTypeMembers = false; - for (auto field : src->getFields()) - { - if (as(field->getFieldType())) - { - hasStructTypeMembers = true; - break; - } - } - if (!hasStructTypeMembers) - return src; - - // We need to: - // 1. Make new struct 1:1 with old struct but without nestested structs (flatten) - // 2. Ensure semantic attributes propegate. This will create overlapping semantics (can be - // handled later). - // 3. Store the mapping from old to new struct fields to allow copying a old-struct to - // new-struct. - builder.setInsertAfter(src); - auto newStruct = builder.createStructType(); - copyNameHintAndDebugDecorations(newStruct, src); - mapFieldToField.setNode(src); - return _flattenNestedStructs( - builder, - newStruct, - src, - nullptr, - nullptr, - mapFieldToField, - varsWithSemanticInfo); + builder.emitStore(pair.dest, builder.emitLoad(pair.value)); } +} - // Replaces all 'IRReturn' by copying the current 'IRReturn' to a new var of type 'newType'. - // Copying logic from 'IRReturn' to 'newType' is controlled by 'copyLogicFunc' function. - template - void _replaceAllReturnInst( - IRBuilder& builder, - IRFunc* targetFunc, - IRStructType* newType, - CopyLogicFunc copyLogicFunc) +static void legalizeFunc(IRFunc* func) +{ + // Insert casts to convert integer return types + auto funcReturnType = func->getResultType(); + if (isIntegralType(funcReturnType)) { - for (auto block : targetFunc->getBlocks()) + for (auto block : func->getBlocks()) { if (auto returnInst = as(block->getTerminator())) { - builder.setInsertBefore(returnInst); - auto returnVal = returnInst->getVal(); - returnInst->setOperand(0, copyLogicFunc(builder, newType, returnVal)); - } - } - } - - UInt _returnNonOverlappingAttributeIndex(std::set& usedSemanticIndex) - { - // Find first unused semantic index of equal semantic type - // to fill any gaps in user set semantic bindings - UInt prev = 0; - for (auto i : usedSemanticIndex) - { - if (i > prev + 1) - { - break; - } - prev = i; - } - usedSemanticIndex.insert(prev + 1); - return prev + 1; - } - - template - struct AttributeParentPair - { - IRLayoutDecoration* layoutDecor; - T* attr; - }; - - IRLayoutDecoration* _replaceAttributeOfLayout( - IRBuilder& builder, - IRLayoutDecoration* parentLayoutDecor, - IRInst* instToReplace, - IRInst* instToReplaceWith) - { - // Replace `instToReplace` with a `instToReplaceWith` - - auto layout = parentLayoutDecor->getLayout(); - // Find the exact same decoration `instToReplace` in-case multiple of the same type exist - List opList; - opList.add(instToReplaceWith); - for (UInt i = 0; i < layout->getOperandCount(); i++) - { - if (layout->getOperand(i) != instToReplace) - opList.add(layout->getOperand(i)); - } - auto newLayoutDecor = builder.addLayoutDecoration( - parentLayoutDecor->getParent(), - builder.getVarLayout(opList)); - parentLayoutDecor->removeAndDeallocate(); - return newLayoutDecor; - } - - IRLayoutDecoration* _simplifyUserSemanticNames( - IRBuilder& builder, - IRLayoutDecoration* layoutDecor) - { - // Ensure all 'ExplicitIndex' semantics such as "SV_TARGET0" are simplified into - // ("SV_TARGET", 0) using 'IRUserSemanticAttr' This is done to ensure we can check semantic - // groups using 'IRUserSemanticAttr1->getName() == IRUserSemanticAttr2->getName()' - SLANG_ASSERT(layoutDecor); - auto layout = layoutDecor->getLayout(); - List layoutOps; - layoutOps.reserve(3); - bool changed = false; - for (auto attr : layout->getAllAttrs()) - { - if (auto userSemantic = as(attr)) - { - UnownedStringSlice outName; - UnownedStringSlice outIndex; - bool hasStringIndex = splitNameAndIndex(userSemantic->getName(), outName, outIndex); - - changed = true; - auto newDecoration = builder.getUserSemanticAttr( - userSemanticName, - hasStringIndex ? stringToInt(outIndex) : 0); - userSemantic->replaceUsesWith(newDecoration); - userSemantic->removeAndDeallocate(); - userSemantic = newDecoration; - - layoutOps.add(userSemantic); - continue; - } - layoutOps.add(attr); - } - if (changed) - { - auto parent = layoutDecor->parent; - layoutDecor->removeAndDeallocate(); - builder.addLayoutDecoration(parent, builder.getVarLayout(layoutOps)); - } - return layoutDecor; - } - - // Find overlapping field semantics and legalize them - void fixFieldSemanticsOfFlatStruct(IRStructType* structType) - { - // Goal is to ensure we do not have overlapping semantics for the user defined semantics: - // Note that in WGSL, the semantics can be either `builtin` without index or `location` with - // index. - /* - // Assume the following code - struct Fragment - { - float4 p0 : SV_POSITION; - float2 p1 : TEXCOORD0; - float2 p2 : TEXCOORD1; - float3 p3 : COLOR0; - float3 p4 : COLOR1; - }; - - // Translates into - struct Fragment - { - float4 p0 : BUILTIN_POSITION; - float2 p1 : LOCATION_0; - float2 p2 : LOCATION_1; - float3 p3 : LOCATION_2; - float3 p4 : LOCATION_3; - }; - */ - - // For Multi-Render-Target, the semantic index must be translated to `location` with - // the same index. Assume the following code - /* - struct Fragment - { - float4 p0 : SV_TARGET1; - float4 p1 : SV_TARGET0; - }; - - // Translates into - struct Fragment - { - float4 p0 : LOCATION_1; - float4 p1 : LOCATION_0; - }; - */ - - IRBuilder builder(this->m_module); - - List overlappingSemanticsDecor; - Dictionary>> - usedSemanticIndexSemanticDecor; - - List> overlappingVarOffset; - Dictionary>> usedSemanticIndexVarOffset; - - List> overlappingUserSemantic; - Dictionary>> - usedSemanticIndexUserSemantic; - - // We store a map from old `IRLayoutDecoration*` to new `IRLayoutDecoration*` since when - // legalizing we may destroy and remake a `IRLayoutDecoration*` - Dictionary oldLayoutDecorToNew; - - // Collect all "semantic info carrying decorations". Any collected decoration will - // fill up their respective 'Dictionary>' - // to keep track of in-use offsets for a semantic type. - // Example: IRSemanticDecoration with name of "SV_TARGET1". - // * This will have SEMANTIC_TYPE of "sv_target". - // * This will use up index '1' - // - // Now if a second equal semantic "SV_TARGET1" is found, we add this decoration to - // a list of 'overlapping semantic info decorations' so we can legalize this - // 'semantic info decoration' later. - // - // NOTE: this is a flat struct, all members are children of the initial - // IRStructType. - for (auto field : structType->getFields()) - { - auto key = field->getKey(); - if (auto semanticDecoration = key->findDecoration()) - { - auto semanticName = semanticDecoration->getSemanticName(); - - // sv_target is treated as a user-semantic because it should be emitted with - // @location like how the user semantics are emitted. - // For fragment shader, only sv_target will user @location, and for non-fragment - // shaders, sv_target is not valid. - bool isUserSemantic = - (semanticName.startsWithCaseInsensitive(toSlice("sv_target")) || - !semanticName.startsWithCaseInsensitive(toSlice("sv_"))); - - // Ensure names are in a uniform lowercase format so we can bunch together simmilar - // semantics. - UnownedStringSlice outName; - UnownedStringSlice outIndex; - bool hasStringIndex = splitNameAndIndex(semanticName, outName, outIndex); - - // user semantics gets all same semantic-name. - auto loweredName = String(outName).toLower(); - auto loweredNameSlice = - isUserSemantic ? userSemanticName : loweredName.getUnownedSlice(); - auto newDecoration = builder.addSemanticDecoration( - key, - loweredNameSlice, - hasStringIndex ? stringToInt(outIndex) : 0); - semanticDecoration->replaceUsesWith(newDecoration); - semanticDecoration->removeAndDeallocate(); - semanticDecoration = newDecoration; - - auto& semanticUse = - usedSemanticIndexSemanticDecor[semanticDecoration->getSemanticName()]; - if (semanticUse.find(semanticDecoration->getSemanticIndex()) != semanticUse.end()) - overlappingSemanticsDecor.add(semanticDecoration); - else - semanticUse.insert(semanticDecoration->getSemanticIndex()); - } - if (auto layoutDecor = key->findDecoration()) - { - // Ensure names are in a uniform lowercase format so we can bunch together simmilar - // semantics - layoutDecor = _simplifyUserSemanticNames(builder, layoutDecor); - oldLayoutDecorToNew[layoutDecor] = layoutDecor; - auto layout = layoutDecor->getLayout(); - for (auto attr : layout->getAllAttrs()) - { - if (auto offset = as(attr)) - { - auto& semanticUse = usedSemanticIndexVarOffset[offset->getResourceKind()]; - if (semanticUse.find(offset->getOffset()) != semanticUse.end()) - overlappingVarOffset.add({layoutDecor, offset}); - else - semanticUse.insert(offset->getOffset()); - } - else if (auto userSemantic = as(attr)) - { - auto& semanticUse = usedSemanticIndexUserSemantic[userSemantic->getName()]; - if (semanticUse.find(userSemantic->getIndex()) != semanticUse.end()) - overlappingUserSemantic.add({layoutDecor, userSemantic}); - else - semanticUse.insert(userSemantic->getIndex()); - } - } - } - } - - // Legalize all overlapping 'semantic info decorations' - for (auto decor : overlappingSemanticsDecor) - { - auto newOffset = _returnNonOverlappingAttributeIndex( - usedSemanticIndexSemanticDecor[decor->getSemanticName()]); - builder.addSemanticDecoration( - decor->getParent(), - decor->getSemanticName(), - (int)newOffset); - decor->removeAndDeallocate(); - } - for (auto& varOffset : overlappingVarOffset) - { - auto newOffset = _returnNonOverlappingAttributeIndex( - usedSemanticIndexVarOffset[varOffset.attr->getResourceKind()]); - auto newVarOffset = builder.getVarOffsetAttr( - varOffset.attr->getResourceKind(), - newOffset, - varOffset.attr->getSpace()); - oldLayoutDecorToNew[varOffset.layoutDecor] = _replaceAttributeOfLayout( - builder, - oldLayoutDecorToNew[varOffset.layoutDecor], - varOffset.attr, - newVarOffset); - } - for (auto& userSemantic : overlappingUserSemantic) - { - auto newOffset = _returnNonOverlappingAttributeIndex( - usedSemanticIndexUserSemantic[userSemantic.attr->getName()]); - auto newUserSemantic = - builder.getUserSemanticAttr(userSemantic.attr->getName(), newOffset); - oldLayoutDecorToNew[userSemantic.layoutDecor] = _replaceAttributeOfLayout( - builder, - oldLayoutDecorToNew[userSemantic.layoutDecor], - userSemantic.attr, - newUserSemantic); - } - } - - void wrapReturnValueInStruct(EntryPointInfo entryPoint) - { - // Wrap return value into a struct if it is not already a struct. - // For example, given this entry point: - // ``` - // float4 main() : SV_Target { return float3(1,2,3); } - // ``` - // We are going to transform it into: - // ``` - // struct Output { - // float4 value : SV_Target; - // }; - // Output main() { return {float3(1,2,3)}; } - - auto func = entryPoint.entryPointFunc; - - auto returnType = func->getResultType(); - if (as(returnType)) - return; - auto entryPointLayoutDecor = func->findDecoration(); - if (!entryPointLayoutDecor) - return; - auto entryPointLayout = as(entryPointLayoutDecor->getLayout()); - if (!entryPointLayout) - return; - auto resultLayout = entryPointLayout->getResultLayout(); - - // If return type is already a struct, just make sure every field has a semantic. - if (auto returnStructType = as(returnType)) - { - IRBuilder builder(func); - MapStructToFlatStruct mapOldFieldToNewField; - // Flatten result struct type to ensure we do not have nested semantics - auto flattenedStruct = maybeFlattenNestedStructs( - builder, - returnStructType, - mapOldFieldToNewField, - semanticInfoToRemove); - if (returnStructType != flattenedStruct) - { - // Replace all return-values with the flattenedStruct we made. - _replaceAllReturnInst( - builder, - func, - flattenedStruct, - [&](IRBuilder& copyBuilder, IRStructType* dstType, IRInst* srcVal) -> IRInst* - { - auto srcStructType = as(srcVal->getDataType()); - SLANG_ASSERT(srcStructType); - auto dstVal = copyBuilder.emitVar(dstType); - mapOldFieldToNewField.emitCopy<( - int)MapStructToFlatStruct::CopyOptions::StructIntoFlatStruct>( - copyBuilder, - dstVal, - srcVal); - return builder.emitLoad(dstVal); - }); - fixUpFuncType(func, flattenedStruct); - } - // Ensure non-overlapping semantics - fixFieldSemanticsOfFlatStruct(flattenedStruct); - ensureStructHasUserSemantic( - flattenedStruct, - resultLayout); - return; - } - - IRBuilder builder(func); - builder.setInsertBefore(func); - IRStructType* structType = builder.createStructType(); - auto stageText = getStageText(entryPoint.entryPointDecor->getProfile().getStage()); - builder.addNameHintDecoration( - structType, - (String(stageText) + toSlice("Output")).getUnownedSlice()); - auto key = builder.createStructKey(); - builder.addNameHintDecoration(key, toSlice("output")); - builder.addLayoutDecoration(key, resultLayout); - builder.createStructField(structType, key, returnType); - IRStructTypeLayout::Builder structTypeLayoutBuilder(&builder); - structTypeLayoutBuilder.addField(key, resultLayout); - auto typeLayout = structTypeLayoutBuilder.build(); - IRVarLayout::Builder varLayoutBuilder(&builder, typeLayout); - auto varLayout = varLayoutBuilder.build(); - ensureStructHasUserSemantic(structType, varLayout); - - _replaceAllReturnInst( - builder, - func, - structType, - [](IRBuilder& copyBuilder, IRStructType* dstType, IRInst* srcVal) -> IRInst* - { return copyBuilder.emitMakeStruct(dstType, 1, &srcVal); }); - - // Assign an appropriate system value semantic for stage output - auto stage = entryPoint.entryPointDecor->getProfile().getStage(); - switch (stage) - { - case Stage::Compute: - case Stage::Fragment: - { - IRInst* operands[] = { - builder.getStringValue(userSemanticName), - builder.getIntValue(builder.getIntType(), 0)}; - builder.addDecoration( - key, - kIROp_SemanticDecoration, - operands, - SLANG_COUNT_OF(operands)); - break; - } - case Stage::Vertex: - { - builder.addTargetSystemValueDecoration(key, toSlice("position")); - break; - } - default: - SLANG_ASSERT(false); - return; - } - - fixUpFuncType(func, structType); - } - - IRInst* tryConvertValue(IRBuilder& builder, IRInst* val, IRType* toType) - { - auto fromType = val->getFullType(); - if (auto fromVector = as(fromType)) - { - if (auto toVector = as(toType)) - { - if (fromVector->getElementCount() != toVector->getElementCount()) - { - fromType = builder.getVectorType( - fromVector->getElementType(), - toVector->getElementCount()); - val = builder.emitVectorReshape(fromType, val); - } - } - else if (as(toType)) - { - UInt index = 0; - val = builder.emitSwizzle(fromVector->getElementType(), val, 1, &index); - if (toType->getOp() == kIROp_VoidType) - return nullptr; - } - } - else if (auto fromBasicType = as(fromType)) - { - if (fromBasicType->getOp() == kIROp_VoidType) - return nullptr; - if (!as(toType)) - return nullptr; - if (toType->getOp() == kIROp_VoidType) - return nullptr; - } - else - { - return nullptr; - } - return builder.emitCast(toType, val); - } - - struct SystemValLegalizationWorkItem - { - IRInst* var; - IRType* varType; - String attrName; - UInt attrIndex; - }; - - std::optional tryToMakeSystemValWorkItem( - IRInst* var, - IRType* varType) - { - if (auto semanticDecoration = var->findDecoration()) - { - if (semanticDecoration->getSemanticName().startsWithCaseInsensitive(toSlice("sv_"))) - { - return { - {var, - varType, - String(semanticDecoration->getSemanticName()).toLower(), - (UInt)semanticDecoration->getSemanticIndex()}}; - } - } - - auto layoutDecor = var->findDecoration(); - if (!layoutDecor) - return {}; - auto sysValAttr = layoutDecor->findAttr(); - if (!sysValAttr) - return {}; - auto semanticName = String(sysValAttr->getName()); - auto sysAttrIndex = sysValAttr->getIndex(); - - return {{var, varType, semanticName, sysAttrIndex}}; - } - - List collectSystemValFromEntryPoint(EntryPointInfo entryPoint) - { - List systemValWorkItems; - for (auto param : entryPoint.entryPointFunc->getParams()) - { - if (auto structType = as(param->getDataType())) - { - for (auto field : structType->getFields()) - { - // Nested struct-s are flattened already by flattenInputParameters(). - SLANG_ASSERT(!as(field->getFieldType())); - - auto key = field->getKey(); - auto fieldType = field->getFieldType(); - auto maybeWorkItem = tryToMakeSystemValWorkItem(key, fieldType); - if (maybeWorkItem.has_value()) - systemValWorkItems.add(std::move(maybeWorkItem.value())); - } - continue; - } - - auto maybeWorkItem = tryToMakeSystemValWorkItem(param, param->getFullType()); - if (maybeWorkItem.has_value()) - systemValWorkItems.add(std::move(maybeWorkItem.value())); - } - return systemValWorkItems; - } - - void legalizeSystemValue(EntryPointInfo entryPoint, SystemValLegalizationWorkItem& workItem) - { - IRBuilder builder(entryPoint.entryPointFunc); - - auto var = workItem.var; - auto varType = workItem.varType; - auto semanticName = workItem.attrName; - - auto indexAsString = String(workItem.attrIndex); - auto info = getSystemValueInfo(semanticName, &indexAsString, var); - - if (info.isUnsupported) - { - reportUnsupportedSystemAttribute(var, semanticName); - return; - } - if (!info.permittedTypes.getCount()) - return; - - builder.addTargetSystemValueDecoration(var, info.wgslSystemValueName.getUnownedSlice()); - - bool varTypeIsPermitted = false; - for (auto& permittedType : info.permittedTypes) - { - varTypeIsPermitted = varTypeIsPermitted || permittedType == varType; - } - - if (!varTypeIsPermitted) - { - // Note: we do not currently prefer any conversion - // example: - // * allowed types for semantic: `float4`, `uint4`, `int4` - // * user used, `float2` - // * Slang will equally prefer `float4` to `uint4` to `int4`. - // This means the type may lose data if slang selects `uint4` or `int4`. - bool foundAConversion = false; - for (auto permittedType : info.permittedTypes) - { - var->setFullType(permittedType); - builder.setInsertBefore( - entryPoint.entryPointFunc->getFirstBlock()->getFirstOrdinaryInst()); - - // get uses before we `tryConvertValue` since this creates a new use - List uses; - for (auto use = var->firstUse; use; use = use->nextUse) - uses.add(use); - - auto convertedValue = tryConvertValue(builder, var, varType); - if (convertedValue == nullptr) - continue; - - foundAConversion = true; - copyNameHintAndDebugDecorations(convertedValue, var); - - for (auto use : uses) - builder.replaceOperand(use, convertedValue); - } - if (!foundAConversion) - { - // If we can't convert the value, report an error. - for (auto permittedType : info.permittedTypes) + auto returnedValue = returnInst->getOperand(0); + auto returnedValueType = returnedValue->getDataType(); + if (isIntegralType(returnedValueType)) { - StringBuilder typeNameSB; - getTypeNameHint(typeNameSB, permittedType); - m_sink->diagnose( - var->sourceLoc, - Diagnostics::systemValueTypeIncompatible, - semanticName, - typeNameSB.produceString()); + IRBuilder builder(returnInst); + builder.setInsertBefore(returnInst); + auto newOp = builder.emitCast(funcReturnType, returnedValue); + builder.replaceOperand(returnInst->getOperands(), newOp); } } } } +} - void legalizeSystemValueParameters(EntryPointInfo entryPoint) - { - List systemValWorkItems = - collectSystemValFromEntryPoint(entryPoint); - - for (auto index = 0; index < systemValWorkItems.getCount(); index++) - { - legalizeSystemValue(entryPoint, systemValWorkItems[index]); - } - fixUpFuncType(entryPoint.entryPointFunc); - } - - void legalizeEntryPointForWGSL(EntryPointInfo entryPoint) - { - // If the entrypoint is receiving varying inputs as a pointer, turn it into a value. - depointerizeInputParams(entryPoint.entryPointFunc); - - // Input Parameter Legalize - flattenInputParameters(entryPoint); - - // System Value Legalize - legalizeSystemValueParameters(entryPoint); - - // Output Value Legalize - wrapReturnValueInStruct(entryPoint); - } - - void legalizeCall(IRCall* call) - { - // WGSL does not allow forming a pointer to a sub part of a composite value. - // For example, if we have - // ``` - // struct S { float x; float y; }; - // void foo(inout float v) { v = 1.0f; } - // void main() { S s; foo(s.x); } - // ``` - // The call to `foo(s.x)` is illegal in WGSL because `s.x` is a sub part of `s`. - // And trying to form `&s.x` in WGSL is illegal. - // To work around this, we will create a local variable to hold the sub part of - // the composite value. - // And then pass the local variable to the function. - // After the call, we will write back the local variable to the sub part of the - // composite value. - // - IRBuilder builder(call); - builder.setInsertBefore(call); - struct WritebackPair - { - IRInst* dest; - IRInst* value; - }; - ShortList pendingWritebacks; - - for (UInt i = 0; i < call->getArgCount(); i++) - { - auto arg = call->getArg(i); - auto ptrType = as(arg->getDataType()); - if (!ptrType) - continue; - switch (arg->getOp()) - { - case kIROp_Var: - case kIROp_Param: - case kIROp_GlobalParam: - case kIROp_GlobalVar: - continue; - default: - break; - } - - // Create a local variable to hold the input argument. - auto var = builder.emitVar(ptrType->getValueType(), AddressSpace::Function); - - // Store the input argument into the local variable. - builder.emitStore(var, builder.emitLoad(arg)); - builder.replaceOperand(call->getArgs() + i, var); - pendingWritebacks.add({arg, var}); - } - - // Perform writebacks after the call. - builder.setInsertAfter(call); - for (auto& pair : pendingWritebacks) - { - builder.emitStore(pair.dest, builder.emitLoad(pair.value)); - } - } - - void legalizeFunc(IRFunc* func) - { - // Insert casts to convert integer return types - auto funcReturnType = func->getResultType(); - if (isIntegralType(funcReturnType)) - { - for (auto block : func->getBlocks()) - { - if (auto returnInst = as(block->getTerminator())) - { - auto returnedValue = returnInst->getOperand(0); - auto returnedValueType = returnedValue->getDataType(); - if (isIntegralType(returnedValueType)) - { - IRBuilder builder(returnInst); - builder.setInsertBefore(returnInst); - auto newOp = builder.emitCast(funcReturnType, returnedValue); - builder.replaceOperand(returnInst->getOperands(), newOp); - } - } - } - } - } - - void legalizeSwitch(IRSwitch* switchInst) - { - // WGSL Requires all switch statements to contain a default case. - // If the switch statement does not contain a default case, we will add one. - if (switchInst->getDefaultLabel() != switchInst->getBreakLabel()) - return; - IRBuilder builder(switchInst); - auto defaultBlock = builder.createBlock(); - builder.setInsertInto(defaultBlock); - builder.emitBranch(switchInst->getBreakLabel()); - defaultBlock->insertBefore(switchInst->getBreakLabel()); - List cases; - for (UInt i = 0; i < switchInst->getCaseCount(); i++) - { - cases.add(switchInst->getCaseValue(i)); - cases.add(switchInst->getCaseLabel(i)); - } - builder.setInsertBefore(switchInst); - auto newSwitch = builder.emitSwitch( - switchInst->getCondition(), - switchInst->getBreakLabel(), - defaultBlock, - (UInt)cases.getCount(), - cases.getBuffer()); - switchInst->transferDecorationsTo(newSwitch); - switchInst->removeAndDeallocate(); - } - - void processInst(IRInst* inst) - { - switch (inst->getOp()) - { - case kIROp_Call: - legalizeCall(static_cast(inst)); - break; - - case kIROp_Switch: - legalizeSwitch(as(inst)); - break; - - // For all binary operators, make sure both side of the operator have the same type - // (vector-ness and matrix-ness). - case kIROp_Add: - case kIROp_Sub: - case kIROp_Mul: - case kIROp_Div: - case kIROp_FRem: - case kIROp_IRem: - case kIROp_And: - case kIROp_Or: - case kIROp_BitAnd: - case kIROp_BitOr: - case kIROp_BitXor: - case kIROp_Lsh: - case kIROp_Rsh: - case kIROp_Eql: - case kIROp_Neq: - case kIROp_Greater: - case kIROp_Less: - case kIROp_Geq: - case kIROp_Leq: - legalizeBinaryOp(inst); - break; +static void legalizeSwitch(IRSwitch* switchInst) +{ + // WGSL Requires all switch statements to contain a default case. + // If the switch statement does not contain a default case, we will add one. + if (switchInst->getDefaultLabel() != switchInst->getBreakLabel()) + return; + IRBuilder builder(switchInst); + auto defaultBlock = builder.createBlock(); + builder.setInsertInto(defaultBlock); + builder.emitBranch(switchInst->getBreakLabel()); + defaultBlock->insertBefore(switchInst->getBreakLabel()); + List cases; + for (UInt i = 0; i < switchInst->getCaseCount(); i++) + { + cases.add(switchInst->getCaseValue(i)); + cases.add(switchInst->getCaseLabel(i)); + } + builder.setInsertBefore(switchInst); + auto newSwitch = builder.emitSwitch( + switchInst->getCondition(), + switchInst->getBreakLabel(), + defaultBlock, + (UInt)cases.getCount(), + cases.getBuffer()); + switchInst->transferDecorationsTo(newSwitch); + switchInst->removeAndDeallocate(); +} - case kIROp_Func: - legalizeFunc(static_cast(inst)); - [[fallthrough]]; - default: - for (auto child : inst->getModifiableChildren()) - { - processInst(child); - } +static void processInst(IRInst* inst) +{ + switch (inst->getOp()) + { + case kIROp_Call: + legalizeCall(static_cast(inst)); + break; + + case kIROp_Switch: + legalizeSwitch(as(inst)); + break; + + // For all binary operators, make sure both side of the operator have the same type + // (vector-ness and matrix-ness). + case kIROp_Add: + case kIROp_Sub: + case kIROp_Mul: + case kIROp_Div: + case kIROp_FRem: + case kIROp_IRem: + case kIROp_And: + case kIROp_Or: + case kIROp_BitAnd: + case kIROp_BitOr: + case kIROp_BitXor: + case kIROp_Lsh: + case kIROp_Rsh: + case kIROp_Eql: + case kIROp_Neq: + case kIROp_Greater: + case kIROp_Less: + case kIROp_Geq: + case kIROp_Leq: + legalizeBinaryOp(inst); + break; + + case kIROp_Func: + legalizeFunc(static_cast(inst)); + [[fallthrough]]; + default: + for (auto child : inst->getModifiableChildren()) + { + processInst(child); } } -}; +} struct GlobalInstInliningContext : public GlobalInstInliningContextGeneric { @@ -1583,13 +215,10 @@ void legalizeIRForWGSL(IRModule* module, DiagnosticSink* sink) entryPoints.add(info); } - LegalizeWGSLEntryPointContext context(sink, module); - for (auto entryPoint : entryPoints) - context.legalizeEntryPointForWGSL(entryPoint); - context.removeSemanticLayoutsFromLegalizedStructs(); + legalizeEntryPointVaryingParamsForWGSL(module, sink, entryPoints); // Go through every instruction in the module and legalize them as needed. - context.processInst(module->getModuleInst()); + processInst(module->getModuleInst()); // Some global insts are illegal, e.g. function calls. // We need to inline and remove those. From 80a330f0cae4d164eb6b2fe513bde2cf9a3b01c1 Mon Sep 17 00:00:00 2001 From: Julius Ikkala Date: Thu, 16 Jan 2025 06:35:11 +0200 Subject: [PATCH 03/23] Implement AnyValue marshalling for 8-bit integers (#6059) * Implement anyvalue marshalling for 8-bit integers * Fix missing offset from int8/uint8 case * Disable anyvalue 8-bit test for DXIL Because it doesn't support 8-bit values anyway. --------- Co-authored-by: Yong He --- .../slang/slang-ir-any-value-marshalling.cpp | 49 ++++++++++++- tests/compute/pack-any-value-8bit.slang | 68 +++++++++++++++++++ .../pack-any-value-8bit.slang.expected.txt | 4 ++ 3 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 tests/compute/pack-any-value-8bit.slang create mode 100644 tests/compute/pack-any-value-8bit.slang.expected.txt diff --git a/source/slang/slang-ir-any-value-marshalling.cpp b/source/slang/slang-ir-any-value-marshalling.cpp index 673a3d1689..d1be600356 100644 --- a/source/slang/slang-ir-any-value-marshalling.cpp +++ b/source/slang/slang-ir-any-value-marshalling.cpp @@ -375,6 +375,25 @@ struct AnyValueMarshallingContext } case kIROp_Int8Type: case kIROp_UInt8Type: + if (fieldOffset < static_cast(anyValInfo->fieldKeys.getCount())) + { + auto srcVal = builder->emitLoad(concreteVar); + srcVal = builder->emitCast(builder->getType(kIROp_UIntType), srcVal); + auto dstAddr = builder->emitFieldAddress( + uintPtrType, + anyValueVar, + anyValInfo->fieldKeys[fieldOffset]); + auto dstVal = builder->emitLoad(dstAddr); + dstVal = builder->emitBitfieldInsert( + dstVal->getFullType(), + dstVal, + srcVal, + builder->getIntValue(builder->getUIntType(), 8 * intraFieldOffset), + builder->getIntValue(builder->getUIntType(), 8)); + builder->emitStore(dstAddr, dstVal); + } + advanceOffset(1); + break; case kIROp_UInt64Type: case kIROp_Int64Type: case kIROp_DoubleType: @@ -600,11 +619,35 @@ struct AnyValueMarshallingContext advanceOffset(2); break; } + case kIROp_Int8Type: + case kIROp_UInt8Type: + if (fieldOffset < static_cast(anyValInfo->fieldKeys.getCount())) + { + auto srcAddr = builder->emitFieldAddress( + uintPtrType, + anyValueVar, + anyValInfo->fieldKeys[fieldOffset]); + auto srcVal = builder->emitLoad(srcAddr); + srcVal = builder->emitBitfieldExtract( + srcVal->getFullType(), + srcVal, + builder->getIntValue(builder->getUIntType(), 8 * intraFieldOffset), + builder->getIntValue(builder->getUIntType(), 8)); + if (dataType->getOp() == kIROp_Int8Type) + { + srcVal = builder->emitCast(builder->getType(kIROp_Int8Type), srcVal); + } + else + { + srcVal = builder->emitCast(builder->getType(kIROp_UInt8Type), srcVal); + } + builder->emitStore(concreteVar, srcVal); + } + advanceOffset(1); + break; case kIROp_UInt64Type: case kIROp_Int64Type: case kIROp_DoubleType: - case kIROp_Int8Type: - case kIROp_UInt8Type: case kIROp_PtrType: #if SLANG_PTR_IS_64 case kIROp_IntPtrType: @@ -832,7 +875,7 @@ SlangInt _getAnyValueSizeRaw(IRType* type, SlangInt offset) return alignUp(offset, 2) + 2; case kIROp_UInt8Type: case kIROp_Int8Type: - return -1; + return offset + 1; case kIROp_VectorType: { auto vectorType = static_cast(type); diff --git a/tests/compute/pack-any-value-8bit.slang b/tests/compute/pack-any-value-8bit.slang new file mode 100644 index 0000000000..a9050d52c5 --- /dev/null +++ b/tests/compute/pack-any-value-8bit.slang @@ -0,0 +1,68 @@ +// Test anyvalue packing of 8bit types. + +//TEST_DISABLED(compute):COMPARE_COMPUTE_EX:-slang -compute -cuda -output-using-type +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -output-using-type -render-feature int16 +//TEST_DISABLED(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_2 -use-dxil -output-using-type + +[anyValueSize(20)] +interface IInterface +{ + float run(); +} + +struct Val : IInterface +{ + int8_t v0; + uint8_t v1; + float f0; + uint16_t v2; + uint8_t v3; + uint32_t v4; + uint8_t v5; + float run() + { + return v0 + v1 + f0 + v2 + v3 + v4 + v5; + } +}; + +struct UserDefinedPackedType +{ + uint values[5]; +}; + +//TEST_INPUT:ubuffer(data=[0 0 0], stride=4):out,name=gOutputBuffer +RWStructuredBuffer gOutputBuffer; + +//TEST_INPUT: type_conformance Val:IInterface = 11 + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + UserDefinedPackedType objStorage; + objStorage.values[0] = 0xA5A50201; + objStorage.values[1] = asuint(3.0f); + objStorage.values[2] = 4u|(0xA505u<<16u); + objStorage.values[3] = 6; + objStorage.values[4] = 7; + + IInterface dynamicObj = createDynamicObject(11, objStorage); + float result = dynamicObj.run(); + gOutputBuffer[0] = result; + + Val v; + v.v0 = 1; + v.v1 = 2; + v.f0 = 3; + v.v2 = 4; + v.v3 = 5; + v.v4 = 6; + v.v5 = 7; + + IInterface dynamicObj1 = createDynamicObject(11, v);; + gOutputBuffer[1] = dynamicObj1.run(); + + var packed = reinterpret(v); + var unpacked = reinterpret(packed); + gOutputBuffer[2] = unpacked.run(); +} + diff --git a/tests/compute/pack-any-value-8bit.slang.expected.txt b/tests/compute/pack-any-value-8bit.slang.expected.txt new file mode 100644 index 0000000000..a4cb5b798d --- /dev/null +++ b/tests/compute/pack-any-value-8bit.slang.expected.txt @@ -0,0 +1,4 @@ +type: float +28.0 +28.0 +28.0 From 6db69eae7ecfb5111a48723d3f3c4104c7da880a Mon Sep 17 00:00:00 2001 From: Anders Leino Date: Thu, 16 Jan 2025 06:37:16 +0200 Subject: [PATCH 04/23] Emit errors when seeing a matrix with row/col count of 1 (#6044) Such matrices aren't well supported except by D3D targets. Therefore, generate an error rather than outputting invalid code for non-D3D targets. This closes #5987. Co-authored-by: Yong He --- source/slang/slang-diagnostic-defs.h | 6 ++++++ source/slang/slang-emit.cpp | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index d86cd8be2a..eacc345a73 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -1658,6 +1658,12 @@ DIAGNOSTIC( overloadedParameterToHigherOrderFunction, "passing overloaded functions to higher order functions is not supported") +DIAGNOSTIC( + 39999, + Error, + matrixColumnOrRowCountIsOne, + "matrices with 1 column or row are not supported by the current code generation target") + // 38xxx DIAGNOSTIC( diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index ee25822670..b17bc03f30 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -555,6 +555,24 @@ static void unexportNonEmbeddableIR(CodeGenTarget target, IRModule* irModule) } } +static void validateMatrixDimensions(DiagnosticSink* sink, IRModule* module) +{ + for (auto globalInst : module->getGlobalInsts()) + { + if (auto matrixType = as(globalInst)) + { + auto colCount = as(matrixType->getColumnCount()); + auto rowCount = as(matrixType->getRowCount()); + + if ((rowCount && (rowCount->getValue() == 1)) || + (colCount && (colCount->getValue() == 1))) + { + sink->diagnose(matrixType->sourceLoc, Diagnostics::matrixColumnOrRowCountIsOne); + } + } + } +} + Result linkAndOptimizeIR( CodeGenContext* codeGenContext, LinkingAndOptimizationOptions const& options, @@ -1504,6 +1522,10 @@ Result linkAndOptimizeIR( #endif validateIRModuleIfEnabled(codeGenContext, irModule); + // Make sure there are no matrices with 1 row/column, except for D3D targets where it's allowed. + if (!isD3DTarget(targetRequest)) + validateMatrixDimensions(sink, irModule); + // The resource-based specialization pass above // may create specialized versions of functions, but // it does not try to completely eliminate the original From 387f2be1e48a805ef0da34510a5ae0ebc0ba9c3e Mon Sep 17 00:00:00 2001 From: Yong He Date: Wed, 15 Jan 2025 20:43:01 -0800 Subject: [PATCH 05/23] Inline global constants that contains opaque handles for legalization. (#6098) * Inline global constants that contains opaque handles for legalization. * Add diagnostics on opaque type global variables. * Fix. * Fix test. --- source/slang/slang-ast-decl.h | 1 + source/slang/slang-check-conformance.cpp | 9 +++- source/slang/slang-check-decl.cpp | 12 +++++ source/slang/slang-diagnostic-defs.h | 4 ++ source/slang/slang-emit.cpp | 3 ++ .../slang/slang-ir-legalize-global-values.cpp | 45 +++++++++++++++++ .../slang/slang-ir-legalize-global-values.h | 8 +++ source/slang/slang-ir-legalize-types.cpp | 2 +- tests/bugs/gh-4874-2.slang | 41 +++++++++++++++ tests/bugs/gh-4874.slang | 50 +++++++++++++++++++ .../variable-redeclaration.slang.expected | 6 +++ 11 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 tests/bugs/gh-4874-2.slang create mode 100644 tests/bugs/gh-4874.slang diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h index 911455f17e..81e7337c71 100644 --- a/source/slang/slang-ast-decl.h +++ b/source/slang/slang-ast-decl.h @@ -141,6 +141,7 @@ enum class TypeTag Unsized = 1, Incomplete = 2, LinkTimeSized = 4, + Opaque = 8, }; // Declaration of a type that represents some sort of aggregate diff --git a/source/slang/slang-check-conformance.cpp b/source/slang/slang-check-conformance.cpp index 499b409eae..71b87f447e 100644 --- a/source/slang/slang-check-conformance.cpp +++ b/source/slang/slang-check-conformance.cpp @@ -347,9 +347,16 @@ TypeTag SemanticsVisitor::getTypeTags(Type* type) if (auto parameterGroupType = as(type)) { auto elementTags = getTypeTags(parameterGroupType->getElementType()); - elementTags = (TypeTag)((int)elementTags & ~(int)TypeTag::Unsized); + elementTags = (TypeTag)(((int)elementTags & ~(int)TypeTag::Unsized) | (int)TypeTag::Opaque); return elementTags; } + else if ( + as(type) || as(type) || + as(type) || as(type) || + as(type)) + { + return TypeTag::Opaque; + } else if (auto declRefType = as(type)) { if (auto aggTypeDecl = as(declRefType->getDeclRef())) diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 04d5b7a75b..2a5b5a4e35 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -2392,6 +2392,18 @@ void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl) { getSink()->diagnose(varDecl, Diagnostics::varCannotBeUnsized); } + + bool isOpaque = (((int)varTypeTags & (int)TypeTag::Opaque) != 0); + if (isOpaque && isGlobalDecl(varDecl) && !varDecl->hasModifier() && + varDecl->hasModifier()) + { + // Opaque type global variable must be const. + getSink()->diagnose(varDecl, Diagnostics::globalVarCannotHaveOpaqueType); + if (varDecl->initExpr) + getSink()->diagnose(varDecl, Diagnostics::doYouMeanStaticConst); + else + getSink()->diagnose(varDecl, Diagnostics::doYouMeanUniform); + } } if (auto elementType = getConstantBufferElementType(varDecl->getType())) diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index eacc345a73..7eddc16a93 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -738,6 +738,10 @@ DIAGNOSTIC( cannotSpecializeGeneric, "cannot specialize generic '$0' with the provided arguments.") +DIAGNOSTIC(30076, Error, globalVarCannotHaveOpaqueType, "global variable cannot have opaque type.") +DIAGNOSTIC(-1, Note, doYouMeanStaticConst, "do you intend to define a `static const` instead?") +DIAGNOSTIC(-1, Note, doYouMeanUniform, "do you intend to define a `uniform` parameter instead?") + DIAGNOSTIC( 30100, Error, diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index b17bc03f30..176cd05586 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -52,6 +52,7 @@ #include "slang-ir-insts.h" #include "slang-ir-layout.h" #include "slang-ir-legalize-array-return-type.h" +#include "slang-ir-legalize-global-values.h" #include "slang-ir-legalize-image-subscript.h" #include "slang-ir-legalize-mesh-outputs.h" #include "slang-ir-legalize-uniform-buffer-load.h" @@ -1073,6 +1074,8 @@ Result linkAndOptimizeIR( // We don't need the legalize pass for C/C++ based types if (options.shouldLegalizeExistentialAndResourceTypes) { + inlineGlobalConstantsForLegalization(irModule); + // The Slang language allows interfaces to be used like // ordinary types (including placing them in constant // buffers and entry-point parameter lists), but then diff --git a/source/slang/slang-ir-legalize-global-values.cpp b/source/slang/slang-ir-legalize-global-values.cpp index 92a1edb2b8..63dbed3bdc 100644 --- a/source/slang/slang-ir-legalize-global-values.cpp +++ b/source/slang/slang-ir-legalize-global-values.cpp @@ -223,6 +223,8 @@ IRInst* GlobalInstInliningContextGeneric::maybeInlineGlobalValue( } if (as(inst)) return inst; + if (!wrapReferences) + return inst; // If we encounter a global value that shouldn't be inlined, e.g. a const literal, // we should insert a GlobalValueRef() inst to wrap around it, so all the dependent @@ -244,4 +246,47 @@ IRInst* GlobalInstInliningContextGeneric::maybeInlineGlobalValue( return inlineInst(builder, cloneEnv, inst); } +struct GlobalInstLegalizationInliningContext : public GlobalInstInliningContextGeneric +{ + static bool isSimpleConstantType(IRType* type) + { + for (;;) + { + if (!type) + return true; + if (as(type)) + return true; + if (as(type)) + return true; + if (as(type)) + return true; + if (auto arrayType = as(type)) + { + type = arrayType->getElementType(); + continue; + } + return false; + } + } + bool isLegalGlobalInstForTarget(IRInst* inst) override + { + auto type = inst->getDataType(); + return isSimpleConstantType(type); + } + + bool isInlinableGlobalInstForTarget(IRInst* /* inst */) override { return false; } + + bool shouldBeInlinedForTarget(IRInst* /* user */) override { return false; } + + IRInst* getOutsideASM(IRInst* beforeInst) override { return beforeInst; } +}; + +void inlineGlobalConstantsForLegalization(IRModule* module) +{ + GlobalInstLegalizationInliningContext context; + + context.wrapReferences = false; + context.inlineGlobalValuesAndRemoveIfUnused(module); +} + } // namespace Slang diff --git a/source/slang/slang-ir-legalize-global-values.h b/source/slang/slang-ir-legalize-global-values.h index c563b02c9c..87b9fccc2f 100644 --- a/source/slang/slang-ir-legalize-global-values.h +++ b/source/slang/slang-ir-legalize-global-values.h @@ -12,6 +12,7 @@ struct IRModule; struct GlobalInstInliningContextGeneric { Dictionary m_mapGlobalInstToShouldInline; + bool wrapReferences = true; // Target-specific control over how inlining happens virtual bool isLegalGlobalInstForTarget(IRInst* inst) = 0; @@ -43,4 +44,11 @@ struct GlobalInstInliningContextGeneric IRInst* inst, IRCloneEnv& cloneEnv); }; + +// For global constant values that are resource typed or struct containing resource types, +// we need to inline their uses to concrete function bodies so they can be legalized during +// resource legalization. +void inlineGlobalConstantsForLegalization(IRModule* module); + + } // namespace Slang diff --git a/source/slang/slang-ir-legalize-types.cpp b/source/slang/slang-ir-legalize-types.cpp index ae95ca8da3..8eb70596fd 100644 --- a/source/slang/slang-ir-legalize-types.cpp +++ b/source/slang/slang-ir-legalize-types.cpp @@ -572,7 +572,7 @@ struct LegalReturnBuilder // We expect that the parent is actually an IR function. // // TODO: What about the case where we have an `IRGlobalVar` - // of a type that needs legalization, and teh variable has + // of a type that needs legalization, and the variable has // an initializer? For now, I believe that case is disallowed // in the legalization for global variables. // diff --git a/tests/bugs/gh-4874-2.slang b/tests/bugs/gh-4874-2.slang new file mode 100644 index 0000000000..7f6944f7a4 --- /dev/null +++ b/tests/bugs/gh-4874-2.slang @@ -0,0 +1,41 @@ +// Same to gh-4874.slang, but defining the global resource typed variables +// without the "const" modifier. This should lead to a compile time error. +// +//TEST:SIMPLE(filecheck=CHECK): -target spirv +RWStructuredBuffer outputBuffer; + +RWStructuredBuffer inputBuffer; + +// CHECK: ([[# @LINE+1]]): error 30076: +static RWStructuredBuffer gBuffer = inputBuffer; + +struct Stuff +{ + __init(RWStructuredBuffer a1, RWStructuredBuffer b2) + { + a = a1; + b = b2; + } + + RWStructuredBuffer a; + RWStructuredBuffer b; +} + +// CHECK: ([[# @LINE+1]]): error 30076: +static Stuff gStuff = Stuff( inputBuffer, inputBuffer ); + +uint test(uint x) +{ + return gBuffer[x] + + gStuff.a[x + 1] * 16 + + gStuff.b[x + 2] * 256; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + let tid = dispatchThreadID.x; + let inVal = tid; + let outVal = test(inVal); + outputBuffer[tid] = outVal; +} \ No newline at end of file diff --git a/tests/bugs/gh-4874.slang b/tests/bugs/gh-4874.slang new file mode 100644 index 0000000000..403f6fc501 --- /dev/null +++ b/tests/bugs/gh-4874.slang @@ -0,0 +1,50 @@ +// type-legalize-global-with-init.slang +// +// Confirm that type legalization can handle a global constant +// with a resource type or a type that recursively contains +// resources. +// +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj -vk +// +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +//TEST_INPUT:ubuffer(data=[1 2 3 4 5 6 7 8], stride=4):name=inputBuffer +RWStructuredBuffer inputBuffer; + +static const RWStructuredBuffer gBuffer = inputBuffer; + +struct Stuff +{ + __init(RWStructuredBuffer a1, RWStructuredBuffer b2) + { + a = a1; + b = b2; + } + + RWStructuredBuffer a; + RWStructuredBuffer b; +} + +static const Stuff gStuff = Stuff( inputBuffer, inputBuffer ); + +uint test(uint x) +{ + return gBuffer[x] + + gStuff.a[x + 1] * 16 + + gStuff.b[x + 2] * 256; +} + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + let tid = dispatchThreadID.x; + let inVal = tid; + let outVal = test(inVal); + outputBuffer[tid] = outVal; + // CHECK: 321 + // CHECK: 432 + // CHECK: 543 + // CHECK: 654 +} \ No newline at end of file diff --git a/tests/diagnostics/variable-redeclaration.slang.expected b/tests/diagnostics/variable-redeclaration.slang.expected index 944037b1cd..1998c13c82 100644 --- a/tests/diagnostics/variable-redeclaration.slang.expected +++ b/tests/diagnostics/variable-redeclaration.slang.expected @@ -18,6 +18,12 @@ tests/diagnostics/variable-redeclaration.slang(51): error 30200: declaration of tests/diagnostics/variable-redeclaration.slang(50): note: see previous declaration of 'size' int size, ^~~~ +tests/diagnostics/variable-redeclaration.slang(14): error 30076: global variable cannot have opaque type. +static Texture2D gA; + ^~ +tests/diagnostics/variable-redeclaration.slang(14): note: do you intend to define a `uniform` parameter instead? +static Texture2D gA; + ^~ tests/diagnostics/variable-redeclaration.slang(21): error 30200: declaration of 'y' conflicts with existing declaration int y = x; ^ From edf5e9f97015a09fa0f2bed58d6a04744992d23f Mon Sep 17 00:00:00 2001 From: Yong He Date: Wed, 15 Jan 2025 22:47:13 -0800 Subject: [PATCH 06/23] Fix argument buffer tier2 layout computation. (#6101) --- source/slang/slang-type-layout.cpp | 75 ++++++++++++++++++- ...test-argument-buffer-tier-2-reflection.cpp | 70 +++++++++++++++++ 2 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 tools/slang-unit-test/unit-test-argument-buffer-tier-2-reflection.cpp diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index 20a18a5ac9..a412bf5b28 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -661,7 +661,7 @@ struct MetalLayoutRulesImpl : public CPULayoutRulesImpl auto alignedElementCount = 1 << Math::Log2Ceil((uint32_t)elementCount); // Metal aligns vectors to 2/4 element boundaries. - size_t size = elementSize * elementCount; + size_t size = alignedElementCount * elementSize; size_t alignment = alignedElementCount * elementSize; SimpleLayoutInfo vectorInfo; @@ -1147,6 +1147,14 @@ struct MetalLayoutRulesFamilyImpl : LayoutRulesFamilyImpl LayoutRulesImpl* getStructuredBufferRules(CompilerOptionSet& compilerOptions) override; }; +struct MetalArgumentBufferTier2LayoutRulesFamilyImpl : MetalLayoutRulesFamilyImpl +{ + virtual LayoutRulesImpl* getConstantBufferRules( + CompilerOptionSet& compilerOptions, + Type* containerType) override; + virtual LayoutRulesImpl* getParameterBlockRules(CompilerOptionSet& compilerOptions) override; +}; + struct WGSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl { virtual LayoutRulesImpl* getAnyValueRules() override; @@ -1175,6 +1183,7 @@ HLSLLayoutRulesFamilyImpl kHLSLLayoutRulesFamilyImpl; CPULayoutRulesFamilyImpl kCPULayoutRulesFamilyImpl; CUDALayoutRulesFamilyImpl kCUDALayoutRulesFamilyImpl; MetalLayoutRulesFamilyImpl kMetalLayoutRulesFamilyImpl; +MetalArgumentBufferTier2LayoutRulesFamilyImpl kMetalArgumentBufferTier2LayoutRulesFamilyImpl; WGSLLayoutRulesFamilyImpl kWGSLLayoutRulesFamilyImpl; // CPU case @@ -1969,8 +1978,44 @@ struct MetalArgumentBufferElementLayoutRulesImpl : ObjectLayoutRulesImpl, Defaul } }; +struct MetalTier2ObjectLayoutRulesImpl : ObjectLayoutRulesImpl +{ + virtual ObjectLayoutInfo GetObjectLayout(ShaderParameterKind kind, const Options& /* options */) + override + { + switch (kind) + { + case ShaderParameterKind::ConstantBuffer: + case ShaderParameterKind::ParameterBlock: + case ShaderParameterKind::StructuredBuffer: + case ShaderParameterKind::MutableStructuredBuffer: + case ShaderParameterKind::RawBuffer: + case ShaderParameterKind::Buffer: + case ShaderParameterKind::MutableRawBuffer: + case ShaderParameterKind::MutableBuffer: + case ShaderParameterKind::ShaderStorageBuffer: + case ShaderParameterKind::AccelerationStructure: + return SimpleLayoutInfo(LayoutResourceKind::Uniform, 8, 8); + case ShaderParameterKind::AppendConsumeStructuredBuffer: + return SimpleLayoutInfo(LayoutResourceKind::Uniform, 16, 8); + case ShaderParameterKind::MutableTexture: + case ShaderParameterKind::TextureUniformBuffer: + case ShaderParameterKind::Texture: + case ShaderParameterKind::SamplerState: + return SimpleLayoutInfo(LayoutResourceKind::Uniform, 8, 8); + case ShaderParameterKind::TextureSampler: + case ShaderParameterKind::MutableTextureSampler: + return SimpleLayoutInfo(LayoutResourceKind::Uniform, 16, 8); + default: + SLANG_UNEXPECTED("unhandled shader parameter kind"); + UNREACHABLE_RETURN(SimpleLayoutInfo()); + } + } +}; + static MetalObjectLayoutRulesImpl kMetalObjectLayoutRulesImpl; static MetalArgumentBufferElementLayoutRulesImpl kMetalArgumentBufferElementLayoutRulesImpl; +static MetalTier2ObjectLayoutRulesImpl kMetalTier2ObjectLayoutRulesImpl; static MetalLayoutRulesImpl kMetalLayoutRulesImpl; LayoutRulesImpl kMetalAnyValueLayoutRulesImpl_ = { @@ -1991,6 +2036,18 @@ LayoutRulesImpl kMetalParameterBlockLayoutRulesImpl_ = { &kMetalArgumentBufferElementLayoutRulesImpl, }; +LayoutRulesImpl kMetalTier2ConstantBufferLayoutRulesImpl_ = { + &kMetalLayoutRulesFamilyImpl, + &kMetalLayoutRulesImpl, + &kMetalTier2ObjectLayoutRulesImpl, +}; + +LayoutRulesImpl kMetalTier2ParameterBlockLayoutRulesImpl_ = { + &kMetalLayoutRulesFamilyImpl, + &kMetalLayoutRulesImpl, + &kMetalTier2ObjectLayoutRulesImpl, +}; + LayoutRulesImpl kMetalStructuredBufferLayoutRulesImpl_ = { &kMetalLayoutRulesFamilyImpl, &kMetalLayoutRulesImpl, @@ -2079,6 +2136,20 @@ LayoutRulesImpl* MetalLayoutRulesFamilyImpl::getHitAttributesParameterRules() return nullptr; } +LayoutRulesImpl* MetalArgumentBufferTier2LayoutRulesFamilyImpl::getConstantBufferRules( + CompilerOptionSet&, + Type*) +{ + return &kMetalTier2ConstantBufferLayoutRulesImpl_; +} + +LayoutRulesImpl* MetalArgumentBufferTier2LayoutRulesFamilyImpl::getParameterBlockRules( + CompilerOptionSet&) +{ + return &kMetalTier2ParameterBlockLayoutRulesImpl_; +} + + // WGSL Family LayoutRulesImpl kWGSLConstantBufferLayoutRulesImpl_ = { @@ -2229,7 +2300,7 @@ TypeLayoutContext getInitialLayoutContextForTarget( rulesFamily = getDefaultLayoutRulesFamilyForTarget(targetReq); break; case slang::LayoutRules::MetalArgumentBufferTier2: - rulesFamily = &kCPULayoutRulesFamilyImpl; + rulesFamily = &kMetalArgumentBufferTier2LayoutRulesFamilyImpl; break; } diff --git a/tools/slang-unit-test/unit-test-argument-buffer-tier-2-reflection.cpp b/tools/slang-unit-test/unit-test-argument-buffer-tier-2-reflection.cpp new file mode 100644 index 0000000000..a63f38f89c --- /dev/null +++ b/tools/slang-unit-test/unit-test-argument-buffer-tier-2-reflection.cpp @@ -0,0 +1,70 @@ +// unit-test-argument-buffer-tier-2-reflection.cpp + +#include "../../source/core/slang-io.h" +#include "../../source/core/slang-process.h" +#include "slang-com-ptr.h" +#include "slang.h" +#include "unit-test/slang-unit-test.h" + +#include +#include + +using namespace Slang; + +// Test metal argument buffer tier2 layout rules. + +SLANG_UNIT_TEST(metalArgumentBufferTier2Reflection) +{ + const char* userSourceBody = R"( + struct A + { + float3 one; + float3 two; + float three; + } + + struct Args{ + ParameterBlock a; + } + ParameterBlock argument_buffer; + RWStructuredBuffer outputBuffer; + + [numthreads(1,1,1)] + void computeMain() + { + outputBuffer[0] = argument_buffer.a.two.x; + } + )"; + + auto moduleName = "moduleG" + String(Process::getId()); + String userSource = "import " + moduleName + ";\n" + userSourceBody; + ComPtr globalSession; + SLANG_CHECK(slang_createGlobalSession(SLANG_API_VERSION, globalSession.writeRef()) == SLANG_OK); + slang::TargetDesc targetDesc = {}; + targetDesc.format = SLANG_SPIRV; + targetDesc.profile = globalSession->findProfile("spirv_1_5"); + slang::SessionDesc sessionDesc = {}; + sessionDesc.targetCount = 1; + sessionDesc.targets = &targetDesc; + ComPtr session; + SLANG_CHECK(globalSession->createSession(sessionDesc, session.writeRef()) == SLANG_OK); + + ComPtr diagnosticBlob; + auto module = session->loadModuleFromSourceString( + "m", + "m.slang", + userSourceBody, + diagnosticBlob.writeRef()); + SLANG_CHECK(module != nullptr); + + auto layout = module->getLayout(); + + auto type = layout->findTypeByName("A"); + auto typeLayout = layout->getTypeLayout(type, slang::LayoutRules::MetalArgumentBufferTier2); + SLANG_CHECK(typeLayout->getFieldByIndex(0)->getOffset() == 0); + SLANG_CHECK(typeLayout->getFieldByIndex(0)->getTypeLayout()->getSize() == 16); + SLANG_CHECK(typeLayout->getFieldByIndex(1)->getOffset() == 16); + SLANG_CHECK(typeLayout->getFieldByIndex(1)->getTypeLayout()->getSize() == 16); + SLANG_CHECK(typeLayout->getFieldByIndex(2)->getOffset() == 32); + SLANG_CHECK(typeLayout->getFieldByIndex(2)->getTypeLayout()->getSize() == 4); +} From 9167e0d04c2d57593506feca94aacf73aad17b65 Mon Sep 17 00:00:00 2001 From: Darren Wihandi <65404740+fairywreath@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:19:47 -0500 Subject: [PATCH 07/23] support SV_ViewIndex for Metal (#6103) Co-authored-by: Yong He --- .../slang/slang-ir-legalize-varying-params.cpp | 4 +++- .../system-values-draw-parameters.slang | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/source/slang/slang-ir-legalize-varying-params.cpp b/source/slang/slang-ir-legalize-varying-params.cpp index 69d62c8bf8..9720084ba3 100644 --- a/source/slang/slang-ir-legalize-varying-params.cpp +++ b/source/slang/slang-ir-legalize-varying-params.cpp @@ -3201,7 +3201,9 @@ class LegalizeMetalEntryPointContext : public LegalizeShaderEntryPointContext } case SystemValueSemanticName::ViewID: { - result.isUnsupported = true; + result.systemValueName = toSlice("amplification_id"); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt)); + result.permittedTypes.add(builder.getBasicType(BaseType::UInt16)); break; } case SystemValueSemanticName::ViewportArrayIndex: diff --git a/tests/hlsl-intrinsic/system-values-draw-parameters.slang b/tests/hlsl-intrinsic/system-values-draw-parameters.slang index ea84ac1165..e453669398 100644 --- a/tests/hlsl-intrinsic/system-values-draw-parameters.slang +++ b/tests/hlsl-intrinsic/system-values-draw-parameters.slang @@ -3,6 +3,8 @@ //TEST:SIMPLE(filecheck=CHECK_HLSL): -entry main -stage vertex -target hlsl //TEST:SIMPLE(filecheck=CHECK_METAL): -entry main -stage vertex -target metal +StructuredBuffer scales; + struct VSInput { uint vertexID : SV_VertexID; @@ -16,19 +18,30 @@ struct VSOutput VSOutput main(VSInput input, uint startVertexLocation : SV_StartVertexLocation, - uint startInstanceLocation : SV_StartInstanceLocation) + uint startInstanceLocation : SV_StartInstanceLocation, + uint viewIndex : SV_ViewID) { VSOutput output; float x = (float)(input.vertexID + startVertexLocation) * 0.1f; float y = (float)(input.instanceID + startInstanceLocation) * 0.2f; - output.position = float4(x, y, 0.0f, 1.0f); + output.position = float4(x, y, 0.0f, 1.0f) * scales[viewIndex]; + + // CHECK_SPIRV: BuiltIn BaseVertex + // CHECK_GLSL: gl_BaseVertex + // CHECK_HLSL: SV_StartVertexLocation + // CHECK_METAL: base_vertex // CHECK_SPIRV: BuiltIn BaseInstance // CHECK_GLSL: gl_BaseInstance // CHECK_HLSL: SV_StartInstanceLocation // CHECK_METAL: base_instance + // CHECK_SPIRV: BuiltIn ViewIndex + // CHECK_GLSL: gl_ViewIndex + // CHECK_HLSL: SV_ViewID + // CHECK_METAL: amplification_id + return output; } From ad7d13a8a934a56db87a4ece4b1afb0f1db1c9d9 Mon Sep 17 00:00:00 2001 From: Darren Wihandi <65404740+fairywreath@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:21:17 -0500 Subject: [PATCH 08/23] Implement Packed Dot Product intrinsics (#6068) * implement dot acc intrinsics * fix sm version * fix test * improve comment --------- Co-authored-by: Yong He --- source/slang/hlsl.meta.slang | 72 +++++++++++++++++-- tests/hlsl-intrinsic/dot-accumulate.slang | 55 ++++++++++++++ .../dot-accumulate.slang.expected.txt | 4 ++ 3 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 tests/hlsl-intrinsic/dot-accumulate.slang create mode 100644 tests/hlsl-intrinsic/dot-accumulate.slang.expected.txt diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 11c4ab6f45..d620197f37 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -16760,20 +16760,80 @@ static const uint HIT_KIND_TRIANGLE_BACK_FACE = 255; // // Shader Model 6.4 +// @public: // -/// Treats `left` and `right` as 4-component vectors of `UInt8` and computes `dot(left, right) + acc` +/// Treats `x` and `y` as 4-component vectors of `UInt8` and computes `dot(x, y) + acc` /// @category math -uint dot4add_u8packed(uint left, uint right, uint acc); +[__readNone] +[ForceInline] +[require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl, sm_6_4)] +uint dot4add_u8packed(uint x, uint y, uint acc) +{ + __target_switch + { + case hlsl: __intrinsic_asm "dot4add_u8packed"; + case wgsl: __intrinsic_asm "(dot4U8Packed($0, $1) + $2)"; + case spirv: + // OpUDotAccSat cannot be used as there should not be any saturation. + return spirv_asm + { + OpCapability DotProduct; + OpCapability DotProductInput4x8BitPacked; + OpExtension "SPV_KHR_integer_dot_product"; + %dotResult = OpUDot $$uint $x $y 0; + result:$$uint = OpIAdd %dotResult $acc; + }; + default: + uint4 vecX = unpack_u8u32(uint8_t4_packed(x)); + uint4 vecY = unpack_u8u32(uint8_t4_packed(y)); + return dot(vecX, vecY) + acc; + } +} -/// Treats `left` and `right` as 4-component vectors of `Int8` and computes `dot(left, right) + acc` +/// Treats `x` and `y` as 4-component vectors of `int8` and computes `dot(x, y) + acc` /// @category math -int dot4add_i8packed(uint left, uint right, int acc); +[__readNone] +[ForceInline] +[require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl, sm_6_4)] +int dot4add_i8packed(uint x, uint y, int acc) +{ + __target_switch + { + case hlsl: __intrinsic_asm "dot4add_i8packed"; + case wgsl: __intrinsic_asm "(dot4I8Packed($0, $1) + $2)"; + case spirv: + // OpSDottAccSat cannot be used as there should not be any saturation. + return spirv_asm + { + OpCapability DotProduct; + OpCapability DotProductInput4x8BitPacked; + OpExtension "SPV_KHR_integer_dot_product"; + %dotResult = OpSDot $$int $x $y 0; + result:$$int = OpIAdd %dotResult $acc; + }; + default: + int4 vecX = unpack_s8s32(int8_t4_packed(x)); + int4 vecY = unpack_s8s32(int8_t4_packed(y)); + return dot(vecX, vecY) + acc; + } +} -/// Computes `dot(left, right) + acc`. +/// Computes `dot(x, y) + acc`. /// May not produce infinities or NaNs for intermediate results that overflow the range of `half` /// @category math -float dot2add(float2 left, float2 right, float acc); +[__readNone] +[ForceInline] +[require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl, sm_6_4)] +float dot2add(half2 x, half2 y, float acc) +{ + __target_switch + { + case hlsl: __intrinsic_asm "dot2add"; + default: + return float(dot(x, y)) + acc; + } +} // // Shader Model 6.5 diff --git a/tests/hlsl-intrinsic/dot-accumulate.slang b/tests/hlsl-intrinsic/dot-accumulate.slang new file mode 100644 index 0000000000..113ae40e35 --- /dev/null +++ b/tests/hlsl-intrinsic/dot-accumulate.slang @@ -0,0 +1,55 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj -output-using-type +// Does not run on DX11 as SM 6.4 is required. +//DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx11 +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile cs_6_4 -use-dxil -shaderobj -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX:-metal -compute -shaderobj -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX:-wgsl -compute -shaderobj -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX:-cuda -compute -shaderobj -g0 -output-using-type +//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -shaderobj -output-using-type + +//TEST_INPUT:ubuffer(data=[0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer outputBuffer; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint outputIndex = 0; + + // + // dot4add_u8packed() + // [4 3 2 1] dot [1 2 4 2] + 5 + // (4 * 1) + (3 * 2) + (2 * 4) + (1 * 2) + 5 = 25 + // + uint unsignedX = 0x01020304U; + uint unsignedY = 0x02040201U; + uint unsignedAcc = 5U; + uint unsignedResult = dot4add_u8packed(unsignedX, unsignedY, unsignedAcc); + outputBuffer[outputIndex++] = unsignedResult; + + // + // dot4add_i8packed() + // [6 2 3 -1] dot [-2 -6 2 6] - 100 + // (6 * -2) + (2 * -6) + (3 * 2) + (-1 * 6) - 100 = -124 + // + int signedX = 0xFF030206; + int signedY = 0x0602FAFE; + int signedAcc = -100; + int signedResult = dot4add_i8packed(signedX, signedY, signedAcc); + outputBuffer[outputIndex++] = signedResult; + + // + // dot2add() + // [10.8 -3.3] dot [1.4 -20.3] - 2.11 + // (10.8 * 1.4) + (-3.3 * -20.3) - 2.0 = 80.11 + // + half2 half2X = half2(half(10.8), half(-3.3)); + half2 half2Y = half2(half(1.4), half(-20.3)); + + // `half2Acc` is assigned -2.0 here. + // Thread index is used so that `half2Acc` will not be implicitly emitted as literal `-2.0` which + // may be treated as a double by DXC and cause it to fail to compile because no overload exists for `dot2add` that + // accepts double. + float half2Acc = float(dispatchThreadID.x + 1) * -2.0f; + float half2Result = dot2add(half2X, half2Y, half2Acc); + outputBuffer[outputIndex++] = int(half2Result); +} diff --git a/tests/hlsl-intrinsic/dot-accumulate.slang.expected.txt b/tests/hlsl-intrinsic/dot-accumulate.slang.expected.txt new file mode 100644 index 0000000000..184864973f --- /dev/null +++ b/tests/hlsl-intrinsic/dot-accumulate.slang.expected.txt @@ -0,0 +1,4 @@ +type: int32_t +25 +-124 +80 From e771f1945ed692168a2634d66a0907acc9c68858 Mon Sep 17 00:00:00 2001 From: Anders Leino Date: Thu, 16 Jan 2025 19:23:35 +0200 Subject: [PATCH 09/23] =?UTF-8?q?C-like=20emitter:=20Add=20parenthesis=20w?= =?UTF-8?q?hen=20combining=20relational=20and=20bitwise=E2=80=A6=20(#6070)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * C-like emitter: Add redundant parentheses in several cases This is required since the Dawn WGSL compiler requires parentheses even though precedence rules could resolve order of operations. This closes #6005. * Fix tests/metal/byte-address-buffer The output now includes parentheses around shift expressions appearing as operands in bitwise expressions, so update the test accordingly. * format code --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: Yong He --- source/slang/slang-emit-c-like.cpp | 178 +++++--------------------- tests/metal/byte-address-buffer.slang | 8 +- 2 files changed, 38 insertions(+), 148 deletions(-) diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index d3a9359ff2..eaf7ef028b 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -706,6 +706,35 @@ void CLikeSourceEmitter::emitLivenessImpl(IRInst* inst) // Expressions // +static bool isBitLogicalOrRelationalOrEquality(EPrecedence prec) +{ + switch (prec) + { + case EPrecedence::kEPrecedence_And_Left: + case EPrecedence::kEPrecedence_And_Right: + case EPrecedence::kEPrecedence_BitAnd_Left: + case EPrecedence::kEPrecedence_BitAnd_Right: + case EPrecedence::kEPrecedence_BitOr_Left: + case EPrecedence::kEPrecedence_BitOr_Right: + case EPrecedence::kEPrecedence_BitXor_Left: + case EPrecedence::kEPrecedence_BitXor_Right: + case EPrecedence::kEPrecedence_Or_Left: + case EPrecedence::kEPrecedence_Or_Right: + case EPrecedence::kEPrecedence_Relational_Left: + case EPrecedence::kEPrecedence_Relational_Right: + case EPrecedence::kEPrecedence_Shift_Left: + case EPrecedence::kEPrecedence_Shift_Right: + case EPrecedence::kEPrecedence_Equality_Left: + case EPrecedence::kEPrecedence_Equality_Right: + return true; + + default: + break; + } + + return false; +} + bool CLikeSourceEmitter::maybeEmitParens(EmitOpInfo& outerPrec, const EmitOpInfo& prec) { bool needParens = (prec.leftPrecedence <= outerPrec.leftPrecedence) || @@ -715,152 +744,13 @@ bool CLikeSourceEmitter::maybeEmitParens(EmitOpInfo& outerPrec, const EmitOpInfo // for common mistakes when parentheses are not used with certain combinations // of the operations. We emit parentheses to avoid the warnings. // - // a | b & c => a | (b & c) - if (prec.leftPrecedence == EPrecedence::kEPrecedence_BitAnd_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_BitOr_Right) - { - needParens = true; - } - // a & b | c => (a & b) | c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_BitAnd_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_BitOr_Left) - { - needParens = true; - } - // a << b + c => a << (b + c) - else if ( - prec.leftPrecedence == EPrecedence::kEPrecedence_Additive_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_Shift_Right) - { - needParens = true; - } - // a + b << c => (a + b) << c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Additive_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_Shift_Left) - { - needParens = true; - } - // a + b & c => (a + b) & c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Additive_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_BitAnd_Left) - { - needParens = true; - } - // a ^ b * c => (a ^ b) * c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_BitXor_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_Multiplicative_Left) - { - needParens = true; - } - // a ^ b + c => a ^ (b + c) - else if ( - prec.leftPrecedence == EPrecedence::kEPrecedence_Additive_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_BitXor_Right) - { - needParens = true; - } - // a + b ^ c => (a + b) ^ c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Additive_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_BitXor_Left) - { - needParens = true; - } - // a | b + c => a | (b + c) - else if ( - prec.leftPrecedence == EPrecedence::kEPrecedence_Additive_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_BitOr_Right) - { - needParens = true; - } - // a + b | c => (a + b) | c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Additive_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_BitOr_Left) - { - needParens = true; - } - // a ^ b * c => a ^ (b * c) - else if ( - prec.leftPrecedence == EPrecedence::kEPrecedence_Multiplicative_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_BitXor_Right) - { - needParens = true; - } - // a * b ^ c => (a * b) ^ c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Multiplicative_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_BitXor_Left) - { - needParens = true; - } - // a | b * c => a | (b * c) - else if ( - prec.leftPrecedence == EPrecedence::kEPrecedence_Multiplicative_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_BitOr_Right) - { - needParens = true; - } - // a * b | c => (a * b) | c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Multiplicative_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_BitOr_Left) - { - needParens = true; - } - // a & b * c => a & (b * c) - else if ( - prec.leftPrecedence == EPrecedence::kEPrecedence_Multiplicative_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_BitAnd_Right) - { - needParens = true; - } - // a * b & c => (a * b) & c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Multiplicative_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_BitAnd_Left) - { - needParens = true; - } - // a << b * c => a << (b * c) - else if ( - prec.leftPrecedence == EPrecedence::kEPrecedence_Multiplicative_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_Shift_Right) - { - needParens = true; - } - // a * b << c => (a * b) << c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Multiplicative_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_Shift_Left) - { - needParens = true; - } - // a != b == c => (a != b) == c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Equality_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_Equality_Left) - { - needParens = true; - } - // a == b < c => a == (b < c) - else if ( - prec.leftPrecedence == EPrecedence::kEPrecedence_Relational_Left && - outerPrec.leftPrecedence == EPrecedence::kEPrecedence_Equality_Right) - { + + if (isBitLogicalOrRelationalOrEquality(prec.leftPrecedence) && + (outerPrec.leftPrecedence > kEPrecedence_Assign_Left)) needParens = true; - } - // a < b == c => (a < b) == c - else if ( - prec.rightPrecedence == EPrecedence::kEPrecedence_Relational_Right && - outerPrec.rightPrecedence == EPrecedence::kEPrecedence_Equality_Left) - { + if (isBitLogicalOrRelationalOrEquality(outerPrec.leftPrecedence) || + isBitLogicalOrRelationalOrEquality(outerPrec.rightPrecedence)) needParens = true; - } if (needParens) { diff --git a/tests/metal/byte-address-buffer.slang b/tests/metal/byte-address-buffer.slang index d4b58061fe..cf704f170f 100644 --- a/tests/metal/byte-address-buffer.slang +++ b/tests/metal/byte-address-buffer.slang @@ -20,11 +20,11 @@ struct TestStruct void main_kernel(uint3 tid: SV_DispatchThreadID) { // CHECK: uint [[WORD0:[a-zA-Z0-9_]+]] = as_type({{.*}}[(int(0))>>2]); - // CHECK: uint8_t [[A:[a-zA-Z0-9_]+]] = uint8_t([[WORD0]] >> 0U & 255U); + // CHECK: uint8_t [[A:[a-zA-Z0-9_]+]] = uint8_t(([[WORD0]] >> 0U) & 255U); // CHECK: uint [[WORD1:[a-zA-Z0-9_]+]] = as_type({{.*}}[(int(0))>>2]); - // CHECK: half [[H:[a-zA-Z0-9_]+]] = as_type(ushort([[WORD1]] >> 16U & 65535U)); + // CHECK: half [[H:[a-zA-Z0-9_]+]] = as_type(ushort(([[WORD1]] >> 16U) & 65535U)); - // CHECK: {{.*}}[(int(128))>>2] = as_type(({{.*}} & 4294967040U) | uint([[A]]) << 0U); - // CHECK: {{.*}}[(int(128))>>2] = as_type(({{.*}} & 65535U) | uint(as_type([[H]])) << 16U); + // CHECK: {{.*}}[(int(128))>>2] = as_type(({{.*}} & 4294967040U) | (uint([[A]]) << 0U)); + // CHECK: {{.*}}[(int(128))>>2] = as_type(({{.*}} & 65535U) | (uint(as_type([[H]])) << 16U)); buffer.Store(128, buffer.Load(0)); } From d3ad6bb4997d3b7ba2dc9653a2d5f7dc965b150f Mon Sep 17 00:00:00 2001 From: Julius Ikkala Date: Fri, 17 Jan 2025 03:06:00 +0200 Subject: [PATCH 10/23] Fix cyclic lookups with UnscopedEnums (#6110) * Fix cyclic lookups with UnscopedEnums * Add test with multiple unscoped enums with explicit types --------- Co-authored-by: Yong He --- source/slang/slang-ast-support-types.h | 1 + source/slang/slang-check-decl.cpp | 11 +++---- source/slang/slang-check-expr.cpp | 3 +- source/slang/slang-check-impl.h | 11 +++++++ source/slang/slang-lookup.cpp | 17 ++++++++-- source/slang/slang-lookup.h | 3 +- .../enums/unscoped-enum-explicit-type-2.slang | 33 +++++++++++++++++++ 7 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 tests/language-feature/enums/unscoped-enum-explicit-type-2.slang diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 21ca27a902..1d3de9c546 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -1225,6 +1225,7 @@ enum class LookupOptions : uint8_t /// checking to see if a keyword is shadowed. IgnoreInheritance = 1 << 4, ///< Lookup only non inheritance children of a struct (including `extension`) + IgnoreTransparentMembers = 1 << 5, }; inline LookupOptions operator&(LookupOptions a, LookupOptions b) { diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 2a5b5a4e35..0319eb2609 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -2897,14 +2897,13 @@ void SemanticsDeclBasesVisitor::visitInheritanceDecl(InheritanceDecl* inheritanc { // check the type being inherited from auto base = inheritanceDecl->base; - Decl* toExclude = nullptr; Decl* parent = getParentDecl(inheritanceDecl); - // We exclude in the case that a circular reference is possible. This is when a parent is a - // transparent decl. If we just blanket "block" all ensure's of a parent a generic may fail when - // trying to fetch a parent + // We exclude transparent members in the case that a circular reference is + // possible. This is when a parent is also a transparent decl. + SemanticsContext context(*this); if (parent->findModifier()) - toExclude = parent; - SemanticsDeclVisitorBase baseVistor(this->withDeclToExcludeFromLookup(toExclude)); + context = context.excludeTransparentMembersFromLookup(); + SemanticsDeclVisitorBase baseVistor(context); baseVistor.CheckConstraintSubType(base); base = baseVistor.TranslateTypeNode(base); inheritanceDecl->base = base; diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index ce62069735..135632ba83 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -3092,7 +3092,8 @@ Expr* SemanticsExprVisitor::visitVarExpr(VarExpr* expr) expr->scope, LookupMask::Default, false, - getDeclToExcludeFromLookup()); + getDeclToExcludeFromLookup(), + getExcludeTransparentMembersFromLookup()); bool diagnosed = false; lookupResult = filterLookupResultByVisibilityAndDiagnose(lookupResult, expr->loc, diagnosed); diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 3ef1e8f3be..6387068146 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -1068,6 +1068,15 @@ struct SemanticsContext Decl* getDeclToExcludeFromLookup() { return m_declToExcludeFromLookup; } + SemanticsContext excludeTransparentMembersFromLookup() + { + SemanticsContext result(*this); + result.m_excludeTransparentMembersFromLookup = true; + return result; + } + + bool getExcludeTransparentMembersFromLookup() { return m_excludeTransparentMembersFromLookup; } + OrderedHashSet* getCapturedTypePacks() { return m_capturedTypePacks; } GLSLBindingOffsetTracker* getGLSLBindingOffsetTracker() @@ -1084,6 +1093,8 @@ struct SemanticsContext Decl* m_declToExcludeFromLookup = nullptr; + bool m_excludeTransparentMembersFromLookup = false; + protected: // TODO: consider making more of this state `private`... diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp index e4f2b188dc..3667e2cd2d 100644 --- a/source/slang/slang-lookup.cpp +++ b/source/slang/slang-lookup.cpp @@ -237,6 +237,12 @@ static void _lookUpDirectAndTransparentMembers( if ((int)request.mask & (int)LookupMask::Attribute) return; + // Also skip transparent members if they're explicitly excluded by the + // request. This prevents cyclic lookups e.g. when looking up UnscopedEnum's + // underlying types. + if (((int)request.options & (int)LookupOptions::IgnoreTransparentMembers) != 0) + return; + for (auto transparentInfo : containerDecl->getTransparentMembers()) { // The reference to the transparent member should use the same @@ -1059,11 +1065,16 @@ LookupResult lookUp( Scope* scope, LookupMask mask, bool considerAllLocalNamesInScope, - Decl* declToExclude) + Decl* declToExclude, + bool ignoreTransparentMembers) { LookupResult result; - const auto options = considerAllLocalNamesInScope ? LookupOptions::ConsiderAllLocalNamesInScope - : LookupOptions::None; + const auto options = + (LookupOptions)((int)(considerAllLocalNamesInScope + ? LookupOptions::ConsiderAllLocalNamesInScope + : LookupOptions::None) | + (int)(ignoreTransparentMembers ? LookupOptions::IgnoreTransparentMembers + : LookupOptions::None)); LookupRequest request = initLookupRequest(semantics, name, mask, options, scope, declToExclude); _lookUpInScopes(astBuilder, name, request, result); return result; diff --git a/source/slang/slang-lookup.h b/source/slang/slang-lookup.h index ad8b64e7e7..21c7b9b982 100644 --- a/source/slang/slang-lookup.h +++ b/source/slang/slang-lookup.h @@ -21,7 +21,8 @@ LookupResult lookUp( Scope* scope, LookupMask mask = LookupMask::Default, bool considerAllLocalNamesInScope = false, - Decl* declToExclude = nullptr); + Decl* declToExclude = nullptr, + bool ignoreTransparentMembers = false); // Perform member lookup in the context of a type LookupResult lookUpMember( diff --git a/tests/language-feature/enums/unscoped-enum-explicit-type-2.slang b/tests/language-feature/enums/unscoped-enum-explicit-type-2.slang new file mode 100644 index 0000000000..1c74dd7b70 --- /dev/null +++ b/tests/language-feature/enums/unscoped-enum-explicit-type-2.slang @@ -0,0 +1,33 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=CHECK):-vk -shaderobj + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +[UnscopedEnum] +enum unscopedEnum1 : uint32_t +{ + ENUM1_A = 1, + ENUM1_B = 2 +}; + +[UnscopedEnum] +enum unscopedEnum2 : uint32_t +{ + ENUM2_A = 3, + ENUM2_B = 4 +}; + +[numthreads(1,1,1)] +void computeMain() +{ + // CHECK: 1 + // CHECK: 2 + // CHECK: 3 + // CHECK: 4 + outputBuffer[0] = ENUM1_A; + outputBuffer[1] = ENUM1_B; + outputBuffer[2] = ENUM2_A; + outputBuffer[3] = ENUM2_B; +} + From 1b7c242bc9ebf649708836f5ed03363f5f0e3969 Mon Sep 17 00:00:00 2001 From: Sai Praveen Bangaru <31557731+saipraveenb25@users.noreply.github.com> Date: Thu, 16 Jan 2025 21:43:46 -0500 Subject: [PATCH 11/23] Fix resource specialization issue where store insts from inlined calls are not considered properly. (#6099) * Fix resource specialization issue where stores from inlined calls are not considered. * Format --- .../slang/slang-ir-specialize-resources.cpp | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/source/slang/slang-ir-specialize-resources.cpp b/source/slang/slang-ir-specialize-resources.cpp index 22cd9cb3fc..2aafaaf31d 100644 --- a/source/slang/slang-ir-specialize-resources.cpp +++ b/source/slang/slang-ir-specialize-resources.cpp @@ -910,6 +910,10 @@ struct ResourceOutputSpecializationPass // SpecializeFuncResult recursiveSpecializationResult = SpecializeFuncResult::Ok; List stores; + + // We'll first specialize any relevant calls that may affect the value stored into the + // param. This may create more stores into the param. + // traverseUses( param, [&](IRUse* use) @@ -917,14 +921,6 @@ struct ResourceOutputSpecializationPass auto user = use->getUser(); switch (user->getOp()) { - case kIROp_Store: - { - auto store = as(user); - if (store->ptr.get() != param) - return; - stores.add(store); - return; - } case kIROp_Call: { // This call may require an inline if it fails to specialize @@ -942,9 +938,33 @@ struct ResourceOutputSpecializationPass return; }; }); + + // If any call specialization fails, we may need to revisit this function at a later + // iteration. if (failedResult(recursiveSpecializationResult)) return recursiveSpecializationResult; + // Then, traverse all stores into this param. + traverseUses( + param, + [&](IRUse* use) + { + auto user = use->getUser(); + switch (user->getOp()) + { + case kIROp_Store: + { + auto store = as(user); + if (store->ptr.get() != param) + return; + stores.add(store); + return; + } + default: + return; + }; + }); + // Having identified the places where a value is stored to // the output parameter, we iterate over those values to // ensure that they are all specializable and consistent From dfb369e87dd7638e84efe8feb9e7069e5238b44c Mon Sep 17 00:00:00 2001 From: Anders Leino Date: Fri, 17 Jan 2025 07:23:31 +0200 Subject: [PATCH 12/23] Move global variable initialization into entry points for WGSL (#6106) * Add test that reproduces the issue in #5986 * Call moveGlobalVarInitializationToEntryPoints for WGSL as well This closes #5986. --------- Co-authored-by: Yong He --- source/slang/slang-emit.cpp | 1 + tests/wgsl/global-call.slang | 20 ++++++++++++++++++++ tests/wgsl/global-call.slang.expected.txt | 4 ++++ 3 files changed, 25 insertions(+) create mode 100644 tests/wgsl/global-call.slang create mode 100644 tests/wgsl/global-call.slang.expected.txt diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 176cd05586..4fee5c4407 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -1471,6 +1471,7 @@ Result linkAndOptimizeIR( default: break; case CodeGenTarget::GLSL: + case CodeGenTarget::WGSL: moveGlobalVarInitializationToEntryPoints(irModule); break; // For SPIR-V to SROA across 2 entry-points a value must not be a global diff --git a/tests/wgsl/global-call.slang b/tests/wgsl/global-call.slang new file mode 100644 index 0000000000..7b67f21398 --- /dev/null +++ b/tests/wgsl/global-call.slang @@ -0,0 +1,20 @@ +//TEST(compute):COMPARE_COMPUTE:-shaderobj + +//TEST_INPUT:ubuffer(data=[3 7 8 10], stride=4):name=inputBuffer +RWStructuredBuffer inputBuffer; + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +uint f(uint a) +{ + return a*inputBuffer[0]; +} + +static uint b = f(2); + +[numthreads(1,1,1)] +void computeMain() +{ + outputBuffer[0] = b; +} \ No newline at end of file diff --git a/tests/wgsl/global-call.slang.expected.txt b/tests/wgsl/global-call.slang.expected.txt new file mode 100644 index 0000000000..e20d75ba3b --- /dev/null +++ b/tests/wgsl/global-call.slang.expected.txt @@ -0,0 +1,4 @@ +6 +0 +0 +0 From 3666c66e26f90b10031578e9c8b8f2ea118aecf9 Mon Sep 17 00:00:00 2001 From: Yong He Date: Fri, 17 Jan 2025 08:55:57 -0800 Subject: [PATCH 13/23] Fix prebound parameter pack - argument list matching logic. (#6111) * Fix prebound parameter pack - argument list matching logic. * Move tests. * Fix. --- source/slang/slang-ast-decl-ref.cpp | 2 +- source/slang/slang-check-overload.cpp | 18 +++++++++++- .../generics/prebound-variadic-pack.slang | 28 +++++++++++++++++++ .../generics/variadic-tuple-field.slang | 22 +++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/language-feature/generics/prebound-variadic-pack.slang create mode 100644 tests/language-feature/generics/variadic-tuple-field.slang diff --git a/source/slang/slang-ast-decl-ref.cpp b/source/slang/slang-ast-decl-ref.cpp index 3025381ea2..9f140a524c 100644 --- a/source/slang/slang-ast-decl-ref.cpp +++ b/source/slang/slang-ast-decl-ref.cpp @@ -318,7 +318,7 @@ void DeclRefBase::toText(StringBuilder& out) return; } - if (as(this->getDecl())) + if (as(this->getDecl())) { SLANG_ASSERT(as(this)); out << this->getDecl()->getName()->text; diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp index fcc6780b63..1ab3aa5b0d 100644 --- a/source/slang/slang-check-overload.cpp +++ b/source/slang/slang-check-overload.cpp @@ -10,6 +10,17 @@ namespace Slang { + +bool isFreeFormTypePackParam(SemanticsVisitor* visitor, Type* type, ParamDecl* paramDecl) +{ + if (auto declRef = isDeclRefTypeOf(type)) + { + return visitor->GetOuterGeneric(declRef.getDecl()) == + visitor->GetOuterGeneric(paramDecl->parentDecl); + } + return false; +} + SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters( FilteredMemberRefList params) { @@ -25,10 +36,15 @@ SemanticsVisitor::ParamCounts SemanticsVisitor::CountParameters( counts.required += typePack->getTypeCount(); allowedArgCountToAdd = typePack->getTypeCount(); } - else + else if (isFreeFormTypePackParam(this, paramType, param.getDecl())) { counts.allowed = -1; } + else + { + counts.required++; + counts.allowed++; + } } else if (!param.getDecl()->initExpr) { diff --git a/tests/language-feature/generics/prebound-variadic-pack.slang b/tests/language-feature/generics/prebound-variadic-pack.slang new file mode 100644 index 0000000000..01edce8f9d --- /dev/null +++ b/tests/language-feature/generics/prebound-variadic-pack.slang @@ -0,0 +1,28 @@ +//TEST:SIMPLE(filecheck=CHECK): -target spirv + +struct Set +{ + Tuple data; + void f(expand each T v){} + void h(U x){} + void g(expand each T d) + { + //CHECK-NOT: ([[# @LINE+1]]): error + f(expand each d); // OK + + //CHECK-NOT: ([[# @LINE+1]]): error + h(54); // OK, specializing free-form parameter U. + + //CHECK: ([[# @LINE+1]]): error + f(); // error, cannot call f without arguments. + + //CHECK: ([[# @LINE+1]]): error + f(5); // error, cannot call f with different type pack. + } +} + +[numthreads(1,1,1)] +void computeMain() +{ + Set v; +} \ No newline at end of file diff --git a/tests/language-feature/generics/variadic-tuple-field.slang b/tests/language-feature/generics/variadic-tuple-field.slang new file mode 100644 index 0000000000..b5ebc4fb3a --- /dev/null +++ b/tests/language-feature/generics/variadic-tuple-field.slang @@ -0,0 +1,22 @@ +//TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK): -output-using-type + +struct Set +{ + Tuple data; + void f(expand each T v){} + __init(expand each T d) { + f(d); + data = makeTuple(d); + } +} + +//TEST_INPUT: set outputBuffer = out ubuffer(data=[0 0 0 0], stride=4) +RWStructuredBuffer outputBuffer; + +[numthreads(1,1,1)] +void computeMain() +{ + let set = Set(1.0); + outputBuffer[0] = set.data._0; + // CHECK: 1.0 +} From e743c17f72ab332f7eb467f02dd9567f3f8d3de0 Mon Sep 17 00:00:00 2001 From: Yong He Date: Fri, 17 Jan 2025 08:56:23 -0800 Subject: [PATCH 14/23] Fix `-capability` arg in SPIRV debug command line output. (#6114) --- source/slang/slang-compiler-options.cpp | 16 +++++++++++++--- tests/spirv/cmd-arg-debug-info.slang | 5 +++++ 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 tests/spirv/cmd-arg-debug-info.slang diff --git a/source/slang/slang-compiler-options.cpp b/source/slang/slang-compiler-options.cpp index 9b70917560..8a27f6fce5 100644 --- a/source/slang/slang-compiler-options.cpp +++ b/source/slang/slang-compiler-options.cpp @@ -54,11 +54,21 @@ void CompilerOptionSet::writeCommandLineArgs(Session* globalSession, StringBuild switch (option.key) { case CompilerOptionName::Capability: - for (auto v : option.value) { - sb << " " << optionInfo.names << " " << v.stringValue; + StringBuilder subBuilder; + for (auto v : option.value) + { + if (subBuilder.getLength() != 0) + subBuilder << "+"; + if (v.kind == CompilerOptionValueKind::Int) + subBuilder << capabilityNameToString((CapabilityName)v.intValue); + else + subBuilder << v.stringValue; + } + if (subBuilder.getLength()) + sb << " " << optionInfo.names << " " << subBuilder.produceString(); + break; } - break; case CompilerOptionName::Include: for (auto v : option.value) { diff --git a/tests/spirv/cmd-arg-debug-info.slang b/tests/spirv/cmd-arg-debug-info.slang new file mode 100644 index 0000000000..da5cebad0a --- /dev/null +++ b/tests/spirv/cmd-arg-debug-info.slang @@ -0,0 +1,5 @@ +//TEST:SIMPLE(filecheck=CHECK): -target spirv -g3 -emit-spirv-directly -profile sm_6_6+spirv_1_5 +// CHECK: OpString "-target spirv {{.*}} -capability spirv_1_5 +[numthreads(1,1,1)] +void computeMain() +{} \ No newline at end of file From 91430870a8a6ec2825969823b892a9c8a0d588ab Mon Sep 17 00:00:00 2001 From: Darren Wihandi <65404740+fairywreath@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:57:06 -0500 Subject: [PATCH 15/23] hoist entry point params for wgsl (#6116) Co-authored-by: Yong He --- .../slang-ir-legalize-varying-params.cpp | 20 ++++++------------- .../metal/nested-struct-fragment-input.slang | 4 ++-- tests/metal/stage-in-2.slang | 6 ++---- tests/wgsl/nested-varying-input.slang | 20 ++++++++++--------- 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/source/slang/slang-ir-legalize-varying-params.cpp b/source/slang/slang-ir-legalize-varying-params.cpp index 9720084ba3..3b65ee59af 100644 --- a/source/slang/slang-ir-legalize-varying-params.cpp +++ b/source/slang/slang-ir-legalize-varying-params.cpp @@ -1574,8 +1574,8 @@ class LegalizeShaderEntryPointContext } protected: - LegalizeShaderEntryPointContext(IRModule* module, DiagnosticSink* sink, bool hoistParameters) - : m_module(module), m_sink(sink), hoistParameters(hoistParameters) + LegalizeShaderEntryPointContext(IRModule* module, DiagnosticSink* sink) + : m_module(module), m_sink(sink) { } @@ -1758,7 +1758,6 @@ class LegalizeShaderEntryPointContext } private: - const bool hoistParameters; HashSet semanticInfoToRemove; void removeSemanticLayoutsFromLegalizedStructs() @@ -2985,16 +2984,9 @@ class LegalizeShaderEntryPointContext // If the entrypoint is receiving varying inputs as a pointer, turn it into a value. depointerizeInputParams(entryPoint.entryPointFunc); - // TODO FIXME: Enable these for WGSL and remove the `hoistParemeters` member field. - // WGSL entry point legalization currently only applies attributes to struct parameters, - // apply the same hoisting from Metal to WGSL to fix it. - if (hoistParameters) - { - hoistEntryPointParameterFromStruct(entryPoint); - packStageInParameters(entryPoint); - } - // Input Parameter Legalize + hoistEntryPointParameterFromStruct(entryPoint); + packStageInParameters(entryPoint); flattenInputParameters(entryPoint); // System Value Legalize @@ -3023,7 +3015,7 @@ class LegalizeMetalEntryPointContext : public LegalizeShaderEntryPointContext { public: LegalizeMetalEntryPointContext(IRModule* module, DiagnosticSink* sink) - : LegalizeShaderEntryPointContext(module, sink, true) + : LegalizeShaderEntryPointContext(module, sink) { generatePermittedTypes_sv_target(); } @@ -3681,7 +3673,7 @@ class LegalizeWGSLEntryPointContext : public LegalizeShaderEntryPointContext { public: LegalizeWGSLEntryPointContext(IRModule* module, DiagnosticSink* sink) - : LegalizeShaderEntryPointContext(module, sink, false) + : LegalizeShaderEntryPointContext(module, sink) { } diff --git a/tests/metal/nested-struct-fragment-input.slang b/tests/metal/nested-struct-fragment-input.slang index f70303306f..77530f9fd5 100644 --- a/tests/metal/nested-struct-fragment-input.slang +++ b/tests/metal/nested-struct-fragment-input.slang @@ -22,7 +22,7 @@ // METAL-NOT: [[ATTR3]] // METAL-DAG: [[ATTR4:COARSEVERTEX_(1|2|3|4)]] -// WGSL:struct FragmentStageInput +// WGSL:struct pixelInput // WGSL-DAG:@location(0) [[VAR0:[A-Za-z_0-9]+]] // WGSL-DAG:@location(1) [[VAR1:[A-Za-z_0-9]+]] // WGSL-DAG:@location(2) [[VAR2:[A-Za-z_0-9]+]] @@ -78,7 +78,7 @@ float4 fragmentMain(FragmentStageInput input) // METAL-DAG: {{.*}}->p3{{.*}}->p2{{.*}}->p1{{.*}}= // METAL-DAG: {{.*}}->p3{{.*}}->p3{{.*}}->p1{{.*}}= - // WGSL: var [[UnpackedInput:[A-Za-z_0-9]+]] : FragmentStageInput + // WGSL: var [[UnpackedInput:[A-Za-z_0-9]+]] : pixelInput // WGSL-DAG: [[UnpackedInput]].{{[A-Za-z_0-9]+}}.{{[A-Za-z_0-9]+}} = [[InputVar]].[[VAR7]]; // WGSL-DAG: [[UnpackedInput]].{{[A-Za-z_0-9]+}}.{{[A-Za-z_0-9]+}}.{{[A-Za-z_0-9]+}} = [[InputVar]].[[VAR6]]; diff --git a/tests/metal/stage-in-2.slang b/tests/metal/stage-in-2.slang index 5b885af492..6ee3b8c512 100644 --- a/tests/metal/stage-in-2.slang +++ b/tests/metal/stage-in-2.slang @@ -15,10 +15,8 @@ //WGSLSPIRV: %vertexMain = OpFunction % //WGSLSPIRV: %fragmentMain = OpFunction % -//WGSL: struct [[CoarseVertex:CoarseVertex[_0-9]*]] -//WGSL-NEXT: { -//WGSL-NEXT: @location(0) color -//WGSL: fn fragmentMain({{.*}}[[CoarseVertex]] +//WGSL: @location(0) output +//WGSL: @location(0) color // Uniform data to be passed from application -> shader. cbuffer Uniforms diff --git a/tests/wgsl/nested-varying-input.slang b/tests/wgsl/nested-varying-input.slang index 2cdf4f7ebd..d2566a6b90 100644 --- a/tests/wgsl/nested-varying-input.slang +++ b/tests/wgsl/nested-varying-input.slang @@ -22,17 +22,17 @@ struct NestedVertexOutput struct VertexOutput { - //VERT: @builtin(position) position - //FRAG: @builtin(position) position - float4 position : SV_Position; - //VERT: @location(0) uv - //FRAG: @location(0) uv + //FRAG-DAG: @location(0) uv float2 uv : TEXCOORD0; //VERT: @location(1) color - //FRAG: @location(1) color + //FRAG-DAG: @location(1) color NestedVertexOutput nested; + + //VERT: @builtin(position) position + //FRAG-DAG: @builtin(position) position + float4 position : SV_Position; }; VertexOutput vertexMain() @@ -44,10 +44,12 @@ VertexOutput vertexMain() return out; } -FragmentOutput fragmentMain(VertexOutput input) +//FRAG-DAG: @location(3) color3 +//FRAG-DAG: @location(6) color6 +FragmentOutput fragmentMain(VertexOutput input, float4 color3: COLOR3, float4 color6: COLOR6) { FragmentOutput out; - out.color0 = input.nested.color; - out.color1 = input.nested.color; + out.color0 = input.nested.color + color3; + out.color1 = input.nested.color + color6; return out; } From ddc4a1799a9d21084604cc2bf334d5d91cb329e3 Mon Sep 17 00:00:00 2001 From: Julius Ikkala Date: Fri, 17 Jan 2025 18:58:18 +0200 Subject: [PATCH 16/23] Fix nullptr in generic specialization (#6066) * Fix nullptr in generic specialization * Fix formatting * Revert "Fix nullptr in generic specialization" and add emitPtrLit instead * Add type parameter to getPtrValue() --------- Co-authored-by: Yong He --- source/slang/slang-ir-clone.cpp | 20 ++++++++++++++------ source/slang/slang-ir-insts.h | 2 +- source/slang/slang-ir-link.cpp | 9 +++++++++ source/slang/slang-ir.cpp | 5 ++--- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/source/slang/slang-ir-clone.cpp b/source/slang/slang-ir-clone.cpp index 6bbaefa262..66af405d6f 100644 --- a/source/slang/slang-ir-clone.cpp +++ b/source/slang/slang-ir-clone.cpp @@ -51,6 +51,20 @@ IRInst* cloneInstAndOperands(IRCloneEnv* env, IRBuilder* builder, IRInst* oldIns SLANG_ASSERT(builder); SLANG_ASSERT(oldInst); + // We start by mapping the type of the orignal instruction + // to its replacement value, if any. + // + auto oldType = oldInst->getFullType(); + auto newType = (IRType*)findCloneForOperand(env, oldType); + + // Pointer literals need to be handled separately, as they carry other data + // than just the operands. + if (oldInst->getOp() == kIROp_PtrLit) + { + auto oldPtr = as(oldInst); + return builder->getPtrValue(newType, oldPtr->value.ptrVal); + } + // This logic will not handle any instructions // with special-case data attached, but that only // applies to `IRConstant`s at this point, and those @@ -62,12 +76,6 @@ IRInst* cloneInstAndOperands(IRCloneEnv* env, IRBuilder* builder, IRInst* oldIns // SLANG_ASSERT(!as(oldInst)); - // We start by mapping the type of the orignal instruction - // to its replacement value, if any. - // - auto oldType = oldInst->getFullType(); - auto newType = (IRType*)findCloneForOperand(env, oldType); - // Next we will iterate over the operands of `oldInst` // to find their replacements and install them as // the operands of `newInst`. diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index f46586aa2b..2fa4de6127 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -3599,7 +3599,7 @@ struct IRBuilder IRInst* getFloatValue(IRType* type, IRFloatingPointValue value); IRStringLit* getStringValue(const UnownedStringSlice& slice); IRBlobLit* getBlobValue(ISlangBlob* blob); - IRPtrLit* _getPtrValue(void* ptr); + IRPtrLit* getPtrValue(IRType* type, void* ptr); IRPtrLit* getNullPtrValue(IRType* type); IRPtrLit* getNullVoidPtrValue() { return getNullPtrValue(getPtrType(getVoidType())); } IRVoidLit* getVoidValue(); diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index d60903cfc2..364e58c48e 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -1172,6 +1172,15 @@ IRInst* cloneInst( { // We need to special-case any instruction that is not // allocated like an ordinary `IRInst` with trailing args. + case kIROp_IntLit: + case kIROp_FloatLit: + case kIROp_BoolLit: + case kIROp_StringLit: + case kIROp_BlobLit: + case kIROp_PtrLit: + case kIROp_VoidLit: + return cloneValue(context, originalInst); + case kIROp_Func: return cloneFuncImpl(context, builder, cast(originalInst), originalValues); diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index e15ec6f07b..ac51ae451d 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -2395,9 +2395,8 @@ IRBlobLit* IRBuilder::getBlobValue(ISlangBlob* blob) return static_cast(_findOrEmitConstant(keyInst)); } -IRPtrLit* IRBuilder::_getPtrValue(void* data) +IRPtrLit* IRBuilder::getPtrValue(IRType* type, void* data) { - auto type = getPtrType(getVoidType()); IRConstant keyInst; memset(&keyInst, 0, sizeof(keyInst)); keyInst.m_op = kIROp_PtrLit; @@ -6324,7 +6323,7 @@ IRDecoration* IRBuilder::addDecoration( void IRBuilder::addHighLevelDeclDecoration(IRInst* inst, Decl* decl) { - auto ptrConst = _getPtrValue(decl); + auto ptrConst = getPtrValue(getPtrType(getVoidType()), decl); addDecoration(inst, kIROp_HighLevelDeclDecoration, ptrConst); } From f68d493d55eddd03d5eb5faad05c4628a2af2d91 Mon Sep 17 00:00:00 2001 From: Jay Kwak <82421531+jkwak-work@users.noreply.github.com> Date: Fri, 17 Jan 2025 08:58:57 -0800 Subject: [PATCH 17/23] Avoid using the backend validation when using test server (#6094) * Avoid using the backend validation when using test server Currently with a debug build, the backend validation such as Vulkan-Validation-Layer or DXC validation is enabled all the time. It means there is a higher chance that we see warning messages while running slang-test with a debug build. However, those warning messages incorrectly treated as the testing result when using test-server. This is mainly because of the fact that the Slang implemention for the RPC commucation expects only one time output result. As soon as any warning is printed, the testing process is incorrectly considered as completed even though it might be still in the middle of initializing the device. This commit disables the backend validation when using the test-server. * format code (#31) Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> --------- Co-authored-by: slangbot Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: Yong He --- tools/render-test/options.cpp | 4 ++++ tools/render-test/options.h | 2 ++ tools/render-test/render-test-main.cpp | 4 +++- tools/slang-test/slang-test-main.cpp | 9 +++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index beacad1a8b..ce438046ac 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -249,6 +249,10 @@ static rhi::DeviceType _toRenderType(Slang::RenderApiType apiType) { SLANG_RETURN_ON_FAIL(reader.expectArg(outOptions.entryPointName)); } + else if (argValue == "-enable-backend-validation") + { + outOptions.enableBackendValidation = true; + } else { // Lookup diff --git a/tools/render-test/options.h b/tools/render-test/options.h index 2497ce7827..9a30030f72 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -87,6 +87,8 @@ struct Options bool generateSPIRVDirectly = true; + bool enableBackendValidation = false; + Options() { downstreamArgs.addName("slang"); } static SlangResult parse( diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index b1f9575512..812a753e3d 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -1367,10 +1367,12 @@ static SlangResult _innerMain( #if _DEBUG desc.enableValidation = true; - desc.enableBackendValidation = true; desc.debugCallback = &debugCallback; #endif + if (options.enableBackendValidation) + desc.enableBackendValidation = true; + desc.slang.lineDirectiveMode = SLANG_LINE_DIRECTIVE_MODE_NONE; if (options.generateSPIRVDirectly) desc.slang.targetFlags = SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY; diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index a8127c3a82..37b1f43c9d 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -3478,6 +3478,15 @@ TestResult runComputeComparisonImpl( auto actualOutputFile = outputStem + ".actual.txt"; cmdLine.addArg(actualOutputFile); +#if _DEBUG + // When using test server, any validation warning printed from the backend + // gets misinterpreted as the result from the test. + // This is due to the limitation that Slang RPC implementation expects only + // one time communication. + if (input.spawnType != SpawnType::UseTestServer) + cmdLine.addArg("-enable-backend-validation"); +#endif + if (context->isExecuting()) { // clear the stale actual output file first. This will allow us to detect error if From 3ff257816fc8f376d9bee76378a690757f8b5377 Mon Sep 17 00:00:00 2001 From: Sai Praveen Bangaru <31557731+saipraveenb25@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:08:03 -0500 Subject: [PATCH 18/23] Fix interface requirement lowering for generic accessors (#6123) --- source/slang/slang-lower-to-ir.cpp | 10 ++++++++ tests/autodiff/generic-accessors.slang | 32 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tests/autodiff/generic-accessors.slang diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 0863457198..54540a3f85 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -8633,6 +8633,9 @@ struct DeclLoweringVisitor : DeclVisitor UInt operandCount = 0; for (auto requirementDecl : decl->members) { + if (as(requirementDecl)) + requirementDecl = getInner(requirementDecl); + if (as(requirementDecl) || as(requirementDecl)) { for (auto accessorDecl : as(requirementDecl)->members) @@ -8782,6 +8785,13 @@ struct DeclLoweringVisitor : DeclVisitor auto requirementKey = getInterfaceRequirementKey(requirementDecl); if (!requirementKey) { + if (auto genericDecl = as(requirementDecl)) + { + // We need to form a declref into the inner decls in case of a generic + // requirement. + requirementDecl = getInner(genericDecl); + } + if (as(requirementDecl) || as(requirementDecl)) { for (auto member : as(requirementDecl)->members) diff --git a/tests/autodiff/generic-accessors.slang b/tests/autodiff/generic-accessors.slang new file mode 100644 index 0000000000..2b179f2569 --- /dev/null +++ b/tests/autodiff/generic-accessors.slang @@ -0,0 +1,32 @@ +//TEST:COMPARE_COMPUTE(filecheck-buffer=CHK): -output-using-type + +interface ITest +{ + __generic + __subscript(I i) -> float + { + [BackwardDifferentiable] get; + } +} +struct Test : ITest +{ + __generic + __subscript(I i) -> float + { + [BackwardDifferentiable] get { return 5.0f * i.toInt(); } + } +} +[Differentiable] +float test(ITest arg) +{ + return arg[1]; +} +//TEST_INPUT:set output = out ubuffer(data=[0 0 0 0], stride=4) +RWStructuredBuffer output; +[numthreads(1,1,1)] +void computeMain() +{ + Test t = {}; + output[0] = test(t); + // CHK: 5.0 +} \ No newline at end of file From fc77070fdc9bfa599e8d66b21743778de3011e53 Mon Sep 17 00:00:00 2001 From: Yong He Date: Fri, 17 Jan 2025 14:37:27 -0800 Subject: [PATCH 19/23] Refactor _Texture to constrain on texel types. (#6115) * Refactor _Texture to constrain on texel types. * Fix tests. * Fix. * Disable glsl texture test because rhi can't run it correctly. --- source/slang/glsl.meta.slang | 513 +++++++++--------- source/slang/hlsl.meta.slang | 298 ++++++---- source/slang/slang-check-inheritance.cpp | 4 + source/slang/slang-emit-cpp.cpp | 5 +- source/slang/slang-ir-peephole.cpp | 2 + tests/diagnostics/illegal-texel-type.slang | 7 + ...insic-derivative-function-in-compute.slang | 58 +- tests/glsl-intrinsic/intrinsic-texture.slang | 312 +++++------ .../intrinsic-texture-ignore-capability.slang | 384 ++++++------- tests/metal/texture-sampler-less.slang | 8 +- tests/metal/texture.slang | 139 +++-- tests/wgsl/texture-gather.slang | 86 +-- tests/wgsl/texture-load.slang | 38 +- tests/wgsl/texture-sampler-less.slang | 115 ++-- tests/wgsl/texture-storage.slang | 41 +- tests/wgsl/texture.slang | 104 ++-- 16 files changed, 1109 insertions(+), 1005 deletions(-) create mode 100644 tests/diagnostics/illegal-texel-type.slang diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang index ba26b5d84a..1b44c7825e 100644 --- a/source/slang/glsl.meta.slang +++ b/source/slang/glsl.meta.slang @@ -1354,6 +1354,9 @@ public vector not(vector x) return !x; } +__intrinsic_op(vectorReshape) +vector __vectorReshape2(U vin); + // // Section 8.9.1. Texture Query Functions // @@ -1472,7 +1475,7 @@ public typealias sampler2DMSArray = Sampler2DMSArray; public typealias isampler2DMSArray = Sampler2DMSArray; public typealias usampler2DMSArray = Sampler2DMSArray; -__generic +__generic public typealias Sampler2DRect = _Texture; public typealias sampler2DRect = Sampler2DRect; public typealias isampler2DRect = Sampler2DRect; @@ -1491,7 +1494,7 @@ public typealias sampler2DRectShadow = _Texture< format >; -__generic +__generic public typealias SamplerBuffer = _Texture< T, __ShapeBuffer, @@ -1512,10 +1515,10 @@ public typealias usamplerBuffer = SamplerBuffer; // textureSize // ------------------- -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_size)] -public int textureSize(Sampler1D> sampler, int lod) +public int textureSize(Sampler1D sampler, int lod) { int result; int numberOfLevels; @@ -1523,10 +1526,10 @@ public int textureSize(Sampler1D> sampler, int lod) return result; } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_size)] -public ivec2 textureSize(Sampler2D> sampler, int lod) +public ivec2 textureSize(Sampler2D sampler, int lod) { vector result; int numberOfLevels; @@ -1534,10 +1537,10 @@ public ivec2 textureSize(Sampler2D> sampler, int lod) return result; } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_size)] -public ivec3 textureSize(Sampler3D> sampler, int lod) +public ivec3 textureSize(Sampler3D sampler, int lod) { vector result; int numberOfLevels; @@ -1545,10 +1548,10 @@ public ivec3 textureSize(Sampler3D> sampler, int lod) return result; } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_size)] -public ivec2 textureSize(SamplerCube> sampler, int lod) +public ivec2 textureSize(SamplerCube sampler, int lod) { vector result; int numberOfLevels; @@ -1587,9 +1590,9 @@ public ivec2 textureSize(samplerCubeShadow sampler, int lod) } [require(glsl_hlsl_spirv, texture_size)] -__generic +__generic [ForceInline] -public ivec3 textureSize(SamplerCubeArray> sampler, int lod) +public ivec3 textureSize(SamplerCubeArray sampler, int lod) { vector result; int numberOfLevels; @@ -1608,9 +1611,9 @@ public ivec3 textureSize(samplerCubeArrayShadow sampler, int lod) } [require(glsl_hlsl_spirv, texture_size)] -__generic +__generic [ForceInline] -public ivec2 textureSize(Sampler2DRect> sampler) +public ivec2 textureSize(Sampler2DRect sampler) { vector result; int numberOfLevels; @@ -1629,9 +1632,9 @@ public ivec2 textureSize(sampler2DRectShadow sampler) } [require(glsl_hlsl_spirv, texture_size)] -__generic +__generic [ForceInline] -public ivec2 textureSize(Sampler1DArray> sampler, int lod) +public ivec2 textureSize(Sampler1DArray sampler, int lod) { vector result; int numberOfLevels; @@ -1650,9 +1653,9 @@ public ivec2 textureSize(sampler1DArrayShadow sampler, int lod) } [require(glsl_hlsl_spirv, texture_size)] -__generic +__generic [ForceInline] -public ivec3 textureSize(Sampler2DArray> sampler, int lod) +public ivec3 textureSize(Sampler2DArray sampler, int lod) { vector result; int numberOfLevels; @@ -1671,9 +1674,9 @@ public ivec3 textureSize(sampler2DArrayShadow sampler, int lod) } [require(glsl_hlsl_spirv, texture_size)] -__generic +__generic [ForceInline] -public int textureSize(SamplerBuffer,format> sampler) +public int textureSize(SamplerBuffer sampler) { uint result; sampler.GetDimensions(result); @@ -1681,9 +1684,9 @@ public int textureSize(SamplerBuffer,format> sampler) } [require(glsl_hlsl_spirv, texture_size)] -__generic +__generic [ForceInline] -public ivec2 textureSize(Sampler2DMS,sampleCount> sampler) +public ivec2 textureSize(Sampler2DMS sampler) { vector result; int sampleCount; @@ -1693,9 +1696,9 @@ public ivec2 textureSize(Sampler2DMS,sampleCount> sampler) } [require(glsl_hlsl_spirv, texture_size)] -__generic +__generic [ForceInline] -public ivec3 textureSize(Sampler2DMSArray,sampleCount> sampler) +public ivec3 textureSize(Sampler2DMSArray sampler) { vector result; int sampleCount; @@ -1708,7 +1711,7 @@ public ivec3 textureSize(Sampler2DMSArray,sampleCount> sampler) // textureQueryLod // ------------------- -__generic +__generic [ForceInline] [require(glsl_hlsl_metal_spirv, texture_querylod)] public vec2 textureQueryLod(_Texture< @@ -1741,7 +1744,7 @@ public vec2 textureQueryLod(_Texture< } } -__generic +__generic [ForceInline] [require(glsl_hlsl_metal_spirv, texture_querylod)] public vec2 textureQueryLod(_Texture< @@ -1778,10 +1781,10 @@ public vec2 textureQueryLod(_Texture< // textureQueryLevels // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_metal_spirv, texture_querylevels)] -public int textureQueryLevels(Sampler1D> sampler) +public int textureQueryLevels(Sampler1D sampler) { int width; int numberOfLevels; @@ -1789,10 +1792,10 @@ public int textureQueryLevels(Sampler1D> sampler) return numberOfLevels; } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_metal_spirv, texture_querylevels)] -public int textureQueryLevels(Sampler2D> sampler) +public int textureQueryLevels(Sampler2D sampler) { vector dim; int numberOfLevels; @@ -1800,10 +1803,10 @@ public int textureQueryLevels(Sampler2D> sampler) return numberOfLevels; } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_metal_spirv, texture_querylevels)] -public int textureQueryLevels(Sampler3D> sampler) +public int textureQueryLevels(Sampler3D sampler) { vector dim; int numberOfLevels; @@ -1811,10 +1814,10 @@ public int textureQueryLevels(Sampler3D> sampler) return numberOfLevels; } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_metal_spirv, texture_querylevels)] -public int textureQueryLevels(SamplerCube> sampler) +public int textureQueryLevels(SamplerCube sampler) { vector dim; int numberOfLevels; @@ -1822,10 +1825,10 @@ public int textureQueryLevels(SamplerCube> sampler) return numberOfLevels; } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_metal_spirv, texture_querylevels)] -public int textureQueryLevels(Sampler1DArray> sampler) +public int textureQueryLevels(Sampler1DArray sampler) { vector dim; int numberOfLevels; @@ -1833,10 +1836,10 @@ public int textureQueryLevels(Sampler1DArray> sampler) return numberOfLevels; } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_metal_spirv, texture_querylevels)] -public int textureQueryLevels(Sampler2DArray> sampler) +public int textureQueryLevels(Sampler2DArray sampler) { vector dim; int numberOfLevels; @@ -1844,10 +1847,10 @@ public int textureQueryLevels(Sampler2DArray> sampler) return numberOfLevels; } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_metal_spirv, texture_querylevels)] -public int textureQueryLevels(SamplerCubeArray> sampler) +public int textureQueryLevels(SamplerCubeArray sampler) { vector dim; int numberOfLevels; @@ -1919,7 +1922,7 @@ public int textureQueryLevels(samplerCubeArrayShadow sampler) // textureSamples // ------------------- -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, image_samples)] public int textureSamples(Sampler2DMS sampler) @@ -1931,7 +1934,7 @@ public int textureSamples(Sampler2DMS sampler) return sampleCount; } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, image_samples)] public int textureSamples(Sampler2DMSArray sampler) @@ -1951,27 +1954,27 @@ public int textureSamples(Sampler2DMSArray sampler) // texture // ------------------- -__generic +__generic [ForceInline] [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector texture(Sampler1D> sampler, float p) +public vector texture(Sampler1D sampler, float p) { - return __vectorReshape<4>(sampler.Sample(p)); + return __vectorReshape2(sampler.Sample(p)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector texture(Sampler1D> sampler, float p, constexpr float bias) +public vector texture(Sampler1D sampler, float p, constexpr float bias) { - return __vectorReshape<4>(sampler.SampleBias(p, bias)); + return __vectorReshape2(sampler.SampleBias(p, bias)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector texture(_Texture< - vector, +public vector texture(_Texture< + T, Shape, isArray, 0, // isMS @@ -1982,14 +1985,14 @@ public vector texture(_Texture< format > sampler, vector p) { - return __vectorReshape<4>(sampler.Sample(p)); + return __vectorReshape2(sampler.Sample(p)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector texture(_Texture< - vector, +public vector texture(_Texture< + T, Shape, isArray, 0, // isMS @@ -2000,7 +2003,7 @@ public vector texture(_Texture< format > sampler, vector p, constexpr float bias) { - return __vectorReshape<4>(sampler.SampleBias(p, bias)); + return __vectorReshape2(sampler.SampleBias(p, bias)); } [ForceInline] @@ -2137,170 +2140,170 @@ public float texture(samplerCubeArrayShadow sampler, vec4 p, float compare) // textureProj // ------------------- -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler1D> sampler, vec2 p) +public vector textureProj(Sampler1D sampler, vec2 p) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p + result:$$vector = OpImageSampleProjImplicitLod $sampler $p }; default: return texture(sampler, p.x / p.y); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler1D> sampler, vec2 p, float bias) +public vector textureProj(Sampler1D sampler, vec2 p, float bias) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias + result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias }; default: return texture(sampler, p.x / p.y, bias); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler1D> sampler, vec4 p) +public vector textureProj(Sampler1D sampler, vec4 p) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p + result:$$vector = OpImageSampleProjImplicitLod $sampler $p }; default: return texture(sampler, p.x / p.w); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler1D> sampler, vec4 p, float bias) +public vector textureProj(Sampler1D sampler, vec4 p, float bias) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias + result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias }; default: return texture(sampler, p.x / p.w, bias); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler2D> sampler, vec3 p) +public vector textureProj(Sampler2D sampler, vec3 p) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p + result:$$vector = OpImageSampleProjImplicitLod $sampler $p }; default: return texture(sampler, p.xy / p.z); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler2D> sampler, vec3 p, float bias) +public vector textureProj(Sampler2D sampler, vec3 p, float bias) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias + result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias }; default: return texture(sampler, p.xy / p.z, bias); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler2D> sampler, vec4 p) +public vector textureProj(Sampler2D sampler, vec4 p) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p + result:$$vector = OpImageSampleProjImplicitLod $sampler $p }; default: return texture(sampler, p.xy / p.w); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler2D> sampler, vec4 p, float bias) +public vector textureProj(Sampler2D sampler, vec4 p, float bias) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias + result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias }; default: return texture(sampler, p.xy / p.w, bias); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler3D> sampler, vec4 p) +public vector textureProj(Sampler3D sampler, vec4 p) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p + result:$$vector = OpImageSampleProjImplicitLod $sampler $p }; default: return texture(sampler, p.xyz / p.w); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProj(Sampler3D> sampler, vec4 p, float bias) +public vector textureProj(Sampler3D sampler, vec4 p, float bias) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProj"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias + result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias $bias }; default: return texture(sampler, p.xyz / p.w, bias); @@ -2395,19 +2398,19 @@ public float textureProj(sampler2DShadow sampler, vec4 p, float bias) // textureLod // ------------------- -__generic +__generic [ForceInline] [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureLod(Sampler1D> sampler, float p, float lod) +public vector textureLod(Sampler1D sampler, float p, float lod) { - return __vectorReshape<4>(sampler.SampleLevel(p, lod)); + return __vectorReshape2(sampler.SampleLevel(p, lod)); } -__generic +__generic [ForceInline] [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureLod(_Texture< - vector, +public vector textureLod(_Texture< + T, Shape, isArray, 0, // isMS @@ -2418,7 +2421,7 @@ public vector textureLod(_Texture< format > sampler, vector p, float lod) { - return __vectorReshape<4>(sampler.SampleLevel(p, lod)); + return __vectorReshape2(sampler.SampleLevel(p, lod)); } [ForceInline] @@ -2501,28 +2504,28 @@ public float textureLod(sampler1DArrayShadow sampler, vec3 p, float lod) // textureOffset // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureOffset(Sampler1D> sampler, float p, constexpr int offset, float bias = 0.0) +public vector textureOffset(Sampler1D sampler, float p, constexpr int offset, float bias = 0.0) { - return __vectorReshape<4>(sampler.SampleBias(p, bias, offset)); + return __vectorReshape2(sampler.SampleBias(p, bias, offset)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureOffset(Sampler2D> sampler, vec2 p, constexpr ivec2 offset, float bias = 0.0) +public vector textureOffset(Sampler2D sampler, vec2 p, constexpr ivec2 offset, float bias = 0.0) { - return __vectorReshape<4>(sampler.SampleBias(p, bias, offset)); + return __vectorReshape2(sampler.SampleBias(p, bias, offset)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureOffset(Sampler3D> sampler, vec3 p, constexpr ivec3 offset, float bias = 0.0) +public vector textureOffset(Sampler3D sampler, vec3 p, constexpr ivec3 offset, float bias = 0.0) { - return __vectorReshape<4>(sampler.SampleBias(p, bias, offset)); + return __vectorReshape2(sampler.SampleBias(p, bias, offset)); } [ForceInline] @@ -2591,20 +2594,20 @@ public float textureOffset(sampler1DShadow sampler, vec3 p, constexpr int offset } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureOffset(Sampler1DArray> sampler, vec2 p, constexpr int offset, float bias = 0.0) +public vector textureOffset(Sampler1DArray sampler, vec2 p, constexpr int offset, float bias = 0.0) { - return __vectorReshape<4>(sampler.SampleBias(p, bias, offset)); + return __vectorReshape2(sampler.SampleBias(p, bias, offset)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureOffset(Sampler2DArray> sampler, vec3 p, constexpr ivec2 offset, float bias = 0.0) +public vector textureOffset(Sampler2DArray sampler, vec3 p, constexpr ivec2 offset, float bias = 0.0) { - return __vectorReshape<4>(sampler.SampleBias(p, bias, offset)); + return __vectorReshape2(sampler.SampleBias(p, bias, offset)); } [ForceInline] @@ -2662,19 +2665,19 @@ public float textureOffset(sampler2DArrayShadow sampler, vec4 p, constexpr ivec2 // texelFetch // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1_samplerless)] -public vector texelFetch(Sampler1D> sampler, int p, int lod) +public vector texelFetch(Sampler1D sampler, int p, int lod) { - return __vectorReshape<4>(sampler.Load(int2(p, lod))); + return __vectorReshape2(sampler.Load(int2(p, lod))); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1_samplerless)] -public vector texelFetch(_Texture< - vector, +public vector texelFetch(_Texture< + T, Shape, isArray, 0, // isMS @@ -2685,30 +2688,30 @@ public vector texelFetch(_Texture< format > sampler, vector p, int lod) { - return __vectorReshape<4>(sampler.Load(__makeVector(p,lod))); + return __vectorReshape2(sampler.Load(__makeVector(p,lod))); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1_samplerless)] -public vector texelFetch(Sampler2DRect> sampler, ivec2 p) +public vector texelFetch(Sampler2DRect sampler, ivec2 p) { - return __vectorReshape<4>(sampler.Load(int3(p.xy,0))); + return __vectorReshape2(sampler.Load(int3(p.xy,0))); } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_1_samplerless)] -public vector texelFetch(SamplerBuffer,format> sampler, int p) +public vector texelFetch(SamplerBuffer sampler, int p) { - return __vectorReshape<4>(sampler.Load(p)); + return __vectorReshape2(sampler.Load(p)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1_samplerless)] -public vector texelFetch(_Texture< - vector, +public vector texelFetch(_Texture< + T, __Shape2D, isArray, 1, // isMS @@ -2724,7 +2727,7 @@ public vector texelFetch(_Texture< case glsl: __intrinsic_asm "texelFetch"; default: // TODO: Need to apply lod - return __vectorReshape<4>(sampler.Load(__makeVector(p, 0))); + return __vectorReshape2(sampler.Load(__makeVector(p, 0))); } } @@ -2732,19 +2735,19 @@ public vector texelFetch(_Texture< // texelFetchOffset // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1_samplerless)] -public vector texelFetchOffset(Sampler1D> sampler, int p, int lod, constexpr int offset) +public vector texelFetchOffset(Sampler1D sampler, int p, int lod, constexpr int offset) { - return __vectorReshape<4>(sampler.Load(int2(p, lod), offset)); + return __vectorReshape2(sampler.Load(int2(p, lod), offset)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1_samplerless)] -public vector texelFetchOffset(_Texture< - vector, +public vector texelFetchOffset(_Texture< + T, Shape, isArray, 0, // isMS @@ -2755,59 +2758,59 @@ public vector texelFetchOffset(_Texture< format > sampler, vector p, int lod, constexpr vector offset) { - return __vectorReshape<4>(sampler.Load(__makeVector(p,lod), offset)); + return __vectorReshape2(sampler.Load(__makeVector(p,lod), offset)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1_samplerless)] -public vector texelFetchOffset(Sampler2DRect> sampler, ivec2 p, constexpr ivec2 offset) +public vector texelFetchOffset(Sampler2DRect sampler, ivec2 p, constexpr ivec2 offset) { - return __vectorReshape<4>(sampler.Load(__makeVector(p, 0), offset)); + return __vectorReshape2(sampler.Load(__makeVector(p, 0), offset)); } // ------------------- // textureProjOffset // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler1D> sampler, vec2 p, constexpr int offset) +public vector textureProjOffset(Sampler1D sampler, vec2 p, constexpr int offset) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProjOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p ConstOffset $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $p ConstOffset $offset }; default: return textureOffset(sampler, p.x / p.y, offset); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler1D> sampler, vec2 p, constexpr int offset, float bias) +public vector textureProjOffset(Sampler1D sampler, vec2 p, constexpr int offset, float bias) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProjOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias|ConstOffset $bias $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias|ConstOffset $bias $offset }; default: return textureOffset(sampler, p.x / p.y, offset, bias); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler1D> sampler, vec4 p, constexpr int offset) +public vector textureProjOffset(Sampler1D sampler, vec4 p, constexpr int offset) { __requireComputeDerivative(); __target_switch @@ -2817,7 +2820,7 @@ public vector textureProjOffset(Sampler1D> sampler, vec4 p, con { vec4 xw__ = p.xwww; return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $xw__ ConstOffset $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $xw__ ConstOffset $offset }; } default: @@ -2825,10 +2828,10 @@ public vector textureProjOffset(Sampler1D> sampler, vec4 p, con } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler1D> sampler, vec4 p, constexpr int offset, float bias) +public vector textureProjOffset(Sampler1D sampler, vec4 p, constexpr int offset, float bias) { __requireComputeDerivative(); __target_switch @@ -2838,7 +2841,7 @@ public vector textureProjOffset(Sampler1D> sampler, vec4 p, con { vec4 xw__ = p.xwww; return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $xw__ Bias|ConstOffset $bias $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $xw__ Bias|ConstOffset $bias $offset }; } default: @@ -2846,44 +2849,44 @@ public vector textureProjOffset(Sampler1D> sampler, vec4 p, con } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler2D> sampler, vec3 p, constexpr ivec2 offset) +public vector textureProjOffset(Sampler2D sampler, vec3 p, constexpr ivec2 offset) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProjOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p ConstOffset $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $p ConstOffset $offset }; default: return textureOffset(sampler, p.xy / p.z, offset); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler2D> sampler, vec3 p, constexpr ivec2 offset, float bias) +public vector textureProjOffset(Sampler2D sampler, vec3 p, constexpr ivec2 offset, float bias) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProjOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias|ConstOffset $bias $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias|ConstOffset $bias $offset }; default: return textureOffset(sampler, p.xy / p.z, offset, bias); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler2D> sampler, vec4 p, constexpr ivec2 offset) +public vector textureProjOffset(Sampler2D sampler, vec4 p, constexpr ivec2 offset) { __requireComputeDerivative(); __target_switch @@ -2893,7 +2896,7 @@ public vector textureProjOffset(Sampler2D> sampler, vec4 p, con { vec4 xyw__ = p.xyww; return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $xyw__ ConstOffset $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $xyw__ ConstOffset $offset }; } default: @@ -2901,10 +2904,10 @@ public vector textureProjOffset(Sampler2D> sampler, vec4 p, con } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler2D> sampler, vec4 p, constexpr ivec2 offset, float bias) +public vector textureProjOffset(Sampler2D sampler, vec4 p, constexpr ivec2 offset, float bias) { __requireComputeDerivative(); __target_switch @@ -2914,7 +2917,7 @@ public vector textureProjOffset(Sampler2D> sampler, vec4 p, con { vec4 xyw_ = p.xyww; return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $xyw_ Bias|ConstOffset $bias $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $xyw_ Bias|ConstOffset $bias $offset }; } default: @@ -2922,34 +2925,34 @@ public vector textureProjOffset(Sampler2D> sampler, vec4 p, con } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler3D> sampler, vec4 p, constexpr ivec3 offset) +public vector textureProjOffset(Sampler3D sampler, vec4 p, constexpr ivec3 offset) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProjOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p ConstOffset $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $p ConstOffset $offset }; default: return textureOffset(sampler, p.xyz / p.w, offset); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjOffset(Sampler3D> sampler, vec4 p, constexpr ivec3 offset, float bias) +public vector textureProjOffset(Sampler3D sampler, vec4 p, constexpr ivec3 offset, float bias) { __requireComputeDerivative(); __target_switch { case glsl: __intrinsic_asm "textureProjOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias|ConstOffset $bias $offset + result:$$vector = OpImageSampleProjImplicitLod $sampler $p Bias|ConstOffset $bias $offset }; default: return textureOffset(sampler, p.xyz / p.w, offset, bias); @@ -3044,19 +3047,19 @@ public float textureProjOffset(sampler2DShadow sampler, vec4 p, constexpr ivec2 // textureLodOffset // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0)] -public vector textureLodOffset(Sampler1D> sampler, float p, float lod, constexpr int offset) +public vector textureLodOffset(Sampler1D sampler, float p, float lod, constexpr int offset) { - return __vectorReshape<4>(sampler.SampleLevel(p, lod, offset)); + return __vectorReshape2(sampler.SampleLevel(p, lod, offset)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_0)] -public vector textureLodOffset(_Texture< - vector, +public vector textureLodOffset(_Texture< + T, Shape, isArray, 0, // isMS @@ -3067,7 +3070,7 @@ public vector textureLodOffset(_Texture< format > sampler, vector p, float lod, constexpr vector offset) { - return __vectorReshape<4>(sampler.SampleLevel(p, lod, offset)); + return __vectorReshape2(sampler.SampleLevel(p, lod, offset)); } [ForceInline] @@ -3134,26 +3137,26 @@ public float textureLodOffset(sampler1DArrayShadow sampler, vec3 p, float lod, c // textureProjLod // ------------------- -__generic +__generic [ForceInline] [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLod(Sampler1D> sampler, vec2 p, float lod) +public vector textureProjLod(Sampler1D sampler, vec2 p, float lod) { __target_switch { case glsl: __intrinsic_asm "textureProjLod"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod $lod + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod $lod }; default: return textureLod(sampler, p.x / p.y, lod); } } -__generic +__generic [ForceInline] [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLod(Sampler1D> sampler, vec4 p, float lod) +public vector textureProjLod(Sampler1D sampler, vec4 p, float lod) { __target_switch { @@ -3162,7 +3165,7 @@ public vector textureProjLod(Sampler1D> sampler, vec4 p, float { vec4 xw__ = p.xwww; return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $xw__ Lod $lod + result:$$vector = OpImageSampleProjExplicitLod $sampler $xw__ Lod $lod }; } default: @@ -3170,26 +3173,26 @@ public vector textureProjLod(Sampler1D> sampler, vec4 p, float } } -__generic +__generic [ForceInline] [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLod(Sampler2D> sampler, vec3 p, float lod) +public vector textureProjLod(Sampler2D sampler, vec3 p, float lod) { __target_switch { case glsl: __intrinsic_asm "textureProjLod"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod $lod + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod $lod }; default: return textureLod(sampler, p.xy / p.z, lod); } } -__generic +__generic [ForceInline] [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLod(Sampler2D> sampler, vec4 p, float lod) +public vector textureProjLod(Sampler2D sampler, vec4 p, float lod) { __target_switch { @@ -3198,7 +3201,7 @@ public vector textureProjLod(Sampler2D> sampler, vec4 p, float { vec4 xyw_ = p.xyww; return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $xyw_ Lod $lod + result:$$vector = OpImageSampleProjExplicitLod $sampler $xyw_ Lod $lod }; } default: @@ -3206,16 +3209,16 @@ public vector textureProjLod(Sampler2D> sampler, vec4 p, float } } -__generic +__generic [ForceInline] [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLod(Sampler3D> sampler, vec4 p, float lod) +public vector textureProjLod(Sampler3D sampler, vec4 p, float lod) { __target_switch { case glsl: __intrinsic_asm "textureProjLod"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod $lod + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod $lod }; default: return textureLod(sampler, p.xyz / p.w, lod); @@ -3266,26 +3269,26 @@ public float textureProjLod(sampler2DShadow sampler, vec4 p, float lod) // textureProjLodOffset // ------------------- -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLodOffset(Sampler1D> sampler, vec2 p, float lod, constexpr int offset) +public vector textureProjLodOffset(Sampler1D sampler, vec2 p, float lod, constexpr int offset) { __target_switch { case glsl: __intrinsic_asm "textureProjLodOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod|ConstOffset $lod $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod|ConstOffset $lod $offset }; default: return textureLodOffset(sampler, p.x / p.y, lod, offset); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLodOffset(Sampler1D> sampler, vec4 p, float lod, constexpr int offset) +public vector textureProjLodOffset(Sampler1D sampler, vec4 p, float lod, constexpr int offset) { __target_switch { @@ -3294,7 +3297,7 @@ public vector textureProjLodOffset(Sampler1D> sampler, vec4 p, { vec4 xw__ = p.xwww; return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $xw__ Lod|ConstOffset $lod $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $xw__ Lod|ConstOffset $lod $offset }; } default: @@ -3302,26 +3305,26 @@ public vector textureProjLodOffset(Sampler1D> sampler, vec4 p, } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLodOffset(Sampler2D> sampler, vec3 p, float lod, constexpr ivec2 offset) +public vector textureProjLodOffset(Sampler2D sampler, vec3 p, float lod, constexpr ivec2 offset) { __target_switch { case glsl: __intrinsic_asm "textureProjLodOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod|ConstOffset $lod $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod|ConstOffset $lod $offset }; default: return textureLodOffset(sampler, p.xy / p.z, lod, offset); } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLodOffset(Sampler2D> sampler, vec4 p, float lod, constexpr ivec2 offset) +public vector textureProjLodOffset(Sampler2D sampler, vec4 p, float lod, constexpr ivec2 offset) { __target_switch { @@ -3330,7 +3333,7 @@ public vector textureProjLodOffset(Sampler2D> sampler, vec4 p, { vec4 xyw_ = p.xyww; return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $xyw_ Lod|ConstOffset $lod $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $xyw_ Lod|ConstOffset $lod $offset }; } default: @@ -3338,16 +3341,16 @@ public vector textureProjLodOffset(Sampler2D> sampler, vec4 p, } } -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_sm_4_0_fragment)] -public vector textureProjLodOffset(Sampler3D> sampler, vec4 p, float lod, constexpr ivec3 offset) +public vector textureProjLodOffset(Sampler3D sampler, vec4 p, float lod, constexpr ivec3 offset) { __target_switch { case glsl: __intrinsic_asm "textureProjLodOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod|ConstOffset $lod $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Lod|ConstOffset $lod $offset }; default: return textureLodOffset(sampler, p.xyz / p.w, lod, offset); @@ -3399,19 +3402,19 @@ public float textureProjLodOffset(sampler2DShadow sampler, vec4 p, float lod, co // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureGrad(Sampler1D> sampler, float p, float dPdx, float dPdy) +public vector textureGrad(Sampler1D sampler, float p, float dPdx, float dPdy) { - return __vectorReshape<4>(sampler.SampleGrad(p, dPdx, dPdy)); + return __vectorReshape2(sampler.SampleGrad(p, dPdx, dPdy)); } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureGrad(_Texture< - vector, +public vector textureGrad(_Texture< + T, Shape, isArray, 0, // isMS @@ -3422,7 +3425,7 @@ public vector textureGrad(_Texture< format > sampler, vector p, vector dPdx, vector dPdy) { - return __vectorReshape<4>(sampler.SampleGrad(p, dPdx, dPdy)); + return __vectorReshape2(sampler.SampleGrad(p, dPdx, dPdy)); } [ForceInline] @@ -3514,19 +3517,19 @@ public float textureGrad(sampler2DArrayShadow sampler, vec4 p, vec2 dPdx, vec2 d // textureGradOffset // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureGradOffset(Sampler1D> sampler, float p, float dPdx, float dPdy, constexpr int offset) +public vector textureGradOffset(Sampler1D sampler, float p, float dPdx, float dPdy, constexpr int offset) { - return __vectorReshape<4>(sampler.SampleGrad(p, dPdx, dPdy, offset)); + return __vectorReshape2(sampler.SampleGrad(p, dPdx, dPdy, offset)); } -__generic +__generic [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] [ForceInline] -public vector textureGradOffset(_Texture< - vector, +public vector textureGradOffset(_Texture< + T, Shape, isArray, 0, // isMS @@ -3537,7 +3540,7 @@ public vector textureGradOffset(_Texture< format > sampler, vector p, vector dPdx, vector dPdy, constexpr vector offset) { - return __vectorReshape<4>(sampler.SampleGrad(p, dPdx, dPdy, offset)); + return __vectorReshape2(sampler.SampleGrad(p, dPdx, dPdy, offset)); } [ForceInline] @@ -3612,26 +3615,26 @@ public float textureGradOffset(sampler2DArrayShadow sampler, vec4 p, vec2 dPdx, // textureProjGrad // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGrad(Sampler1D> sampler, vec2 p, float dPdx, float dPdy) +public vector textureProjGrad(Sampler1D sampler, vec2 p, float dPdx, float dPdy) { __target_switch { case glsl: __intrinsic_asm "textureProjGrad"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad $dPdx $dPdy + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad $dPdx $dPdy }; default: return textureGrad(sampler, p.x / p.y, dPdx, dPdy); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGrad(Sampler1D> sampler, vec4 p, float dPdx, float dPdy) +public vector textureProjGrad(Sampler1D sampler, vec4 p, float dPdx, float dPdy) { __target_switch { @@ -3640,7 +3643,7 @@ public vector textureProjGrad(Sampler1D> sampler, vec4 p, float { vec4 xw__ = p.xwww; return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $xw__ Grad $dPdx $dPdy + result:$$vector = OpImageSampleProjExplicitLod $sampler $xw__ Grad $dPdx $dPdy }; } default: @@ -3648,26 +3651,26 @@ public vector textureProjGrad(Sampler1D> sampler, vec4 p, float } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGrad(Sampler2D> sampler, vec3 p, vec2 dPdx, vec2 dPdy) +public vector textureProjGrad(Sampler2D sampler, vec3 p, vec2 dPdx, vec2 dPdy) { __target_switch { case glsl: __intrinsic_asm "textureProjGrad"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad $dPdx $dPdy + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad $dPdx $dPdy }; default: return textureGrad(sampler, p.xy / p.z, dPdx, dPdy); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGrad(Sampler2D> sampler, vec4 p, vec2 dPdx, vec2 dPdy) +public vector textureProjGrad(Sampler2D sampler, vec4 p, vec2 dPdx, vec2 dPdy) { __target_switch { @@ -3676,7 +3679,7 @@ public vector textureProjGrad(Sampler2D> sampler, vec4 p, vec2 { vec4 xyw_ = p.xyww; return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $xyw_ Grad $dPdx $dPdy + result:$$vector = OpImageSampleProjExplicitLod $sampler $xyw_ Grad $dPdx $dPdy }; } default: @@ -3684,16 +3687,16 @@ public vector textureProjGrad(Sampler2D> sampler, vec4 p, vec2 } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGrad(Sampler3D> sampler, vec4 p, vec3 dPdx, vec3 dPdy) +public vector textureProjGrad(Sampler3D sampler, vec4 p, vec3 dPdx, vec3 dPdy) { __target_switch { case glsl: __intrinsic_asm "textureProjGrad"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad $dPdx $dPdy + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad $dPdx $dPdy }; default: return textureGrad(sampler, p.xyz / p.w, dPdx, dPdy); @@ -3744,26 +3747,26 @@ public float textureProjGrad(sampler2DShadow sampler, vec4 p, vec2 dPdx, vec2 dP // textureProjGradOffset // ------------------- -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGradOffset(Sampler1D> sampler, vec2 p, float dPdx, float dPdy, constexpr int offset) +public vector textureProjGradOffset(Sampler1D sampler, vec2 p, float dPdx, float dPdy, constexpr int offset) { __target_switch { case glsl: __intrinsic_asm "textureProjGradOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad|ConstOffset $dPdx $dPdy $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad|ConstOffset $dPdx $dPdy $offset }; default: return textureGradOffset(sampler, p.x / p.y, dPdx, dPdy, offset); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGradOffset(Sampler1D> sampler, vec4 p, float dPdx, float dPdy, constexpr int offset) +public vector textureProjGradOffset(Sampler1D sampler, vec4 p, float dPdx, float dPdy, constexpr int offset) { __target_switch { @@ -3772,7 +3775,7 @@ public vector textureProjGradOffset(Sampler1D> sampler, vec4 p, { vec4 xw__ = p.xwww; return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $xw__ Grad|ConstOffset $dPdx $dPdy $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $xw__ Grad|ConstOffset $dPdx $dPdy $offset }; } default: @@ -3780,26 +3783,26 @@ public vector textureProjGradOffset(Sampler1D> sampler, vec4 p, } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGradOffset(Sampler2D> sampler, vec3 p, vec2 dPdx, vec2 dPdy, constexpr ivec2 offset) +public vector textureProjGradOffset(Sampler2D sampler, vec3 p, vec2 dPdx, vec2 dPdy, constexpr ivec2 offset) { __target_switch { case glsl: __intrinsic_asm "textureProjGradOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad|ConstOffset $dPdx $dPdy $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad|ConstOffset $dPdx $dPdy $offset }; default: return textureGradOffset(sampler, p.xy / p.z, dPdx, dPdy, offset); } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGradOffset(Sampler2D> sampler, vec4 p, vec2 dPdx, vec2 dPdy, constexpr ivec2 offset) +public vector textureProjGradOffset(Sampler2D sampler, vec4 p, vec2 dPdx, vec2 dPdy, constexpr ivec2 offset) { __target_switch { @@ -3808,7 +3811,7 @@ public vector textureProjGradOffset(Sampler2D> sampler, vec4 p, { vec4 xyw_ = p.xyww; return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $xyw_ Grad|ConstOffset $dPdx $dPdy $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $xyw_ Grad|ConstOffset $dPdx $dPdy $offset }; } default: @@ -3816,16 +3819,16 @@ public vector textureProjGradOffset(Sampler2D> sampler, vec4 p, } } -__generic +__generic [ForceInline] [require(cpp_glsl_hlsl_spirv, texture_sm_4_1)] -public vector textureProjGradOffset(Sampler3D> sampler, vec4 p, vec3 dPdx, vec3 dPdy, constexpr ivec3 offset) +public vector textureProjGradOffset(Sampler3D sampler, vec4 p, vec3 dPdx, vec3 dPdy, constexpr ivec3 offset) { __target_switch { case glsl: __intrinsic_asm "textureProjGradOffset"; case spirv: return spirv_asm { - result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad|ConstOffset $dPdx $dPdy $offset + result:$$vector = OpImageSampleProjExplicitLod $sampler $p Grad|ConstOffset $dPdx $dPdy $offset }; default: return textureGradOffset(sampler, p.xyz / p.w, dPdx, dPdy, offset); @@ -3880,11 +3883,11 @@ public float textureProjGradOffset(sampler2DShadow sampler, vec4 p, vec2 dPdx, v // textureGather // ------------------- -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_gather)] -public vector textureGather(_Texture< - vector, +public vector textureGather(_Texture< + T, Shape, isArray, 0, // isMS @@ -3926,11 +3929,11 @@ public vec4 textureGather(_Texture< // textureGatherOffset // ------------------- -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_gather)] -public vector textureGatherOffset(_Texture< - vector, +public vector textureGatherOffset(_Texture< + T, __Shape2D, isArray, 0, // isMS @@ -3972,11 +3975,11 @@ public vec4 textureGatherOffset(_Texture< // textureGatherOffsets // ------------------- -__generic +__generic [ForceInline] [require(glsl_hlsl_spirv, texture_gather)] -public vector textureGatherOffsets(_Texture< - vector, +public vector textureGatherOffsets(_Texture< + T, __Shape2D, isArray, 0, // isMS diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index d620197f37..9d6a81f84a 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -10,6 +10,7 @@ void __requireGLSLExtension(String extensionName); /// Represents an interface for buffer data layout. /// This interface is used as a base for defining specific data layouts for buffers. [sealed] +[builtin] __magic_type(IBufferDataLayoutType) interface IBufferDataLayout { @@ -512,6 +513,86 @@ __intrinsic_op(makeVector) __generic vector __makeVector(vector vec, T scalar); +//@public: +/// Represent types that can be used as texel element. +[sealed] +[builtin] +interface ITexelElement +{ + associatedtype Element : __BuiltinArithmeticType; + static const int elementCount; + __init(Element x); +} + +${{{ +// Scalar types that can be used as texel element. +const char* texeElementScalarTypes[] = { + "half", + "float", + "int", + "uint", + "int8_t", + "int16_t", + "uint8_t", + "uint16_t" +}; +for (auto elementType : texeElementScalarTypes) +{ +}}} +extension $(elementType) : ITexelElement +{ + typealias Element = $(elementType); + static const int elementCount = 1; + __intrinsic_op(0) __init(Element x); +} +extension vector<$(elementType), N> : ITexelElement +{ + typealias Element = $(elementType); + static const int elementCount = N; + __intrinsic_op($(kIROp_MakeVectorFromScalar)) __init(Element x); +} +${{{ +} // end for texelElementScalarTypes. +}}} + +// Additional 64-bit types that can be used as texel element. +extension double:ITexelElement +{ + typealias Element = double; + static const int elementCount = 1; + __intrinsic_op(0) __init(Element x); +} +extension double2:ITexelElement +{ + typealias Element = double; + static const int elementCount = 2; + __intrinsic_op($(kIROp_MakeVectorFromScalar)) __init(Element x); +} +extension uint64_t:ITexelElement +{ + typealias Element = uint64_t; + static const int elementCount = 1; + __intrinsic_op(0) __init(Element x); +} +extension int64_t:ITexelElement +{ + typealias Element = int64_t; + static const int elementCount = 1; + __intrinsic_op(0) __init(Element x); +} +extension vector:ITexelElement +{ + typealias Element = uint64_t; + static const int elementCount = 2; + __intrinsic_op($(kIROp_MakeVectorFromScalar)) __init(Element x); +} +extension vector:ITexelElement +{ + typealias Element = int64_t; + static const int elementCount = 2; + __intrinsic_op($(kIROp_MakeVectorFromScalar)) __init(Element x); +} + //@public: /// A parameterized type that represents all flavors of texture types supported by the Slang language. /// Please note that this type is not intended to be used directly in user code, and not all combinations @@ -591,7 +672,7 @@ vector __makeVector(vector vec, T scalar); /// @category texture_types Texture types __magic_type(TextureType) __intrinsic_type($(kIROp_TextureType)) -struct _Texture +struct _Texture { } @@ -776,7 +857,7 @@ float __glsl_texture_level_offset_1d_shadow(TTexture //@public: -__generic +__generic extension _Texture { //@hidden: @@ -1498,7 +1579,7 @@ extension _Texture // Non-combined texture types specific functions -__generic +__generic extension _Texture { typealias TextureCoord = vector; @@ -1596,7 +1677,7 @@ extension _Texture } } -__generic +__generic extension _Texture { [__readNone] @@ -2720,7 +2801,7 @@ for (int isMS = 0; isMS <= 1; isMS++) { TextureTypeInfo textureTypeInfo(kBaseTextureShapes[shapeIndex], isArray, isMS, 0, sb, path); }}}} -__generic +__generic extension _Texture { ${{{{ @@ -2733,7 +2814,7 @@ ${{{{ }}}} // Texture.GetSamplePosition(int s); -__generic +__generic extension _Texture { [require(cpp_cuda_glsl_hlsl_spirv, texture_sm_4_1_vertex_fragment_geometry)] @@ -2745,10 +2826,10 @@ Array __makeArray(T v0, T v1, T v2, T v3); // Beginning of Texture Gather -__generic +__generic [ForceInline] [require(glsl_metal_spirv_wgsl, texture_gather)] -vector __texture_gather( +vector __texture_gather( _Texture texture, SamplerState s, vector location, @@ -2781,7 +2862,7 @@ vector __texture_gather( case spirv: return spirv_asm { %sampledImage : __sampledImageType(texture) = OpSampledImage $texture $s; - result:$$vector = OpImageGather %sampledImage $location $component; + result:$$vector = OpImageGather %sampledImage $location $component; }; case wgsl: if (isShadow == 1) @@ -2814,10 +2895,10 @@ vector __texture_gather( } } -__generic +__generic [ForceInline] [require(glsl_spirv, texture_gather)] -vector __texture_gather( +vector __texture_gather( _Texture sampler, vector location, int component) @@ -2828,15 +2909,15 @@ vector __texture_gather( __intrinsic_asm "textureGather($0, $1, $2)"; case spirv: return spirv_asm { - result:$$vector = OpImageGather $sampler $location $component; + result:$$vector = OpImageGather $sampler $location $component; }; } } -__generic +__generic [ForceInline] [require(glsl_metal_spirv_wgsl, texture_gather)] -vector __texture_gather_offset( +vector __texture_gather_offset( _Texture texture, SamplerState s, vector location, @@ -2862,7 +2943,7 @@ vector __texture_gather_offset( return spirv_asm { OpCapability ImageGatherExtended; %sampledImage : __sampledImageType(texture) = OpSampledImage $texture $s; - result:$$vector = OpImageGather %sampledImage $location $component Offset $offset; + result:$$vector = OpImageGather %sampledImage $location $component Offset $offset; }; case wgsl: if (isShadow == 1) @@ -2895,10 +2976,10 @@ vector __texture_gather_offset( } } -__generic +__generic [ForceInline] [require(glsl_spirv, texture_gather)] -vector __texture_gather_offset( +vector __texture_gather_offset( _Texture sampler, vector location, constexpr vector offset, @@ -2911,15 +2992,15 @@ vector __texture_gather_offset( case spirv: return spirv_asm { OpCapability ImageGatherExtended; - result:$$vector = OpImageGather $sampler $location $component Offset $offset; + result:$$vector = OpImageGather $sampler $location $component Offset $offset; }; } } -__generic +__generic [ForceInline] [require(glsl_spirv, texture_gather)] -vector __texture_gather_offsets( +vector __texture_gather_offsets( _Texture texture, SamplerState s, vector location, @@ -2938,15 +3019,15 @@ vector __texture_gather_offsets( return spirv_asm { OpCapability ImageGatherExtended; %sampledImage : __sampledImageType(texture) = OpSampledImage $texture $s; - result:$$vector = OpImageGather %sampledImage $location $component ConstOffsets $offsets; + result:$$vector = OpImageGather %sampledImage $location $component ConstOffsets $offsets; }; } } -__generic +__generic [ForceInline] [require(glsl_spirv, texture_gather)] -vector __texture_gather_offsets( +vector __texture_gather_offsets( _Texture sampler, vector location, constexpr vector offset1, @@ -2963,19 +3044,19 @@ vector __texture_gather_offsets( let offsets = __makeArray(offset1,offset2,offset3,offset4); return spirv_asm { OpCapability ImageGatherExtended; - result:$$vector = OpImageGather $sampler $location $component ConstOffsets $offsets; + result:$$vector = OpImageGather $sampler $location $component ConstOffsets $offsets; }; } } -__generic +__generic [ForceInline] [require(glsl_metal_spirv_wgsl, texture_gather)] -vector __texture_gatherCmp( +vector __texture_gatherCmp( _Texture texture, SamplerComparisonState s, vector location, - TElement compareValue) + T.Element compareValue) { __target_switch { @@ -2999,7 +3080,7 @@ vector __texture_gatherCmp( case spirv: return spirv_asm { %sampledImage : __sampledImageType(texture) = OpSampledImage $texture $s; - result:$$vector = OpImageDrefGather %sampledImage $location $compareValue; + result:$$vector = OpImageDrefGather %sampledImage $location $compareValue; }; case wgsl: static_assert(isShadow == 1, "WGSL supports textureGatherCompare only for depth textures."); @@ -3018,13 +3099,13 @@ vector __texture_gatherCmp( } } -__generic +__generic [ForceInline] [require(glsl_spirv, texture_gather)] -vector __texture_gatherCmp( +vector __texture_gatherCmp( _Texture sampler, vector location, - TElement compareValue) + T.Element compareValue) { __target_switch { @@ -3032,19 +3113,19 @@ vector __texture_gatherCmp( __intrinsic_asm "textureGather($0, $1, $2)"; case spirv: return spirv_asm { - result:$$vector = OpImageDrefGather $sampler $location $compareValue; + result:$$vector = OpImageDrefGather $sampler $location $compareValue; }; } } -__generic +__generic [ForceInline] [require(glsl_metal_spirv_wgsl, texture_gather)] -vector __texture_gatherCmp_offset( +vector __texture_gatherCmp_offset( _Texture texture, SamplerComparisonState s, vector location, - TElement compareValue, + T.Element compareValue, constexpr vector offset) { __target_switch @@ -3065,7 +3146,7 @@ vector __texture_gatherCmp_offset( case spirv: return spirv_asm { %sampledImage : __sampledImageType(texture) = OpSampledImage $texture $s; - result:$$vector = OpImageDrefGather %sampledImage $location $compareValue ConstOffset $offset; + result:$$vector = OpImageDrefGather %sampledImage $location $compareValue ConstOffset $offset; }; case wgsl: static_assert(isShadow == 1, "WGSL supports textureGatherCompare only for depth textures."); @@ -3084,13 +3165,13 @@ vector __texture_gatherCmp_offset( } } -__generic +__generic [ForceInline] [require(glsl_spirv, texture_gather)] -vector __texture_gatherCmp_offset( +vector __texture_gatherCmp_offset( _Texture sampler, vector location, - TElement compareValue, + T.Element compareValue, constexpr vector offset) { __target_switch @@ -3099,19 +3180,19 @@ vector __texture_gatherCmp_offset( __intrinsic_asm "textureGatherOffset($0, $1, $2, $3)"; case spirv: return spirv_asm { - result:$$vector = OpImageDrefGather $sampler $location $compareValue ConstOffset $offset; + result:$$vector = OpImageDrefGather $sampler $location $compareValue ConstOffset $offset; }; } } -__generic +__generic [ForceInline] [require(glsl_spirv, texture_gather)] -vector __texture_gatherCmp_offsets( +vector __texture_gatherCmp_offsets( _Texture texture, SamplerComparisonState s, vector location, - TElement compareValue, + T.Element compareValue, vector offset1, vector offset2, vector offset3, @@ -3126,18 +3207,18 @@ vector __texture_gatherCmp_offsets( return spirv_asm { OpCapability ImageGatherExtended; %sampledImage : __sampledImageType(texture) = OpSampledImage $texture $s; - result:$$vector = OpImageDrefGather %sampledImage $location $compareValue ConstOffsets $offsets; + result:$$vector = OpImageDrefGather %sampledImage $location $compareValue ConstOffsets $offsets; }; } } -__generic +__generic [ForceInline] [require(glsl_spirv, texture_gather)] -vector __texture_gatherCmp_offsets( +vector __texture_gatherCmp_offsets( _Texture sampler, vector location, - TElement compareValue, + T.Element compareValue, vector offset1, vector offset2, vector offset3, @@ -3151,30 +3232,26 @@ vector __texture_gatherCmp_offsets( let offsets = __makeArray(offset1,offset2,offset3,offset4); return spirv_asm { OpCapability ImageGatherExtended; - result:$$vector = OpImageDrefGather $sampler $location $compareValue ConstOffsets $offsets; + result:$$vector = OpImageDrefGather $sampler $location $compareValue ConstOffsets $offsets; }; } } ${{{{ for (int isCombined = 0; isCombined < 2; isCombined++) -for (int isScalarTexture = 0; isScalarTexture < 2; isScalarTexture++) { - const char* extSizeParam = isScalarTexture ? "" : ", let N:int"; - const char* extTexType = isScalarTexture ? "T" : "vector"; - }}}} -// Gather for [TextureType = $(extTexType), isCombined = $(isCombined)] +// Gather for [isCombined = $(isCombined)] -__generic -extension _Texture<$(extTexType),Shape,isArray,0,sampleCount,0,isShadow,$(isCombined),format> +__generic +extension _Texture { ${{{{ for (int isShadow = 0; isShadow < 2; isShadow++) for (auto componentId = 0; componentId < 5; componentId++) { const char* compareFunc = isShadow ? "Cmp" : ""; - const char* compareParam = isShadow ? ", T compareValue" : ""; + const char* compareParam = isShadow ? ", T.Element compareValue" : ""; const char* compareArg = isShadow ? ", compareValue" : ""; // Some targets support the combined texture natively @@ -3190,7 +3267,7 @@ ${{{{ }}}} [ForceInline] [require(glsl_hlsl_metal_spirv_wgsl, texture_gather)] - vector Gather$(compareFunc)$(componentFunc)( + vector Gather$(compareFunc)$(componentFunc)( $(samplerParam) vector location $(compareParam)) @@ -3203,16 +3280,16 @@ ${{{{ case hlsl: __intrinsic_asm ".Gather$(compareFunc)$(componentFunc)"; case metal: case wgsl: - return __texture_gather$(compareFunc)($(getTexture) $(getSampler), location $(compareArg) $(componentArg)); + return __texture_gather$(compareFunc)($(getTexture) $(getSampler), location $(compareArg) $(componentArg)); case glsl: case spirv: - return __texture_gather$(compareFunc)(this $(samplerArg), location $(compareArg) $(componentArg)); + return __texture_gather$(compareFunc)(this $(samplerArg), location $(compareArg) $(componentArg)); } } [ForceInline] [require(hlsl, texture_gather)] - vector Gather$(compareFunc)$(componentFunc)( + vector Gather$(compareFunc)$(componentFunc)( $(samplerParam) vector location $(compareParam), @@ -3229,7 +3306,7 @@ ${{{{ [ForceInline] [require(glsl_hlsl_metal_spirv_wgsl, texture_gather)] - vector Gather$(compareFunc)$(componentFunc)( + vector Gather$(compareFunc)$(componentFunc)( $(samplerParam) vector location $(compareParam), @@ -3243,16 +3320,16 @@ ${{{{ case hlsl: __intrinsic_asm ".Gather$(compareFunc)$(componentFunc)"; case metal: case wgsl: - return __texture_gather$(compareFunc)_offset($(getTexture) $(getSampler), location $(compareArg), offset $(componentArg)); + return __texture_gather$(compareFunc)_offset($(getTexture) $(getSampler), location $(compareArg), offset $(componentArg)); case glsl: case spirv: - return __texture_gather$(compareFunc)_offset(this $(samplerArg), location $(compareArg), offset $(componentArg)); + return __texture_gather$(compareFunc)_offset(this $(samplerArg), location $(compareArg), offset $(componentArg)); } } [ForceInline] [require(hlsl, texture_gather)] - vector Gather$(compareFunc)$(componentFunc)( + vector Gather$(compareFunc)$(componentFunc)( $(samplerParam) vector location $(compareParam), @@ -3270,7 +3347,7 @@ ${{{{ [ForceInline] [require(glsl_hlsl_spirv, texture_gather)] - vector Gather$(compareFunc)$(componentFunc)( + vector Gather$(compareFunc)$(componentFunc)( $(samplerParam) vector location $(compareParam), @@ -3293,7 +3370,7 @@ ${{{{ [ForceInline] [require(hlsl, texture_gather)] - vector Gather$(compareFunc)$(componentFunc)( + vector Gather$(compareFunc)$(componentFunc)( $(samplerParam) vector location $(compareParam), @@ -3315,7 +3392,7 @@ ${{{{ ${{{{ } // for (componentId) }}}} -} // End of: Gather for [TextureType = $(extTexType), isCombined = $(isCombined)] +} // End of: Gather for [isCombined = $(isCombined)] ${{{{ } // for (isScalarTexture) @@ -3325,7 +3402,7 @@ ${{{{ // Load/Subscript for readonly, no MS textures -__generic +__generic extension _Texture { //@hidden: @@ -3453,7 +3530,7 @@ extension _Texture case $(SLANG_TEXTURE_3D): __intrinsic_asm "textureLoad($0, ($1).xyz, ($1).w)$z"; } - return T(); + return __default(); } } @@ -3555,7 +3632,7 @@ extension _Texture // Texture Load/Subscript for readonly, MS textures -__generic +__generic extension _Texture { //@hidden: @@ -3746,7 +3823,7 @@ ${{{{ const char* glslIntrinsicMSOffset = "$cimageLoad($0, ($1)+($3), $2)$z"; }}}} -__generic +__generic extension _Texture { ${{{{ @@ -4081,7 +4158,7 @@ if (access == kCoreModule_ResourceAccessReadWrite) { // RW MS textures. -__generic +__generic extension _Texture { [__readNone] @@ -4234,7 +4311,7 @@ ${{{{ }}}} // Definitions to support the legacy texture .mips[][] operator. -struct __TextureMip +struct __TextureMip { _Texture tex; int mip; @@ -4245,7 +4322,7 @@ struct __TextureMip +struct __TextureMips { _Texture tex; __subscript(int mip)->__TextureMip @@ -4256,7 +4333,7 @@ struct __TextureMips +__generic extension _Texture { property __TextureMips mips @@ -4267,7 +4344,7 @@ extension _Texture +struct __TextureSample { _Texture tex; int sample; @@ -4278,7 +4355,7 @@ struct __TextureSample +struct __TextureSampleMS { _Texture tex; __subscript(int sample)->__TextureSample @@ -4288,7 +4365,7 @@ struct __TextureSampleMS +__generic extension _Texture { property __TextureSampleMS sample @@ -4345,7 +4422,7 @@ ${{{{ /// @param format The storage format of the texture. /// @see Please refer to `_Texture` for more information about texture types. /// @category texture_types -typealias $(accessPrefix[access])$(textureTypeName)$(shapeTypeNames[shape])$(msPostFix[isMS])$(arrayPostFix[isArray]) = _Texture; +typealias $(accessPrefix[access])$(textureTypeName)$(shapeTypeNames[shape])$(msPostFix[isMS])$(arrayPostFix[isArray]) = _Texture; ${{{{ } }}}} @@ -15633,7 +15710,7 @@ for (int aa = 0; aa < kBaseBufferAccessLevelCount; ++aa) { auto access = kBaseBufferAccessLevels[aa].access; sb << "/// @category texture_types\n"; - sb << "__generic\n"; + sb << "__generic\n"; sb << "typealias "; sb << kBaseBufferAccessLevels[aa].name; sb << "Buffer = _Texture;\n"; @@ -15648,7 +15725,7 @@ for (int aa = 0; aa < kBaseBufferAccessLevelCount; ++aa) char const* requireToSet_onlyHLSL = (isReadOnly) ? "[require(hlsl, texture_sm_4_1)]" : "[require(hlsl, texture_sm_4_1_compute_fragment)]"; }}}} -__generic +__generic extension _Texture { [__readNone] @@ -16920,23 +16997,33 @@ interface __BuiltinSamplerFeedbackType {}; [sealed] __magic_type(FeedbackType, $(int(FeedbackType::Kind::MinMip))) __target_intrinsic(hlsl, SAMPLER_FEEDBACK_MIN_MIP) -struct SAMPLER_FEEDBACK_MIN_MIP : __BuiltinSamplerFeedbackType {}; +struct SAMPLER_FEEDBACK_MIN_MIP : __BuiltinSamplerFeedbackType, ITexelElement { + typealias Element = float; + static const int elementCount = 1; + __intrinsic_op(0) __init(float x){} +}; /// @category texture_types [sealed] __magic_type(FeedbackType, $(int(FeedbackType::Kind::MipRegionUsed))) __target_intrinsic(hlsl, SAMPLER_FEEDBACK_MIP_REGION_USED) -struct SAMPLER_FEEDBACK_MIP_REGION_USED : __BuiltinSamplerFeedbackType {}; +struct SAMPLER_FEEDBACK_MIP_REGION_USED : __BuiltinSamplerFeedbackType, ITexelElement +{ + typealias Element = float; + static const int elementCount = 1; + __intrinsic_op(0) __init(float x){} +}; // All of these objects are write-only resources that point to a special kind of unordered access view meant for sampler feedback. -__generic -extension _Texture +extension _Texture + where T:ITexelElement + where T:__BuiltinSamplerFeedbackType { // With Clamp [require(cpp_hlsl)] - void WriteSamplerFeedback(Texture2D tex, SamplerState samp, float2 location, float clamp) + void WriteSamplerFeedback(Texture2D tex, SamplerState samp, float2 location, float clamp) { __target_switch { @@ -16946,7 +17033,7 @@ extension _Texture(Texture2D tex, SamplerState samp, float2 location, float bias, float clamp) + void WriteSamplerFeedbackBias(Texture2D tex, SamplerState samp, float2 location, float bias, float clamp) { __target_switch { @@ -16956,7 +17043,7 @@ extension _Texture(Texture2D tex, SamplerState samp, float2 location, float2 ddx, float2 ddy, float clamp) + void WriteSamplerFeedbackGrad(Texture2D tex, SamplerState samp, float2 location, float2 ddx, float2 ddy, float clamp) { __target_switch { @@ -16968,7 +17055,7 @@ extension _Texture(Texture2D tex, SamplerState samp, float2 location, float lod) + void WriteSamplerFeedbackLevel(Texture2D tex, SamplerState samp, float2 location, float lod) { __target_switch { @@ -16980,7 +17067,7 @@ extension _Texture(Texture2D tex, SamplerState samp, float2 location) + void WriteSamplerFeedback(Texture2D tex, SamplerState samp, float2 location) { __target_switch { @@ -16990,7 +17077,7 @@ extension _Texture(Texture2D tex, SamplerState samp, float2 location, float bias) + void WriteSamplerFeedbackBias(Texture2D tex, SamplerState samp, float2 location, float bias) { __target_switch { @@ -17000,7 +17087,7 @@ extension _Texture(Texture2D tex, SamplerState samp, float2 location, float2 ddx, float2 ddy) + void WriteSamplerFeedbackGrad(Texture2D tex, SamplerState samp, float2 location, float2 ddx, float2 ddy) { __target_switch { @@ -17010,13 +17097,14 @@ extension _Texture -extension _Texture +extension _Texture + where T:__BuiltinSamplerFeedbackType + where T:ITexelElement { // With Clamp [require(cpp_hlsl)] - void WriteSamplerFeedback(Texture2DArray texArray, SamplerState samp, float3 location, float clamp) + void WriteSamplerFeedback(Texture2DArray texArray, SamplerState samp, float3 location, float clamp) { __target_switch { @@ -17026,7 +17114,7 @@ extension _Texture(Texture2DArray texArray, SamplerState samp, float3 location, float bias, float clamp) + void WriteSamplerFeedbackBias(Texture2DArray texArray, SamplerState samp, float3 location, float bias, float clamp) { __target_switch { @@ -17036,7 +17124,7 @@ extension _Texture(Texture2DArray texArray, SamplerState samp, float3 location, float3 ddx, float3 ddy, float clamp) + void WriteSamplerFeedbackGrad(Texture2DArray texArray, SamplerState samp, float3 location, float3 ddx, float3 ddy, float clamp) { __target_switch { @@ -17048,7 +17136,7 @@ extension _Texture(Texture2DArray texArray, SamplerState samp, float3 location, float lod) + void WriteSamplerFeedbackLevel(Texture2DArray texArray, SamplerState samp, float3 location, float lod) { __target_switch { @@ -17060,7 +17148,7 @@ extension _Texture(Texture2DArray texArray, SamplerState samp, float3 location) + void WriteSamplerFeedback(Texture2DArray texArray, SamplerState samp, float3 location) { __target_switch { @@ -17070,7 +17158,7 @@ extension _Texture(Texture2DArray texArray, SamplerState samp, float3 location, float bias) + void WriteSamplerFeedbackBias(Texture2DArray texArray, SamplerState samp, float3 location, float bias) { __target_switch { @@ -17080,7 +17168,7 @@ extension _Texture(Texture2DArray texArray, SamplerState samp, float3 location, float3 ddx, float3 ddy) + void WriteSamplerFeedbackGrad(Texture2DArray texArray, SamplerState samp, float3 location, float3 ddx, float3 ddy) { __target_switch { @@ -20025,10 +20113,10 @@ ${ // for any resource type. } -__intrinsic_op($(kIROp_GetRegisterSpace)) uint __getRegisterSpace(_Texture texture); +__intrinsic_op($(kIROp_GetRegisterSpace)) uint __getRegisterSpace(_Texture texture); __intrinsic_op($(kIROp_GetRegisterSpace)) uint __getRegisterSpace(SamplerState sampler); -__intrinsic_op($(kIROp_GetRegisterIndex)) uint __getRegisterIndex(_Texture texture); +__intrinsic_op($(kIROp_GetRegisterIndex)) uint __getRegisterIndex(_Texture texture); __intrinsic_op($(kIROp_GetRegisterIndex)) uint __getRegisterIndex(SamplerState sampler); //@public: @@ -20218,7 +20306,7 @@ ${ // further clutter the original type declarations. } -__generic +__generic extension _Texture { ${ @@ -21035,7 +21123,7 @@ enum __DynamicResourceKind Sampler = 1 } -__generic +__generic extension _Texture : __IDynamicResourceCastable<__DynamicResourceKind.General> { __intrinsic_op($(kIROp_CastDynamicResource)) diff --git a/source/slang/slang-check-inheritance.cpp b/source/slang/slang-check-inheritance.cpp index f774aae383..7af2d2e811 100644 --- a/source/slang/slang-check-inheritance.cpp +++ b/source/slang/slang-check-inheritance.cpp @@ -1149,6 +1149,10 @@ InheritanceInfo SharedSemanticsContext::_calcInheritanceInfo( info.facets = FacetList(directFacet); return info; } + else if (auto modifiedType = as(type)) + { + return _calcInheritanceInfo(modifiedType->getBase(), circularityInfo); + } else { // As a fallback, any type not covered by the above cases will diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index b0d1fbb4cc..13a85e8abf 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -614,7 +614,10 @@ void CPPSourceEmitter::emitGlobalRTTISymbolPrefix() void CPPSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable) { - auto interfaceType = cast(witnessTable->getConformanceType()); + auto interfaceType = as(witnessTable->getConformanceType()); + + if (!interfaceType) + return; // Ignore witness tables for builtin interface types. if (isBuiltin(interfaceType)) diff --git a/source/slang/slang-ir-peephole.cpp b/source/slang/slang-ir-peephole.cpp index 0a08c67ffa..ced0b5eb00 100644 --- a/source/slang/slang-ir-peephole.cpp +++ b/source/slang/slang-ir-peephole.cpp @@ -829,6 +829,8 @@ struct PeepholeContext : InstPassBase case kIROp_VectorReshape: { auto fromType = as(inst->getOperand(0)->getDataType()); + if (!fromType) + break; auto resultType = as(inst->getDataType()); if (!resultType) { diff --git a/tests/diagnostics/illegal-texel-type.slang b/tests/diagnostics/illegal-texel-type.slang new file mode 100644 index 0000000000..847f9ea18b --- /dev/null +++ b/tests/diagnostics/illegal-texel-type.slang @@ -0,0 +1,7 @@ +//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK): -target spirv + +// CHECK: ([[# @LINE+1]]): error 38029 +Texture2D t1; + +// CHECK: ([[# @LINE+1]]): error 38029 +RWBuffer t2; \ No newline at end of file diff --git a/tests/glsl-intrinsic/compute-derivative/intrinsic-derivative-function-in-compute.slang b/tests/glsl-intrinsic/compute-derivative/intrinsic-derivative-function-in-compute.slang index c5c2e36cc0..1c91529698 100644 --- a/tests/glsl-intrinsic/compute-derivative/intrinsic-derivative-function-in-compute.slang +++ b/tests/glsl-intrinsic/compute-derivative/intrinsic-derivative-function-in-compute.slang @@ -49,10 +49,10 @@ buffer MyBlockName uniform sampler1D uniform_sampler1D; -__generic -bool textureFuncs(Sampler1D> gsampler1D) +__generic +bool textureFuncs(Sampler1D gsampler1D) { - typealias gvec4 = vector; + typealias gvec4 = vector; constexpr ivec2 ivec2_0 = ivec2(0); @@ -61,32 +61,32 @@ bool textureFuncs(Sampler1D> gsampler1D) && int(0) == textureSize(gsampler1D, int(0)) && vec2(0) == textureQueryLod(gsampler1D, float(0)) && int(0) == textureQueryLevels(gsampler1D) - && gvec4(T(0)) == texture(gsampler1D, float(0)) - && gvec4(T(0)) == texture(gsampler1D, float(0), float(0)) - && gvec4(T(0)) == textureProj(gsampler1D, vec2(0)) - && gvec4(T(0)) == textureProj(gsampler1D, vec2(0), float(0)) - && gvec4(T(0)) == textureProj(gsampler1D, vec4(0)) - && gvec4(T(0)) == textureProj(gsampler1D, vec4(0), float(0)) - && gvec4(T(0)) == textureLod(gsampler1D, float(0), float(0)) - && gvec4(T(0)) == textureOffset(gsampler1D, float(0), __LINE__) - && gvec4(T(0)) == textureOffset(gsampler1D, float(0), __LINE__, float(0)) - && gvec4(T(0)) == texelFetch(gsampler1D, int(0), int(0)) - && gvec4(T(0)) == texelFetchOffset(gsampler1D, int(0), int(0), __LINE__) - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec2(0), __LINE__) - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec2(0), __LINE__, float(0)) - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec4(0), __LINE__) - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec4(0), __LINE__,float(0)) - && gvec4(T(0)) == textureLodOffset(gsampler1D, float(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjLod(gsampler1D, vec2(0), float(0)) - && gvec4(T(0)) == textureProjLod(gsampler1D, vec4(0), float(0)) - && gvec4(T(0)) == textureProjLodOffset(gsampler1D, vec2(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjLodOffset(gsampler1D, vec4(0), float(0), __LINE__) - && gvec4(T(0)) == textureGrad(gsampler1D, float(0), float(0), float(0)) - && gvec4(T(0)) == textureGradOffset(gsampler1D, float(0), float(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjGrad(gsampler1D, vec2(0), float(0), float(0)) - && gvec4(T(0)) == textureProjGrad(gsampler1D, vec4(0), float(0), float(0)) - && gvec4(T(0)) == textureProjGradOffset(gsampler1D, vec2(0), float(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjGradOffset(gsampler1D, vec4(0), float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == texture(gsampler1D, float(0)) + && gvec4(T.Element(0)) == texture(gsampler1D, float(0), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec2(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec2(0), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec4(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler1D, float(0), float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler1D, float(0), __LINE__) + && gvec4(T.Element(0)) == textureOffset(gsampler1D, float(0), __LINE__, float(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler1D, int(0), int(0)) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler1D, int(0), int(0), __LINE__) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec2(0), __LINE__) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec2(0), __LINE__, float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec4(0), __LINE__) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec4(0), __LINE__,float(0)) + && gvec4(T.Element(0)) == textureLodOffset(gsampler1D, float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureProjLod(gsampler1D, vec2(0), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler1D, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler1D, vec2(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler1D, vec4(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureGrad(gsampler1D, float(0), float(0), float(0)) + && gvec4(T.Element(0)) == textureGradOffset(gsampler1D, float(0), float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureProjGrad(gsampler1D, vec2(0), float(0), float(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler1D, vec4(0), float(0), float(0)) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler1D, vec2(0), float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler1D, vec4(0), float(0), float(0), __LINE__) && vec4(0) == texture1D(uniform_sampler1D, float(0)) && vec4(0) == texture1D(uniform_sampler1D, float(0), float(0)) && vec4(0) == texture1DProj(uniform_sampler1D, vec2(0)) diff --git a/tests/glsl-intrinsic/intrinsic-texture.slang b/tests/glsl-intrinsic/intrinsic-texture.slang index c1e73e73d4..7ad9fcf55a 100644 --- a/tests/glsl-intrinsic/intrinsic-texture.slang +++ b/tests/glsl-intrinsic/intrinsic-texture.slang @@ -7,7 +7,7 @@ //TEST:SIMPLE(filecheck=CUDA): -allow-glsl -stage compute -entry computeMain -target cuda -DCUDA //TEST:SIMPLE(filecheck=CUDA): -allow-glsl -stage fragment -entry fragMain -target cuda -DCUDA -//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -output-using-type -xslang -DSPIRV +//DISABLED_TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -allow-glsl -output-using-type -xslang -DSPIRV // TODO: This test revealed a few problems for a path from GLSL to HLSL //T-EST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -compute -entry computeMain -allow-glsl -use-dxil -output-using-type -profile cs_6_6 -xslang -DHLSL @@ -138,32 +138,32 @@ uniform usampler2DArray uniform_usampler2DArray; //TEST_INPUT: TextureSamplerCube(size=4, content = zero, arrayLength = 2):name uniform_usamplerCubeArray uniform usamplerCubeArray uniform_usamplerCubeArray; -//TEST_INPUT: TextureSampler1D(size=4, content = zero):name uniform_usamplerBuffer +//TEST_INPUT: Texture1D(size=4, content = zero):name uniform_usamplerBuffer uniform usamplerBuffer uniform_usamplerBuffer; -//TEST_INPUT: TextureSampler2D(size=4, content = zero):name uniform_usampler2DMS +//TEST_INPUT: Texture2D(size=4, content = zero):name uniform_usampler2DMS uniform usampler2DMS uniform_usampler2DMS; -//TEST_INPUT: TextureSampler2D(size=4, content = zero, arrayLength = 2):name uniform_usampler2DMSArray +//TEST_INPUT: Texture2D(size=4, content = zero, arrayLength = 2):name uniform_usampler2DMSArray uniform usampler2DMSArray uniform_usampler2DMSArray; -__generic -bool textureFuncs( Sampler1D> gsampler1D - , Sampler2D> gsampler2D - , Sampler2DRect> gsampler2DRect - , Sampler3D> gsampler3D - , SamplerCube> gsamplerCube - , Sampler1DArray> gsampler1DArray - , Sampler2DArray> gsampler2DArray - , SamplerCubeArray> gsamplerCubeArray - , SamplerBuffer> gsamplerBuffer - , Sampler2DMS> gsampler2DMS - , Sampler2DMSArray> gsampler2DMSArray +__generic +bool textureFuncs( Sampler1D gsampler1D + , Sampler2D gsampler2D + , Sampler2DRect gsampler2DRect + , Sampler3D gsampler3D + , SamplerCube gsamplerCube + , Sampler1DArray gsampler1DArray + , Sampler2DArray gsampler2DArray + , SamplerCubeArray gsamplerCubeArray + , SamplerBuffer gsamplerBuffer + , Sampler2DMS gsampler2DMS + , Sampler2DMSArray gsampler2DMSArray , bool ignoreResult ) { // GLSL-LABEL: textureFuncs_0 - typealias gvec4 = vector; + typealias gvec4 = vector; constexpr float coord = 0.5; @@ -171,8 +171,8 @@ bool textureFuncs( Sampler1D> gsampler1D constexpr ivec3 offset3D = ivec3(3); constexpr ivec2 offsets[4] = { ivec2(1), ivec2(2), ivec2(3), ivec2(4) }; - bool ignoreResultF32 = ignoreResult && T is float; - bool ignoreResultI32 = ignoreResult && T is int32_t; + bool ignoreResultF32 = ignoreResult && T.Element is float; + bool ignoreResultI32 = ignoreResult && T.Element is int32_t; return true // 8.9.1. Texture Query Functions @@ -444,42 +444,42 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: texture({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]] - && gvec4(T(0)) == texture(gsampler1D, float(coord)) + && gvec4(T.Element(0)) == texture(gsampler1D, float(coord)) // GLSL: texture({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == texture(gsampler1D, float(coord), float(0)) + && gvec4(T.Element(0)) == texture(gsampler1D, float(coord), float(0)) // GLSL: texture({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]] - && gvec4(T(0)) == texture(gsampler2D, vec2(coord)) + && gvec4(T.Element(0)) == texture(gsampler2D, vec2(coord)) // GLSL: texture({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == texture(gsampler2D, vec2(coord), float(0)) + && gvec4(T.Element(0)) == texture(gsampler2D, vec2(coord), float(0)) // GLSL: texture({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]] - && gvec4(T(0)) == texture(gsampler3D, vec3(coord)) + && gvec4(T.Element(0)) == texture(gsampler3D, vec3(coord)) // GLSL: texture({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == texture(gsampler3D, vec3(coord), float(0)) + && gvec4(T.Element(0)) == texture(gsampler3D, vec3(coord), float(0)) // GLSL: texture({{.*}}samplerCube // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCube // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]] - && gvec4(T(0)) == texture(gsamplerCube, vec3(coord)) + && gvec4(T.Element(0)) == texture(gsamplerCube, vec3(coord)) // GLSL: texture({{.*}}samplerCube // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCube // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == texture(gsamplerCube, vec3(coord), float(0)) + && gvec4(T.Element(0)) == texture(gsamplerCube, vec3(coord), float(0)) // GLSL: texture({{.*}}sampler1DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DShadow @@ -514,32 +514,32 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: texture({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]] - && gvec4(T(0)) == texture(gsampler2DArray, vec3(coord)) + && gvec4(T.Element(0)) == texture(gsampler2DArray, vec3(coord)) // GLSL: texture({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == texture(gsampler2DArray, vec3(coord), float(0)) + && gvec4(T.Element(0)) == texture(gsampler2DArray, vec3(coord), float(0)) // GLSL: texture({{.*}}samplerCubeArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCubeArray // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]] - && gvec4(T(0)) == texture(gsamplerCubeArray, vec4(coord)) + && gvec4(T.Element(0)) == texture(gsamplerCubeArray, vec4(coord)) // GLSL: texture({{.*}}samplerCubeArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCubeArray // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == texture(gsamplerCubeArray, vec4(coord), float(0)) + && gvec4(T.Element(0)) == texture(gsamplerCubeArray, vec4(coord), float(0)) // GLSL: texture({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]] - && gvec4(T(0)) == texture(gsampler1DArray, vec2(coord)) + && gvec4(T.Element(0)) == texture(gsampler1DArray, vec2(coord)) // GLSL: texture({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == texture(gsampler1DArray, vec2(coord), float(0)) + && gvec4(T.Element(0)) == texture(gsampler1DArray, vec2(coord), float(0)) // GLSL: texture({{.*}}sampler1DArrayShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArrayShadow @@ -559,7 +559,7 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: texture({{.*}}sampler2DRect // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageSampleImplicitLod{{.*}}[[LOAD]] - && gvec4(T(0)) == texture(gsampler2DRect, vec2(coord)) + && gvec4(T.Element(0)) == texture(gsampler2DRect, vec2(coord)) // GLSL: texture({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -574,52 +574,52 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureProj({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]] - && gvec4(T(0)) == textureProj(gsampler1D, vec2(coord)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec2(coord)) // GLSL: textureProj({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == textureProj(gsampler1D, vec2(coord), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec2(coord), float(0)) // GLSL: textureProj({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]] - && gvec4(T(0)) == textureProj(gsampler1D, vec4(coord)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec4(coord)) // GLSL: textureProj({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == textureProj(gsampler1D, vec4(coord), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec4(coord), float(0)) // GLSL: textureProj({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]] - && gvec4(T(0)) == textureProj(gsampler2D, vec3(coord)) + && gvec4(T.Element(0)) == textureProj(gsampler2D, vec3(coord)) // GLSL: textureProj({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == textureProj(gsampler2D, vec3(coord), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler2D, vec3(coord), float(0)) // GLSL: textureProj({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]] - && gvec4(T(0)) == textureProj(gsampler2D, vec4(coord)) + && gvec4(T.Element(0)) == textureProj(gsampler2D, vec4(coord)) // GLSL: textureProj({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == textureProj(gsampler2D, vec4(coord), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler2D, vec4(coord), float(0)) // GLSL: textureProj({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]] - && gvec4(T(0)) == textureProj(gsampler3D, vec4(coord)) + && gvec4(T.Element(0)) == textureProj(gsampler3D, vec4(coord)) // GLSL: textureProj({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias % - && gvec4(T(0)) == textureProj(gsampler3D, vec4(coord), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler3D, vec4(coord), float(0)) // GLSL: textureProj({{.*}}sampler1DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DShadow @@ -644,8 +644,8 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureProj({{.*}}sampler2DRect // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]] - && gvec4(T(0)) == textureProj(gsampler2DRect, vec3(coord)) - && gvec4(T(0)) == textureProj(gsampler2DRect, vec4(coord)) + && gvec4(T.Element(0)) == textureProj(gsampler2DRect, vec3(coord)) + && gvec4(T.Element(0)) == textureProj(gsampler2DRect, vec4(coord)) // GLSL: textureProj({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -655,22 +655,22 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureLod({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureLod(gsampler1D, float(coord), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler1D, float(coord), float(0)) // GLSL: textureLod({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureLod(gsampler2D, vec2(coord), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler2D, vec2(coord), float(0)) // GLSL: textureLod({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureLod(gsampler3D, vec3(coord), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler3D, vec3(coord), float(0)) // GLSL: textureLod({{.*}}samplerCube // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCube // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureLod(gsamplerCube, vec3(coord), float(0)) + && gvec4(T.Element(0)) == textureLod(gsamplerCube, vec3(coord), float(0)) // GLSL: textureLod({{.*}}sampler2DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DShadow @@ -685,7 +685,7 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureLod({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureLod(gsampler1DArray, vec2(coord), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler1DArray, vec2(coord), float(0)) // GLSL: textureLod({{.*}}sampler1DArrayShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArrayShadow @@ -695,42 +695,42 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureLod({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureLod(gsampler2DArray, vec3(coord), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler2DArray, vec3(coord), float(0)) // GLSL: textureLod({{.*}}samplerCubeArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCubeArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureLod(gsamplerCubeArray, vec4(coord), float(0)) + && gvec4(T.Element(0)) == textureLod(gsamplerCubeArray, vec4(coord), float(0)) // GLSL: textureOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}}ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler1D, float(coord), int(1)) + && gvec4(T.Element(0)) == textureOffset(gsampler1D, float(coord), int(1)) // GLSL: textureOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler1D, float(coord), int(0), float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler1D, float(coord), int(0), float(0)) // GLSL: textureOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}}ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler2D, vec2(coord), offset2D) + && gvec4(T.Element(0)) == textureOffset(gsampler2D, vec2(coord), offset2D) // GLSL: textureOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler2D, vec2(coord), offset2D, float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler2D, vec2(coord), offset2D, float(0)) // GLSL: textureOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}}ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler3D, vec3(coord), offset3D) + && gvec4(T.Element(0)) == textureOffset(gsampler3D, vec3(coord), offset3D) // GLSL: textureOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler3D, vec3(coord), offset3D, float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler3D, vec3(coord), offset3D, float(0)) // GLSL: textureOffset({{.*}}sampler2DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DShadow @@ -745,7 +745,7 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureOffset({{.*}}sampler2DRect // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}}ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler2DRect, vec2(coord), offset2D) + && gvec4(T.Element(0)) == textureOffset(gsampler2DRect, vec2(coord), offset2D) // GLSL: textureOffset({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -765,22 +765,22 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureOffset({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}}ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler1DArray, vec2(coord), int(1)) + && gvec4(T.Element(0)) == textureOffset(gsampler1DArray, vec2(coord), int(1)) // GLSL: textureOffset({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler1DArray, vec2(coord), int(0), float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler1DArray, vec2(coord), int(0), float(0)) // GLSL: textureOffset({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}}ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler2DArray, vec3(coord), offset2D) + && gvec4(T.Element(0)) == textureOffset(gsampler2DArray, vec3(coord), offset2D) // GLSL: textureOffset({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageSampleImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureOffset(gsampler2DArray, vec3(coord), offset2D, float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler2DArray, vec3(coord), offset2D, float(0)) // GLSL: textureOffset({{.*}}sampler1DArrayShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArrayShadow @@ -801,146 +801,146 @@ bool textureFuncs( Sampler1D> gsampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod % - && gvec4(T(0)) == texelFetch(gsampler1D, int(coord), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler1D, int(coord), int(0)) // GLSL: texelFetch({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod % - && gvec4(T(0)) == texelFetch(gsampler2D, ivec2(coord), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2D, ivec2(coord), int(0)) // GLSL: texelFetch({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod % - && gvec4(T(0)) == texelFetch(gsampler3D, ivec3(coord), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler3D, ivec3(coord), int(0)) // GLSL: texelFetch({{.*}}sampler2DRect // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]] - && gvec4(T(0)) == texelFetch(gsampler2DRect, ivec2(coord)) + && gvec4(T.Element(0)) == texelFetch(gsampler2DRect, ivec2(coord)) // GLSL: texelFetch({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod % - && gvec4(T(0)) == texelFetch(gsampler1DArray, ivec2(coord), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler1DArray, ivec2(coord), int(0)) // GLSL: texelFetch({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod % - && gvec4(T(0)) == texelFetch(gsampler2DArray, ivec3(coord), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2DArray, ivec3(coord), int(0)) // GLSL: imageLoad({{.*}}samplerBuffer // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerBuffer // SPIR: OpImageRead {{.*}}[[LOAD]] - && (gvec4(T(0)) == texelFetch(gsamplerBuffer, int(coord)) || ignoreResultF32) + && (gvec4(T.Element(0)) == texelFetch(gsamplerBuffer, int(coord)) || ignoreResultF32) // GLSL: texelFetch({{.*}}sampler2DMS // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DMS // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // S-PIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod % - && gvec4(T(0)) == texelFetch(gsampler2DMS, ivec2(coord), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2DMS, ivec2(coord), int(0)) // GLSL: texelFetch({{.*}}sampler2DMSArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DMSArray // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // S-PIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod % - && gvec4(T(0)) == texelFetch(gsampler2DMSArray, ivec3(coord), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2DMSArray, ivec3(coord), int(0)) // GLSL: texelFetchOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == texelFetchOffset(gsampler1D, int(coord), int(0), int(0)) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler1D, int(coord), int(0), int(0)) // GLSL: texelFetchOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == texelFetchOffset(gsampler2D, ivec2(coord), int(0), offset2D) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2D, ivec2(coord), int(0), offset2D) // GLSL: texelFetchOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == texelFetchOffset(gsampler3D, ivec3(coord), int(0), offset3D) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler3D, ivec3(coord), int(0), offset3D) // GLSL: texelFetchOffset({{.*}}sampler2DRect // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == texelFetchOffset(gsampler2DRect, ivec2(coord), offset2D) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2DRect, ivec2(coord), offset2D) // GLSL: texelFetchOffset({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == texelFetchOffset(gsampler1DArray, ivec2(coord), int(0), int(0)) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler1DArray, ivec2(coord), int(0), int(0)) // GLSL: texelFetchOffset({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: [[IMAGE:%[1-9][0-9]*]] = OpImage{{.*}}[[LOAD]] // SPIR: OpImageFetch {{.*}}[[IMAGE]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == texelFetchOffset(gsampler2DArray, ivec3(coord), int(0), offset2D) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2DArray, ivec3(coord), int(0), offset2D) // GLSL: textureProjOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec2(coord), int(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec2(coord), int(0)) // GLSL: textureProjOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec2(coord), int(0), float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec2(coord), int(0), float(0)) // GLSL: textureProjOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec4(coord), int(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec4(coord), int(0)) // GLSL: textureProjOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec4(coord), int(0), float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec4(coord), int(0), float(0)) // GLSL: textureProjOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler2D, vec3(coord), offset2D) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2D, vec3(coord), offset2D) // GLSL: textureProjOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler2D, vec3(coord), offset2D, float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2D, vec3(coord), offset2D, float(0)) // GLSL: textureProjOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler2D, vec4(coord), offset2D) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2D, vec4(coord), offset2D) // GLSL: textureProjOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler2D, vec4(coord), offset2D, float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2D, vec4(coord), offset2D, float(0)) // GLSL: textureProjOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler3D, vec4(coord), offset3D) + && gvec4(T.Element(0)) == textureProjOffset(gsampler3D, vec4(coord), offset3D) // GLSL: textureProjOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} Bias|ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler3D, vec4(coord), offset3D, float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler3D, vec4(coord), offset3D, float(0)) // GLSL-COUNT-2: textureProjOffset({{.*}}sampler2DRect // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageSampleProjImplicitLod {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureProjOffset(gsampler2DRect, vec3(coord), offset2D) - && gvec4(T(0)) == textureProjOffset(gsampler2DRect, vec4(coord), offset2D) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2DRect, vec3(coord), offset2D) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2DRect, vec4(coord), offset2D) // GLSL: textureProjOffset({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -970,17 +970,17 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureLodOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == textureLodOffset(gsampler1D, float(coord), float(0), 0) + && gvec4(T.Element(0)) == textureLodOffset(gsampler1D, float(coord), float(0), 0) // GLSL: textureLodOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == textureLodOffset(gsampler2D, vec2(coord), float(0), offset2D) + && gvec4(T.Element(0)) == textureLodOffset(gsampler2D, vec2(coord), float(0), offset2D) // GLSL: textureLodOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == textureLodOffset(gsampler3D, vec3(coord), float(0), offset3D) + && gvec4(T.Element(0)) == textureLodOffset(gsampler3D, vec3(coord), float(0), offset3D) // GLSL: textureLodOffset({{.*}}sampler1DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DShadow @@ -995,12 +995,12 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureLodOffset({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == textureLodOffset(gsampler1DArray, vec2(coord), float(0), 0) + && gvec4(T.Element(0)) == textureLodOffset(gsampler1DArray, vec2(coord), float(0), 0) // GLSL: textureLodOffset({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == textureLodOffset(gsampler2DArray, vec3(coord), float(0), offset2D) + && gvec4(T.Element(0)) == textureLodOffset(gsampler2DArray, vec3(coord), float(0), offset2D) // GLSL: textureLodOffset({{.*}}sampler1DArrayShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArrayShadow @@ -1010,19 +1010,19 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureProjLod({{.*}}sampler1D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureProjLod(gsampler1D, vec2(coord), float(0)) - && gvec4(T(0)) == textureProjLod(gsampler1D, vec4(coord), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler1D, vec2(coord), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler1D, vec4(coord), float(0)) // GLSL-COUNT-2: textureProjLod({{.*}}sampler2D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureProjLod(gsampler2D, vec3(coord), float(0)) - && gvec4(T(0)) == textureProjLod(gsampler2D, vec4(coord), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler2D, vec3(coord), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler2D, vec4(coord), float(0)) // GLSL: textureProjLod({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Lod % - && gvec4(T(0)) == textureProjLod(gsampler3D, vec4(coord), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler3D, vec4(coord), float(0)) // GLSL: textureProjLod({{.*}}sampler1DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DShadow @@ -1037,19 +1037,19 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureProjLodOffset({{.*}}sampler1D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == textureProjLodOffset(gsampler1D, vec2(coord), float(0), 0) - && gvec4(T(0)) == textureProjLodOffset(gsampler1D, vec4(coord), float(0), 0) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler1D, vec2(coord), float(0), 0) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler1D, vec4(coord), float(0), 0) // GLSL-COUNT-2: textureProjLodOffset({{.*}}sampler2D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == textureProjLodOffset(gsampler2D, vec3(coord), float(0), offset2D) - && gvec4(T(0)) == textureProjLodOffset(gsampler2D, vec4(coord), float(0), offset2D) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler2D, vec3(coord), float(0), offset2D) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler2D, vec4(coord), float(0), offset2D) // GLSL: textureProjLodOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Lod|ConstOffset % - && gvec4(T(0)) == textureProjLodOffset(gsampler3D, vec4(coord), float(0), offset3D) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler3D, vec4(coord), float(0), offset3D) // GLSL: textureProjLodOffset({{.*}}sampler1DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DShadow @@ -1064,27 +1064,27 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureGrad({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureGrad(gsampler1D, float(coord), float(0), float(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler1D, float(coord), float(0), float(0)) // GLSL: textureGrad({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureGrad(gsampler2D, vec2(coord), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler2D, vec2(coord), vec2(0), vec2(0)) // GLSL: textureGrad({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureGrad(gsampler3D, vec3(coord), vec3(0), vec3(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler3D, vec3(coord), vec3(0), vec3(0)) // GLSL: textureGrad({{.*}}samplerCube // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCube // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureGrad(gsamplerCube, vec3(coord), vec3(0), vec3(0)) + && gvec4(T.Element(0)) == textureGrad(gsamplerCube, vec3(coord), vec3(0), vec3(0)) // GLSL: textureGrad({{.*}}sampler2DRect // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureGrad(gsampler2DRect, vec2(coord), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler2DRect, vec2(coord), vec2(0), vec2(0)) // GLSL: textureGrad({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -1099,12 +1099,12 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureGrad({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureGrad(gsampler1DArray, vec2(coord), float(0), float(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler1DArray, vec2(coord), float(0), float(0)) // GLSL: textureGrad({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureGrad(gsampler2DArray, vec3(coord), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler2DArray, vec3(coord), vec2(0), vec2(0)) // GLSL: textureGrad({{.*}}sampler1DArrayShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArrayShadow @@ -1129,27 +1129,27 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureGrad({{.*}}samplerCubeArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCubeArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureGrad(gsamplerCubeArray, vec4(coord), vec3(0), vec3(0)) + && gvec4(T.Element(0)) == textureGrad(gsamplerCubeArray, vec4(coord), vec3(0), vec3(0)) // GLSL: textureGradOffset({{.*}}sampler1D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureGradOffset(gsampler1D, float(coord), float(0), float(0), 0) + && gvec4(T.Element(0)) == textureGradOffset(gsampler1D, float(coord), float(0), float(0), 0) // GLSL: textureGradOffset({{.*}}sampler2D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureGradOffset(gsampler2D, vec2(coord), vec2(0), vec2(0), offset2D) + && gvec4(T.Element(0)) == textureGradOffset(gsampler2D, vec2(coord), vec2(0), vec2(0), offset2D) // GLSL: textureGradOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureGradOffset(gsampler3D, vec3(coord), vec3(0), vec3(0), offset3D) + && gvec4(T.Element(0)) == textureGradOffset(gsampler3D, vec3(coord), vec3(0), vec3(0), offset3D) // GLSL: textureGradOffset({{.*}}sampler2DRect // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureGradOffset(gsampler2DRect, vec2(coord), vec2(0), vec2(0), offset2D) + && gvec4(T.Element(0)) == textureGradOffset(gsampler2DRect, vec2(coord), vec2(0), vec2(0), offset2D) // GLSL: textureGradOffset({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -1169,12 +1169,12 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL: textureGradOffset({{.*}}sampler2DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureGradOffset(gsampler2DArray, vec3(coord), vec2(0), vec2(0), offset2D) + && gvec4(T.Element(0)) == textureGradOffset(gsampler2DArray, vec3(coord), vec2(0), vec2(0), offset2D) // GLSL: textureGradOffset({{.*}}sampler1DArray // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArray // SPIR: OpImageSampleExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureGradOffset(gsampler1DArray, vec2(coord), float(0), float(0), int(0)) + && gvec4(T.Element(0)) == textureGradOffset(gsampler1DArray, vec2(coord), float(0), float(0), int(0)) // GLSL: textureGradOffset({{.*}}sampler1DArrayShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1DArrayShadow @@ -1189,25 +1189,25 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureProjGrad({{.*}}sampler1D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureProjGrad(gsampler1D, vec2(coord), float(0), float(0)) - && gvec4(T(0)) == textureProjGrad(gsampler1D, vec4(coord), float(0), float(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler1D, vec2(coord), float(0), float(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler1D, vec4(coord), float(0), float(0)) // GLSL-COUNT-2: textureProjGrad({{.*}}sampler2D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureProjGrad(gsampler2D, vec3(coord), vec2(0), vec2(0)) - && gvec4(T(0)) == textureProjGrad(gsampler2D, vec4(coord), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler2D, vec3(coord), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler2D, vec4(coord), vec2(0), vec2(0)) // GLSL: textureProjGrad({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureProjGrad(gsampler3D, vec4(coord), vec3(0), vec3(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler3D, vec4(coord), vec3(0), vec3(0)) // GLSL-COUNT-2: textureProjGrad({{.*}}sampler2DRect // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Grad % - && gvec4(T(0)) == textureProjGrad(gsampler2DRect, vec3(coord), vec2(0), vec2(0)) - && gvec4(T(0)) == textureProjGrad(gsampler2DRect, vec4(coord), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler2DRect, vec3(coord), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler2DRect, vec4(coord), vec2(0), vec2(0)) // GLSL: textureProjGrad({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -1227,25 +1227,25 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureProjGradOffset({{.*}}sampler1D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler1D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureProjGradOffset(gsampler1D, vec2(coord), float(0), float(0), 0) - && gvec4(T(0)) == textureProjGradOffset(gsampler1D, vec4(coord), float(0), float(0), 0) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler1D, vec2(coord), float(0), float(0), 0) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler1D, vec4(coord), float(0), float(0), 0) // GLSL-COUNT-2: textureProjGradOffset({{.*}}sampler2D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureProjGradOffset(gsampler2D, vec3(coord), vec2(0), vec2(0), offset2D) - && gvec4(T(0)) == textureProjGradOffset(gsampler2D, vec4(coord), vec2(0), vec2(0), offset2D) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler2D, vec3(coord), vec2(0), vec2(0), offset2D) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler2D, vec4(coord), vec2(0), vec2(0), offset2D) // GLSL: textureProjGradOffset({{.*}}sampler3D // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler3D // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureProjGradOffset(gsampler3D, vec4(coord), vec3(0), vec3(0), offset3D) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler3D, vec4(coord), vec3(0), vec3(0), offset3D) // GLSL-COUNT-2: textureProjGradOffset({{.*}}sampler2DRect // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageSampleProjExplicitLod {{.*}}[[LOAD]]{{.*}} Grad|ConstOffset % - && gvec4(T(0)) == textureProjGradOffset(gsampler2DRect, vec3(coord), vec2(0), vec2(0), offset2D) - && gvec4(T(0)) == textureProjGradOffset(gsampler2DRect, vec4(coord), vec2(0), vec2(0), offset2D) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler2DRect, vec3(coord), vec2(0), vec2(0), offset2D) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler2DRect, vec4(coord), vec2(0), vec2(0), offset2D) // GLSL: textureProjGradOffset({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -1267,32 +1267,32 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureGather({{.*}}sampler2D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageGather {{.*}}[[LOAD]] - && gvec4(T(0)) == textureGather(gsampler2D, vec2(coord)) - && gvec4(T(0)) == textureGather(gsampler2D, vec2(coord), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2D, vec2(coord)) + && gvec4(T.Element(0)) == textureGather(gsampler2D, vec2(coord), int(0)) // GLSL-COUNT-2: textureGather({{.*}}sampler2DArray // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageGather {{.*}}[[LOAD]] - && gvec4(T(0)) == textureGather(gsampler2DArray, vec3(coord)) - && gvec4(T(0)) == textureGather(gsampler2DArray, vec3(coord), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DArray, vec3(coord)) + && gvec4(T.Element(0)) == textureGather(gsampler2DArray, vec3(coord), int(0)) // GLSL-COUNT-2: textureGather({{.*}}samplerCube // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCube // SPIR: OpImageGather {{.*}}[[LOAD]] - && gvec4(T(0)) == textureGather(gsamplerCube, vec3(coord)) - && gvec4(T(0)) == textureGather(gsamplerCube, vec3(coord), int(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCube, vec3(coord)) + && gvec4(T.Element(0)) == textureGather(gsamplerCube, vec3(coord), int(0)) // GLSL-COUNT-2: textureGather({{.*}}samplerCubeArray // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}samplerCubeArray // SPIR: OpImageGather {{.*}}[[LOAD]] - && gvec4(T(0)) == textureGather(gsamplerCubeArray, vec4(coord)) - && gvec4(T(0)) == textureGather(gsamplerCubeArray, vec4(coord), int(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCubeArray, vec4(coord)) + && gvec4(T.Element(0)) == textureGather(gsamplerCubeArray, vec4(coord), int(0)) // GLSL-COUNT-2: textureGather({{.*}}sampler2DRect // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageGather {{.*}}[[LOAD]] - && gvec4(T(0)) == textureGather(gsampler2DRect, vec2(coord)) - && gvec4(T(0)) == textureGather(gsampler2DRect, vec2(coord), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DRect, vec2(coord)) + && gvec4(T.Element(0)) == textureGather(gsampler2DRect, vec2(coord), int(0)) // GLSL: textureGather({{.*}}sampler2DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DShadow @@ -1322,14 +1322,14 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureGatherOffset({{.*}}sampler2D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageGather {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureGatherOffset(gsampler2D, vec2(coord), offset2D) - && gvec4(T(0)) == textureGatherOffset(gsampler2D, vec2(coord), offset2D, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2D, vec2(coord), offset2D) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2D, vec2(coord), offset2D, int(0)) // GLSL-COUNT-2: textureGatherOffset({{.*}}sampler2DArray // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageGather {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureGatherOffset(gsampler2DArray, vec3(coord), offset2D) - && gvec4(T(0)) == textureGatherOffset(gsampler2DArray, vec3(coord), offset2D, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DArray, vec3(coord), offset2D) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DArray, vec3(coord), offset2D, int(0)) // GLSL: textureGatherOffset({{.*}}sampler2DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DShadow @@ -1344,8 +1344,8 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureGatherOffset({{.*}}sampler2DRect // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageGather {{.*}}[[LOAD]]{{.*}} ConstOffset % - && gvec4(T(0)) == textureGatherOffset(gsampler2DRect, vec2(coord), offset2D) - && gvec4(T(0)) == textureGatherOffset(gsampler2DRect, vec2(coord), offset2D, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DRect, vec2(coord), offset2D) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DRect, vec2(coord), offset2D, int(0)) // GLSL: textureGatherOffset({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow @@ -1355,14 +1355,14 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureGatherOffsets({{.*}}sampler2D // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2D // SPIR: OpImageGather {{.*}}[[LOAD]]{{.*}} ConstOffsets % - && gvec4(T(0)) == textureGatherOffsets(gsampler2D, vec2(coord), offsets) - && gvec4(T(0)) == textureGatherOffsets(gsampler2D, vec2(coord), offsets, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2D, vec2(coord), offsets) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2D, vec2(coord), offsets, int(0)) // GLSL-COUNT-2: textureGatherOffsets({{.*}}sampler2DArray // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DArray // SPIR: OpImageGather {{.*}}[[LOAD]]{{.*}} ConstOffsets % - && gvec4(T(0)) == textureGatherOffsets(gsampler2DArray, vec3(coord), offsets) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DArray, vec3(coord), offsets, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DArray, vec3(coord), offsets) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DArray, vec3(coord), offsets, int(0)) // GLSL: textureGatherOffsets({{.*}}sampler2DShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DShadow @@ -1377,8 +1377,8 @@ bool textureFuncs( Sampler1D> gsampler1D // GLSL-COUNT-2: textureGatherOffsets({{.*}}sampler2DRect // SPIR-COUNT-2: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRect // SPIR: OpImageGather {{.*}}[[LOAD]]{{.*}} ConstOffsets % - && gvec4(T(0)) == textureGatherOffsets(gsampler2DRect, vec2(coord), offsets) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DRect, vec2(coord), offsets, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DRect, vec2(coord), offsets) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DRect, vec2(coord), offsets, int(0)) // GLSL: textureGatherOffsets({{.*}}sampler2DRectShadow // SPIR: [[LOAD:%[1-9][0-9]*]] = OpLoad{{.*}}sampler2DRectShadow diff --git a/tests/language-feature/capability/intrinsic-texture-ignore-capability.slang b/tests/language-feature/capability/intrinsic-texture-ignore-capability.slang index 148eff07a1..dfdbb70832 100644 --- a/tests/language-feature/capability/intrinsic-texture-ignore-capability.slang +++ b/tests/language-feature/capability/intrinsic-texture-ignore-capability.slang @@ -50,19 +50,19 @@ uniform usampler2DArray uniform_usampler2DArray; uniform usamplerCubeArray uniform_usamplerCubeArray; uniform usamplerBuffer uniform_usamplerBuffer; -__generic -bool textureFuncs( Sampler1D> gsampler1D - , Sampler2D> gsampler2D - , Sampler2DRect> gsampler2DRect - , Sampler3D> gsampler3D - , SamplerCube> gsamplerCube - , Sampler1DArray> gsampler1DArray - , Sampler2DArray> gsampler2DArray - , SamplerCubeArray> gsamplerCubeArray - , SamplerBuffer> gsamplerBuffer +__generic +bool textureFuncs( Sampler1D gsampler1D + , Sampler2D gsampler2D + , Sampler2DRect gsampler2DRect + , Sampler3D gsampler3D + , SamplerCube gsamplerCube + , Sampler1DArray gsampler1DArray + , Sampler2DArray gsampler2DArray + , SamplerCubeArray gsamplerCubeArray + , SamplerBuffer gsamplerBuffer ) { - typealias gvec4 = vector; + typealias gvec4 = vector; constexpr ivec2 ivec2_0 = ivec2(0); @@ -111,209 +111,209 @@ bool textureFuncs( Sampler1D> gsampler1D && int(0) == textureQueryLevels(uniform_sampler2DArrayShadow) && int(0) == textureQueryLevels(uniform_samplerCubeArrayShadow) // 8.9.2. Texel Lookup Functions - && gvec4(T(0)) == texture(gsampler1D, float(0)) - && gvec4(T(0)) == texture(gsampler1D, float(0), float(0)) - && gvec4(T(0)) == texture(gsampler2D, vec2(0)) - && gvec4(T(0)) == texture(gsampler2D, vec2(0), float(0)) - && gvec4(T(0)) == texture(gsampler3D, vec3(0)) - && gvec4(T(0)) == texture(gsampler3D, vec3(0), float(0)) - && gvec4(T(0)) == texture(gsamplerCube, vec3(0) ) - && gvec4(T(0)) == texture(gsamplerCube, vec3(0), float(0)) + && gvec4(T.Element(0)) == texture(gsampler1D, float(0)) + && gvec4(T.Element(0)) == texture(gsampler1D, float(0), float(0)) + && gvec4(T.Element(0)) == texture(gsampler2D, vec2(0)) + && gvec4(T.Element(0)) == texture(gsampler2D, vec2(0), float(0)) + && gvec4(T.Element(0)) == texture(gsampler3D, vec3(0)) + && gvec4(T.Element(0)) == texture(gsampler3D, vec3(0), float(0)) + && gvec4(T.Element(0)) == texture(gsamplerCube, vec3(0) ) + && gvec4(T.Element(0)) == texture(gsamplerCube, vec3(0), float(0)) && float(0) == texture(uniform_sampler1DShadow, vec3(0)) && float(0) == texture(uniform_sampler1DShadow, vec3(0), float(0)) && float(0) == texture(uniform_sampler2DShadow, vec3(0)) && float(0) == texture(uniform_sampler2DShadow, vec3(0), float(0)) && float(0) == texture(uniform_samplerCubeShadow, vec4(0)) && float(0) == texture(uniform_samplerCubeShadow, vec4(0), float(0)) - && gvec4(T(0)) == texture(gsampler2DArray, vec3(0)) - && gvec4(T(0)) == texture(gsampler2DArray, vec3(0), float(0)) - && gvec4(T(0)) == texture(gsamplerCubeArray, vec4(0) ) - && gvec4(T(0)) == texture(gsamplerCubeArray, vec4(0), float(0)) - && gvec4(T(0)) == texture(gsampler1DArray, vec2(0)) - && gvec4(T(0)) == texture(gsampler1DArray, vec2(0), float(0)) + && gvec4(T.Element(0)) == texture(gsampler2DArray, vec3(0)) + && gvec4(T.Element(0)) == texture(gsampler2DArray, vec3(0), float(0)) + && gvec4(T.Element(0)) == texture(gsamplerCubeArray, vec4(0) ) + && gvec4(T.Element(0)) == texture(gsamplerCubeArray, vec4(0), float(0)) + && gvec4(T.Element(0)) == texture(gsampler1DArray, vec2(0)) + && gvec4(T.Element(0)) == texture(gsampler1DArray, vec2(0), float(0)) && float(0) == texture(uniform_sampler1DArrayShadow, vec3(0)) && float(0) == texture(uniform_sampler1DArrayShadow, vec3(0), float(0)) && float(0) == texture(uniform_sampler2DArrayShadow, vec4(0)) - && gvec4(T(0)) == texture(gsampler2DRect, vec2(0)) + && gvec4(T.Element(0)) == texture(gsampler2DRect, vec2(0)) && float(0) == texture(uniform_sampler2DRectShadow, vec3(0)) && float(0) == texture(uniform_samplerCubeArrayShadow, vec4(0), float(0)) - && gvec4(T(0)) == textureProj(gsampler1D, vec2(0)) - && gvec4(T(0)) == textureProj(gsampler1D, vec2(0), float(0)) - && gvec4(T(0)) == textureProj(gsampler1D, vec4(0)) - && gvec4(T(0)) == textureProj(gsampler1D, vec4(0), float(0)) - && gvec4(T(0)) == textureProj(gsampler2D, vec3(0)) - && gvec4(T(0)) == textureProj(gsampler2D, vec3(0), float(0)) - && gvec4(T(0)) == textureProj(gsampler2D, vec4(0)) - && gvec4(T(0)) == textureProj(gsampler2D, vec4(0), float(0)) - && gvec4(T(0)) == textureProj(gsampler3D, vec4(0)) - && gvec4(T(0)) == textureProj(gsampler3D, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec2(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec2(0), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec4(0)) + && gvec4(T.Element(0)) == textureProj(gsampler1D, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler2D, vec3(0)) + && gvec4(T.Element(0)) == textureProj(gsampler2D, vec3(0), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler2D, vec4(0)) + && gvec4(T.Element(0)) == textureProj(gsampler2D, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureProj(gsampler3D, vec4(0)) + && gvec4(T.Element(0)) == textureProj(gsampler3D, vec4(0), float(0)) && float(0) == textureProj(uniform_sampler1DShadow, vec4(0)) && float(0) == textureProj(uniform_sampler1DShadow, vec4(0), float(0)) && float(0) == textureProj(uniform_sampler2DShadow, vec4(0)) && float(0) == textureProj(uniform_sampler2DShadow, vec4(0), float(0)) - && gvec4(T(0)) == textureProj(gsampler2DRect, vec3(0)) - && gvec4(T(0)) == textureProj(gsampler2DRect, vec4(0)) + && gvec4(T.Element(0)) == textureProj(gsampler2DRect, vec3(0)) + && gvec4(T.Element(0)) == textureProj(gsampler2DRect, vec4(0)) && float(0) == textureProj(uniform_sampler2DRectShadow, vec4(0)) - && gvec4(T(0)) == textureLod(gsampler1D, float(0), float(0)) - && gvec4(T(0)) == textureLod(gsampler2D, vec2(0), float(0)) - && gvec4(T(0)) == textureLod(gsampler3D, vec3(0), float(0)) - && gvec4(T(0)) == textureLod(gsamplerCube, vec3(0), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler1D, float(0), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler2D, vec2(0), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler3D, vec3(0), float(0)) + && gvec4(T.Element(0)) == textureLod(gsamplerCube, vec3(0), float(0)) && float(0) == textureLod(uniform_sampler2DShadow, vec3(0), float(0)) && float(0) == textureLod(uniform_sampler1DShadow, vec3(0), float(0)) - && gvec4(T(0)) == textureLod(gsampler1DArray, vec2(0), float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler1DArray, vec2(0), float(0)) && float(0) == textureLod(uniform_sampler1DArrayShadow, vec3(0), float(0)) - && gvec4(T(0)) == textureLod(gsampler2DArray, vec3(0), float(0)) - && gvec4(T(0)) == textureLod(gsamplerCubeArray, vec4(0), float(0)) - && gvec4(T(0)) == textureOffset(gsampler1D, float(0), __LINE__) - && gvec4(T(0)) == textureOffset(gsampler1D, float(0), __LINE__, float(0)) - && gvec4(T(0)) == textureOffset(gsampler2D, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureOffset(gsampler2D, vec2(0), { __LINE__ }, float(0)) - && gvec4(T(0)) == textureOffset(gsampler3D, vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureOffset(gsampler3D, vec3(0), { __LINE__ }, float(0)) + && gvec4(T.Element(0)) == textureLod(gsampler2DArray, vec3(0), float(0)) + && gvec4(T.Element(0)) == textureLod(gsamplerCubeArray, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler1D, float(0), __LINE__) + && gvec4(T.Element(0)) == textureOffset(gsampler1D, float(0), __LINE__, float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler2D, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureOffset(gsampler2D, vec2(0), { __LINE__ }, float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler3D, vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureOffset(gsampler3D, vec3(0), { __LINE__ }, float(0)) && float(0) == textureOffset(uniform_sampler2DShadow, vec3(0), { __LINE__ }) && float(0) == textureOffset(uniform_sampler2DShadow, vec3(0), { __LINE__ }, float(0)) - && gvec4(T(0)) == textureOffset(gsampler2DRect, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureOffset(gsampler2DRect, vec2(0), { __LINE__ }) && float(0) == textureOffset(uniform_sampler2DRectShadow, vec3(0), { __LINE__ }) && float(0) == textureOffset(uniform_sampler1DShadow, vec3(0), __LINE__) && float(0) == textureOffset(uniform_sampler1DShadow, vec3(0), __LINE__, float(0)) - && gvec4(T(0)) == textureOffset(gsampler1DArray, vec2(0), __LINE__) - && gvec4(T(0)) == textureOffset(gsampler1DArray, vec2(0), __LINE__, float(0)) - && gvec4(T(0)) == textureOffset(gsampler2DArray, vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureOffset(gsampler2DArray, vec3(0), { __LINE__ }, float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler1DArray, vec2(0), __LINE__) + && gvec4(T.Element(0)) == textureOffset(gsampler1DArray, vec2(0), __LINE__, float(0)) + && gvec4(T.Element(0)) == textureOffset(gsampler2DArray, vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureOffset(gsampler2DArray, vec3(0), { __LINE__ }, float(0)) && float(0) == textureOffset(uniform_sampler1DArrayShadow, vec3(0), __LINE__) && float(0) == textureOffset(uniform_sampler1DArrayShadow, vec3(0), __LINE__, float(0)) && float(0) == textureOffset(uniform_sampler2DArrayShadow, vec4(0), { __LINE__ }) - && gvec4(T(0)) == texelFetch(gsampler1D, int(0), int(0)) - && gvec4(T(0)) == texelFetch(gsampler2D, ivec2(0), int(0)) - && gvec4(T(0)) == texelFetch(gsampler3D, ivec3(0), int(0)) - && gvec4(T(0)) == texelFetch(gsampler2DRect, ivec2(0)) - && gvec4(T(0)) == texelFetch(gsampler1DArray, ivec2(0), int(0)) - && gvec4(T(0)) == texelFetch(gsampler2DArray, ivec3(0), int(0)) - && gvec4(T(0)) == texelFetch(gsamplerBuffer, int(0)) - && gvec4(T(0)) == texelFetchOffset(gsampler1D, int(0), int(0), __LINE__) - && gvec4(T(0)) == texelFetchOffset(gsampler2D, ivec2(0), int(0), { __LINE__ }) - && gvec4(T(0)) == texelFetchOffset(gsampler3D, ivec3(0), int(0), { __LINE__ }) - && gvec4(T(0)) == texelFetchOffset(gsampler2DRect, ivec2(0), { __LINE__ }) - && gvec4(T(0)) == texelFetchOffset(gsampler1DArray, ivec2(0), int(0), __LINE__) - && gvec4(T(0)) == texelFetchOffset(gsampler2DArray, ivec3(0), int(0), { __LINE__ }) - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec2(0), __LINE__) - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec2(0), __LINE__, float(0)) - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec4(0), __LINE__) - && gvec4(T(0)) == textureProjOffset(gsampler1D, vec4(0), __LINE__,float(0)) - && gvec4(T(0)) == textureProjOffset(gsampler2D, vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureProjOffset(gsampler2D, vec3(0), { __LINE__ }, float(0)) - && gvec4(T(0)) == textureProjOffset(gsampler2D, vec4(0), { __LINE__ }) - && gvec4(T(0)) == textureProjOffset(gsampler2D, vec4(0), { __LINE__ }, float(0)) - && gvec4(T(0)) == textureProjOffset(gsampler3D, vec4(0), { __LINE__ }) - && gvec4(T(0)) == textureProjOffset(gsampler3D, vec4(0), { __LINE__ }, float(0)) - && gvec4(T(0)) == textureProjOffset(gsampler2DRect, vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureProjOffset(gsampler2DRect, vec4(0), { __LINE__ }) + && gvec4(T.Element(0)) == texelFetch(gsampler1D, int(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2D, ivec2(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler3D, ivec3(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2DRect, ivec2(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler1DArray, ivec2(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2DArray, ivec3(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsamplerBuffer, int(0)) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler1D, int(0), int(0), __LINE__) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2D, ivec2(0), int(0), { __LINE__ }) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler3D, ivec3(0), int(0), { __LINE__ }) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2DRect, ivec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler1DArray, ivec2(0), int(0), __LINE__) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2DArray, ivec3(0), int(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec2(0), __LINE__) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec2(0), __LINE__, float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec4(0), __LINE__) + && gvec4(T.Element(0)) == textureProjOffset(gsampler1D, vec4(0), __LINE__,float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2D, vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2D, vec3(0), { __LINE__ }, float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2D, vec4(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2D, vec4(0), { __LINE__ }, float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler3D, vec4(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjOffset(gsampler3D, vec4(0), { __LINE__ }, float(0)) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2DRect, vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjOffset(gsampler2DRect, vec4(0), { __LINE__ }) && float(0) == textureProjOffset(uniform_sampler2DRectShadow, vec4(0), { __LINE__ }) && float(0) == textureProjOffset(uniform_sampler1DShadow, vec4(0), __LINE__) && float(0) == textureProjOffset(uniform_sampler1DShadow, vec4(0), __LINE__, float(0)) && float(0) == textureProjOffset(uniform_sampler2DShadow, vec4(0), { __LINE__ }) && float(0) == textureProjOffset(uniform_sampler2DShadow, vec4(0), { __LINE__ }, float(0)) - && gvec4(T(0)) == textureLodOffset(gsampler1D, float(0), float(0), __LINE__) - && gvec4(T(0)) == textureLodOffset(gsampler2D, vec2(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureLodOffset(gsampler3D, vec3(0), float(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureLodOffset(gsampler1D, float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureLodOffset(gsampler2D, vec2(0), float(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureLodOffset(gsampler3D, vec3(0), float(0), { __LINE__ }) && float(0) == textureLodOffset(uniform_sampler1DShadow, vec3(0), float(0), __LINE__) && float(1) == textureLodOffset(uniform_sampler2DShadow, vec3(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureLodOffset(gsampler1DArray, vec2(0), float(0), __LINE__) - && gvec4(T(0)) == textureLodOffset(gsampler2DArray, vec3(0), float(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureLodOffset(gsampler1DArray, vec2(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureLodOffset(gsampler2DArray, vec3(0), float(0), { __LINE__ }) && float(0) == textureLodOffset(uniform_sampler1DArrayShadow, vec3(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjLod(gsampler1D, vec2(0), float(0)) - && gvec4(T(0)) == textureProjLod(gsampler1D, vec4(0), float(0)) - && gvec4(T(0)) == textureProjLod(gsampler2D, vec3(0), float(0)) - && gvec4(T(0)) == textureProjLod(gsampler2D, vec4(0), float(0)) - && gvec4(T(0)) == textureProjLod(gsampler3D, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler1D, vec2(0), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler1D, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler2D, vec3(0), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler2D, vec4(0), float(0)) + && gvec4(T.Element(0)) == textureProjLod(gsampler3D, vec4(0), float(0)) && float(0) == textureProjLod(uniform_sampler1DShadow, vec4(0), float(0)) && float(0) == textureProjLod(uniform_sampler2DShadow, vec4(0), float(0)) - && gvec4(T(0)) == textureProjLodOffset(gsampler1D, vec2(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjLodOffset(gsampler1D, vec4(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjLodOffset(gsampler2D, vec3(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureProjLodOffset(gsampler2D, vec4(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureProjLodOffset(gsampler3D, vec4(0), float(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler1D, vec2(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler1D, vec4(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler2D, vec3(0), float(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler2D, vec4(0), float(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjLodOffset(gsampler3D, vec4(0), float(0), { __LINE__ }) && float(0) == textureProjLodOffset(uniform_sampler1DShadow, vec4(0), float(0), __LINE__) && float(0) == textureProjLodOffset(uniform_sampler2DShadow, vec4(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureGrad(gsampler1D, float(0), float(0), float(0)) - && gvec4(T(0)) == textureGrad(gsampler2D, vec2(0), vec2(0), vec2(0)) - && gvec4(T(0)) == textureGrad(gsampler3D, vec3(0), vec3(0), vec3(0)) - && gvec4(T(0)) == textureGrad(gsamplerCube, vec3(0), vec3(0), vec3(0)) - && gvec4(T(0)) == textureGrad(gsampler2DRect, vec2(0), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler1D, float(0), float(0), float(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler2D, vec2(0), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler3D, vec3(0), vec3(0), vec3(0)) + && gvec4(T.Element(0)) == textureGrad(gsamplerCube, vec3(0), vec3(0), vec3(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler2DRect, vec2(0), vec2(0), vec2(0)) && float(0) == textureGrad(uniform_sampler2DRectShadow, vec3(0), vec2(0), vec2(0)) && float(0) == textureGrad(uniform_sampler1DShadow, vec3(0), float(0), float(0)) - && gvec4(T(0)) == textureGrad(gsampler1DArray, vec2(0), float(0), float(0)) - && gvec4(T(0)) == textureGrad(gsampler2DArray, vec3(0), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler1DArray, vec2(0), float(0), float(0)) + && gvec4(T.Element(0)) == textureGrad(gsampler2DArray, vec3(0), vec2(0), vec2(0)) && float(0) == textureGrad(uniform_sampler1DArrayShadow, vec3(0), float(0), float(0)) && float(0) == textureGrad(uniform_sampler2DShadow, vec3(0), vec2(0), vec2(0)) && float(0) == textureGrad(uniform_samplerCubeShadow, vec4(0), vec3(0), vec3(0)) && float(0) == textureGrad(uniform_sampler2DArrayShadow, vec4(0), vec2(0), vec2(0)) - && gvec4(T(0)) == textureGrad(gsamplerCubeArray, vec4(0), vec3(0), vec3(0)) - && gvec4(T(0)) == textureGradOffset(gsampler1D, float(0), float(0), float(0), __LINE__) - && gvec4(T(0)) == textureGradOffset(gsampler2D, vec2(0), vec2(0), vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGradOffset(gsampler3D, vec3(0), vec3(0), vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureGradOffset(gsampler2DRect, vec2(0), vec2(0), vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGrad(gsamplerCubeArray, vec4(0), vec3(0), vec3(0)) + && gvec4(T.Element(0)) == textureGradOffset(gsampler1D, float(0), float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureGradOffset(gsampler2D, vec2(0), vec2(0), vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGradOffset(gsampler3D, vec3(0), vec3(0), vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGradOffset(gsampler2DRect, vec2(0), vec2(0), vec2(0), { __LINE__ }) && float(0) == textureGradOffset(uniform_sampler2DRectShadow, vec3(0), vec2(0), vec2(0), { __LINE__ }) && float(0) == textureGradOffset(uniform_sampler1DShadow, vec3(0), float(0), float(0), __LINE__) && float(0) == textureGradOffset(uniform_sampler2DShadow, vec3(0), vec2(0), vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGradOffset(gsampler2DArray, vec3(0), vec2(0), vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGradOffset(gsampler1DArray, vec2(0), float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureGradOffset(gsampler2DArray, vec3(0), vec2(0), vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGradOffset(gsampler1DArray, vec2(0), float(0), float(0), __LINE__) && float(0) == textureGradOffset(uniform_sampler1DArrayShadow, vec3(0), float(0), float(0), __LINE__) && float(0) == textureGradOffset(uniform_sampler2DArrayShadow, vec4(0), vec2(0), vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureProjGrad(gsampler1D, vec2(0), float(0), float(0)) - && gvec4(T(0)) == textureProjGrad(gsampler1D, vec4(0), float(0), float(0)) - && gvec4(T(0)) == textureProjGrad(gsampler2D, vec3(0), vec2(0), vec2(0)) - && gvec4(T(0)) == textureProjGrad(gsampler2D, vec4(0), vec2(0), vec2(0)) - && gvec4(T(0)) == textureProjGrad(gsampler3D, vec4(0), vec3(0), vec3(0)) - && gvec4(T(0)) == textureProjGrad(gsampler2DRect, vec3(0), vec2(0), vec2(0)) - && gvec4(T(0)) == textureProjGrad(gsampler2DRect, vec4(0), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler1D, vec2(0), float(0), float(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler1D, vec4(0), float(0), float(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler2D, vec3(0), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler2D, vec4(0), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler3D, vec4(0), vec3(0), vec3(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler2DRect, vec3(0), vec2(0), vec2(0)) + && gvec4(T.Element(0)) == textureProjGrad(gsampler2DRect, vec4(0), vec2(0), vec2(0)) && float(0) == textureProjGrad(uniform_sampler2DRectShadow, vec4(0), vec2(0), vec2(0)) && float(0) == textureProjGrad(uniform_sampler1DShadow, vec4(0), float(0), float(0)) && float(0) == textureProjGrad(uniform_sampler2DShadow, vec4(0), vec2(0), vec2(0)) - && gvec4(T(0)) == textureProjGradOffset(gsampler1D, vec2(0), float(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjGradOffset(gsampler1D, vec4(0), float(0), float(0), __LINE__) - && gvec4(T(0)) == textureProjGradOffset(gsampler2D, vec3(0), vec2(0), vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureProjGradOffset(gsampler2D, vec4(0), vec2(0), vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureProjGradOffset(gsampler3D, vec4(0), vec3(0), vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureProjGradOffset(gsampler2DRect, vec3(0), vec2(0), vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureProjGradOffset(gsampler2DRect, vec4(0), vec2(0), vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler1D, vec2(0), float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler1D, vec4(0), float(0), float(0), __LINE__) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler2D, vec3(0), vec2(0), vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler2D, vec4(0), vec2(0), vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler3D, vec4(0), vec3(0), vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler2DRect, vec3(0), vec2(0), vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureProjGradOffset(gsampler2DRect, vec4(0), vec2(0), vec2(0), { __LINE__ }) && float(0) == textureProjGradOffset(uniform_sampler2DRectShadow, vec4(0), vec2(0), vec2(0), { __LINE__ }) && float(0) == textureProjGradOffset(uniform_sampler1DShadow, vec4(0), float(0), float(0), __LINE__) && float(0) == textureProjGradOffset(uniform_sampler2DShadow, vec4(0), vec2(0), vec2(0), { __LINE__ }) // 8.9.4. Texture Gather Functions - && gvec4(T(0)) == textureGather(gsampler2D, vec2(0)) - && gvec4(T(0)) == textureGather(gsampler2D, vec2(0), int(0)) - && gvec4(T(0)) == textureGather(gsampler2DArray, vec3(0)) - && gvec4(T(0)) == textureGather(gsampler2DArray, vec3(0), int(0)) - && gvec4(T(0)) == textureGather(gsamplerCube, vec3(0)) - && gvec4(T(0)) == textureGather(gsamplerCube, vec3(0), int(0)) - && gvec4(T(0)) == textureGather(gsamplerCubeArray, vec4(0)) - && gvec4(T(0)) == textureGather(gsamplerCubeArray, vec4(0), int(0)) - && gvec4(T(0)) == textureGather(gsampler2DRect, vec2(0)) - && gvec4(T(0)) == textureGather(gsampler2DRect, vec2(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2D, vec2(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2D, vec2(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DArray, vec3(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DArray, vec3(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCube, vec3(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCube, vec3(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCubeArray, vec4(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCubeArray, vec4(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DRect, vec2(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DRect, vec2(0), int(0)) && vec4(0) == textureGather(uniform_sampler2DShadow, vec2(0), float(0)) && vec4(0) == textureGather(uniform_sampler2DArrayShadow, vec3(0), float(0)) && vec4(0) == textureGather(uniform_samplerCubeShadow, vec3(0), float(0)) && vec4(0) == textureGather(uniform_samplerCubeArrayShadow, vec4(0), float(0)) && vec4(0) == textureGather(uniform_sampler2DRectShadow, vec2(0), float(0)) - && gvec4(T(0)) == textureGatherOffset(gsampler2D, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffset(gsampler2D, vec2(0), { __LINE__ }, int(0)) - && gvec4(T(0)) == textureGatherOffset(gsampler2DArray, vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffset(gsampler2DArray, vec3(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2D, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2D, vec2(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DArray, vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DArray, vec3(0), { __LINE__ }, int(0)) && vec4(0) == textureGatherOffset(uniform_sampler2DShadow, vec2(0), float(0), { __LINE__ }) && vec4(0) == textureGatherOffset(uniform_sampler2DArrayShadow, vec3(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffset(gsampler2DRect, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffset(gsampler2DRect, vec2(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DRect, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DRect, vec2(0), { __LINE__ }, int(0)) && vec4(0) == textureGatherOffset(uniform_sampler2DRectShadow, vec2(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2D, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2D, vec2(0), { __LINE__ }, int(0)) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DArray, vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DArray, vec3(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2D, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2D, vec2(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DArray, vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DArray, vec3(0), { __LINE__ }, int(0)) && vec4(0) == textureGatherOffsets(uniform_sampler2DShadow, vec2(0), float(0), { __LINE__ }) && vec4(0) == textureGatherOffsets(uniform_sampler2DArrayShadow, vec3(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DRect, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DRect, vec2(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DRect, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DRect, vec2(0), { __LINE__ }, int(0)) && vec4(0) == textureGatherOffsets(uniform_sampler2DRectShadow, vec2(0), float(0), { __LINE__ }) // 8.9.5. Compatibility Profile Texture Functions @@ -359,19 +359,19 @@ bool textureFuncs( Sampler1D> gsampler1D ; } -__generic -bool itextureFuncs(Sampler1D> gsampler1D - , Sampler2D> gsampler2D - , Sampler2DRect> gsampler2DRect - , Sampler3D> gsampler3D - , SamplerCube> gsamplerCube - , Sampler1DArray> gsampler1DArray - , Sampler2DArray> gsampler2DArray - , SamplerCubeArray> gsamplerCubeArray - , SamplerBuffer> gsamplerBuffer +__generic +bool itextureFuncs(Sampler1D gsampler1D + , Sampler2D gsampler2D + , Sampler2DRect gsampler2DRect + , Sampler3D gsampler3D + , SamplerCube gsamplerCube + , Sampler1DArray gsampler1DArray + , Sampler2DArray gsampler2DArray + , SamplerCubeArray gsamplerCubeArray + , SamplerBuffer gsamplerBuffer ) { - typealias gvec4 = vector; + typealias gvec4 = vector; constexpr ivec2 ivec2_0 = ivec2(0); @@ -394,53 +394,53 @@ bool itextureFuncs(Sampler1D> gsampler1D && ivec3(0) == textureSize(uniform_sampler2DArrayShadow, int(0)) && int(0) == textureSize(gsamplerBuffer) - && gvec4(T(0)) == texelFetch(gsampler1D, int(0), int(0)) - && gvec4(T(0)) == texelFetch(gsampler2D, ivec2(0), int(0)) - && gvec4(T(0)) == texelFetch(gsampler3D, ivec3(0), int(0)) - && gvec4(T(0)) == texelFetch(gsampler2DRect, ivec2(0)) - && gvec4(T(0)) == texelFetch(gsampler1DArray, ivec2(0), int(0)) - && gvec4(T(0)) == texelFetch(gsampler2DArray, ivec3(0), int(0)) - && gvec4(T(0)) == texelFetch(gsamplerBuffer, int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler1D, int(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2D, ivec2(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler3D, ivec3(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2DRect, ivec2(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler1DArray, ivec2(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsampler2DArray, ivec3(0), int(0)) + && gvec4(T.Element(0)) == texelFetch(gsamplerBuffer, int(0)) - && gvec4(T(0)) == texelFetchOffset(gsampler1D, int(0), int(0), __LINE__) - && gvec4(T(0)) == texelFetchOffset(gsampler2D, ivec2(0), int(0), { __LINE__ }) - && gvec4(T(0)) == texelFetchOffset(gsampler3D, ivec3(0), int(0), { __LINE__ }) - && gvec4(T(0)) == texelFetchOffset(gsampler2DRect, ivec2(0), { __LINE__ }) - && gvec4(T(0)) == texelFetchOffset(gsampler1DArray, ivec2(0), int(0), __LINE__) - && gvec4(T(0)) == texelFetchOffset(gsampler2DArray, ivec3(0), int(0), { __LINE__ }) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler1D, int(0), int(0), __LINE__) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2D, ivec2(0), int(0), { __LINE__ }) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler3D, ivec3(0), int(0), { __LINE__ }) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2DRect, ivec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler1DArray, ivec2(0), int(0), __LINE__) + && gvec4(T.Element(0)) == texelFetchOffset(gsampler2DArray, ivec3(0), int(0), { __LINE__ }) // 8.9.4. Texture Gather Functions - && gvec4(T(0)) == textureGather(gsampler2D, vec2(0)) - && gvec4(T(0)) == textureGather(gsampler2D, vec2(0), int(0)) - && gvec4(T(0)) == textureGather(gsampler2DArray, vec3(0)) - && gvec4(T(0)) == textureGather(gsampler2DArray, vec3(0), int(0)) - && gvec4(T(0)) == textureGather(gsamplerCube, vec3(0)) - && gvec4(T(0)) == textureGather(gsamplerCube, vec3(0), int(0)) - && gvec4(T(0)) == textureGather(gsamplerCubeArray, vec4(0)) - && gvec4(T(0)) == textureGather(gsamplerCubeArray, vec4(0), int(0)) - && gvec4(T(0)) == textureGather(gsampler2DRect, vec2(0)) - && gvec4(T(0)) == textureGather(gsampler2DRect, vec2(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2D, vec2(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2D, vec2(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DArray, vec3(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DArray, vec3(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCube, vec3(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCube, vec3(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCubeArray, vec4(0)) + && gvec4(T.Element(0)) == textureGather(gsamplerCubeArray, vec4(0), int(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DRect, vec2(0)) + && gvec4(T.Element(0)) == textureGather(gsampler2DRect, vec2(0), int(0)) && vec4(0) == textureGather(uniform_sampler2DShadow, vec2(0), float(0)) && vec4(0) == textureGather(uniform_sampler2DArrayShadow, vec3(0), float(0)) && vec4(0) == textureGather(uniform_samplerCubeShadow, vec3(0), float(0)) && vec4(0) == textureGather(uniform_samplerCubeArrayShadow, vec4(0), float(0)) && vec4(0) == textureGather(uniform_sampler2DRectShadow, vec2(0), float(0)) - && gvec4(T(0)) == textureGatherOffset(gsampler2D, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffset(gsampler2D, vec2(0), { __LINE__ }, int(0)) - && gvec4(T(0)) == textureGatherOffset(gsampler2DArray, vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffset(gsampler2DArray, vec3(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2D, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2D, vec2(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DArray, vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DArray, vec3(0), { __LINE__ }, int(0)) && vec4(0) == textureGatherOffset(uniform_sampler2DShadow, vec2(0), float(0), { __LINE__ }) && vec4(0) == textureGatherOffset(uniform_sampler2DArrayShadow, vec3(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffset(gsampler2DRect, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffset(gsampler2DRect, vec2(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DRect, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffset(gsampler2DRect, vec2(0), { __LINE__ }, int(0)) && vec4(0) == textureGatherOffset(uniform_sampler2DRectShadow, vec2(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2D, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2D, vec2(0), { __LINE__ }, int(0)) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DArray, vec3(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DArray, vec3(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2D, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2D, vec2(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DArray, vec3(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DArray, vec3(0), { __LINE__ }, int(0)) && vec4(0) == textureGatherOffsets(uniform_sampler2DShadow, vec2(0), float(0), { __LINE__ }) && vec4(0) == textureGatherOffsets(uniform_sampler2DArrayShadow, vec3(0), float(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DRect, vec2(0), { __LINE__ }) - && gvec4(T(0)) == textureGatherOffsets(gsampler2DRect, vec2(0), { __LINE__ }, int(0)) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DRect, vec2(0), { __LINE__ }) + && gvec4(T.Element(0)) == textureGatherOffsets(gsampler2DRect, vec2(0), { __LINE__ }, int(0)) && vec4(0) == textureGatherOffsets(uniform_sampler2DRectShadow, vec2(0), float(0), { __LINE__ }) ; } diff --git a/tests/metal/texture-sampler-less.slang b/tests/metal/texture-sampler-less.slang index a2631e90e6..6a7646e9a8 100644 --- a/tests/metal/texture-sampler-less.slang +++ b/tests/metal/texture-sampler-less.slang @@ -29,7 +29,7 @@ Sampler2DArray t2DArray; SamplerCubeArray tCubeArray; // Metal doc says "For depth texture types, T must be float." -__generic +__generic typealias depth2d = _Texture< T, __Shape2D, @@ -42,7 +42,7 @@ typealias depth2d = _Texture< format >; -__generic +__generic typealias depth2d_array = _Texture< T, __Shape2D, @@ -55,7 +55,7 @@ typealias depth2d_array = _Texture< format >; -__generic +__generic typealias depthcube = _Texture< T, __ShapeCube, @@ -68,7 +68,7 @@ typealias depthcube = _Texture< format >; -__generic +__generic typealias depthcube_array = _Texture< T, __ShapeCube, diff --git a/tests/metal/texture.slang b/tests/metal/texture.slang index 816c168ff6..12274edef5 100644 --- a/tests/metal/texture.slang +++ b/tests/metal/texture.slang @@ -121,7 +121,7 @@ Texture2DArray t2DArray_u16; TextureCubeArray tCubeArray_u16; // Metal doc says "For depth texture types, T must be float." -__generic +__generic typealias depth2d = _Texture< T, __Shape2D, @@ -134,7 +134,7 @@ typealias depth2d = _Texture< format >; -__generic +__generic typealias depth2d_array = _Texture< T, __Shape2D, @@ -147,7 +147,7 @@ typealias depth2d_array = _Texture< format >; -__generic +__generic typealias depthcube = _Texture< T, __ShapeCube, @@ -160,7 +160,7 @@ typealias depthcube = _Texture< format >; -__generic +__generic typealias depthcube_array = _Texture< T, __ShapeCube, @@ -188,20 +188,19 @@ SamplerState samplerState; SamplerComparisonState shadowSampler; -__generic -bool TEST_texture( - Texture1D> t1D, - Texture2D> t2D, - Texture3D> t3D, - TextureCube> tCube, - Texture1DArray> t1DArray, - Texture2DArray> t2DArray, - TextureCubeArray> tCubeArray -) +bool TEST_texture( + Texture1D t1D, + Texture2D t2D, + Texture3D t3D, + TextureCube tCube, + Texture1DArray t1DArray, + Texture2DArray t2DArray, + TextureCubeArray tCubeArray +) where T: ITexelElement, IArithmetic { // METAL-LABEL: TEST_texture - typealias Tvn = vector; - typealias Tv4 = vector; + typealias Tvn = T; + typealias Tv4 = vector; float u = 0; float u2 = 0.5; @@ -421,31 +420,31 @@ bool TEST_texture( // METAL: t1D{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_1d.v4f32( - && all(Tvn(T(0)) == t1D.Sample(samplerState, u)) + && all(Tvn(T.Element(0)) == t1D.Sample(samplerState, u)) // METAL: t2D{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_2d.v4f32( - && all(Tvn(T(0)) == t2D.Sample(samplerState, float2(u, u))) + && all(Tvn(T.Element(0)) == t2D.Sample(samplerState, float2(u, u))) // METAL: t3D{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_3d.v4f32( - && all(Tvn(T(0)) == t3D.Sample(samplerState, float3(u, u, u))) + && all(Tvn(T.Element(0)) == t3D.Sample(samplerState, float3(u, u, u))) // METAL: tCube{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_cube.v4f32( - && all(Tvn(T(0)) == tCube.Sample(samplerState, normalize(float3(u, 1 - u, u)))) + && all(Tvn(T.Element(0)) == tCube.Sample(samplerState, normalize(float3(u, 1 - u, u)))) // METAL: t1DArray{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_1d_array.v4f32( - && all(Tvn(T(0)) == t1DArray.Sample(samplerState, float2(u, 0))) + && all(Tvn(T.Element(0)) == t1DArray.Sample(samplerState, float2(u, 0))) // METAL: t2DArray{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_2d_array.v4f32( - && all(Tvn(T(0)) == t2DArray.Sample(samplerState, float3(u, u, 0))) + && all(Tvn(T.Element(0)) == t2DArray.Sample(samplerState, float3(u, u, 0))) // METAL: tCubeArray{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_cube_array.v4f32( - && all(Tvn(T(0)) == tCubeArray.Sample(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tvn(T.Element(0)) == tCubeArray.Sample(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) // Offset variant @@ -453,15 +452,15 @@ bool TEST_texture( // METAL: t2D{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_2d.v4f32( - && all(Tvn(T(0)) == t2D.Sample(samplerState, float2(u, u), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.Sample(samplerState, float2(u, u), int2(1, 1))) // METAL: t3D{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_3d.v4f32( - && all(Tvn(T(0)) == t3D.Sample(samplerState, float3(u, u, u), int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.Sample(samplerState, float3(u, u, u), int3(1, 1, 1))) // METAL: t2DArray{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_2d_array.v4f32( - && all(Tvn(T(0)) == t2DArray.Sample(samplerState, float3(u, u, 0), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.Sample(samplerState, float3(u, u, 0), int2(1, 1))) // Clamp variant @@ -469,15 +468,15 @@ bool TEST_texture( // METAL: t2D{{.*}}.sample({{.*}} min_lod_clamp( // METALLIB: call {{.*}}.sample_texture_2d.v4f32( - && all(Tvn(T(0)) == t2D.Sample(samplerState, float2(u, u), int2(1, 1), float(1))) + && all(Tvn(T.Element(0)) == t2D.Sample(samplerState, float2(u, u), int2(1, 1), float(1))) // METAL: t3D{{.*}}.sample({{.*}} min_lod_clamp( // METALLIB: call {{.*}}.sample_texture_3d.v4f32( - && all(Tvn(T(0)) == t3D.Sample(samplerState, float3(u, u, u), int3(1, 1, 1), float(1))) + && all(Tvn(T.Element(0)) == t3D.Sample(samplerState, float3(u, u, u), int3(1, 1, 1), float(1))) // METAL: t2DArray{{.*}}.sample({{.*}} min_lod_clamp( // METALLIB: call {{.*}}.sample_texture_2d_array.v4f32( - && all(Tvn(T(0)) == t2DArray.Sample(samplerState, float3(u, u, 0), int2(1, 1), float(1))) + && all(Tvn(T.Element(0)) == t2DArray.Sample(samplerState, float3(u, u, 0), int2(1, 1), float(1))) // =============== // T SampleBias() @@ -487,23 +486,23 @@ bool TEST_texture( // METAL: t2D{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_2d.v4f32( - && all(Tvn(T(0)) == t2D.SampleBias(samplerState, float2(u, u), float(-1))) + && all(Tvn(T.Element(0)) == t2D.SampleBias(samplerState, float2(u, u), float(-1))) // METAL: t3D{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_3d.v4f32( - && all(Tvn(T(0)) == t3D.SampleBias(samplerState, float3(u, u, u), float(-1))) + && all(Tvn(T.Element(0)) == t3D.SampleBias(samplerState, float3(u, u, u), float(-1))) // METAL: tCube{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_cube.v4f32( - && all(Tvn(T(0)) == tCube.SampleBias(samplerState, normalize(float3(u, 1 - u, u)), float(-1))) + && all(Tvn(T.Element(0)) == tCube.SampleBias(samplerState, normalize(float3(u, 1 - u, u)), float(-1))) // METAL: t2DArray{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_2d_array.v4f32( - && all(Tvn(T(0)) == t2DArray.SampleBias(samplerState, float3(u, u, 0), float(-1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleBias(samplerState, float3(u, u, 0), float(-1))) // METAL: tCubeArray{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_cube_array.v4f32( - && all(Tvn(T(0)) == tCubeArray.SampleBias(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), float(-1))) + && all(Tvn(T.Element(0)) == tCubeArray.SampleBias(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), float(-1))) // Offset variant @@ -511,15 +510,15 @@ bool TEST_texture( // METAL: t2D{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_2d.v4f32( - && all(Tvn(T(0)) == t2D.SampleBias(samplerState, float2(u, u), float(-1), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.SampleBias(samplerState, float2(u, u), float(-1), int2(1, 1))) // METAL: t3D{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_3d.v4f32( - && all(Tvn(T(0)) == t3D.SampleBias(samplerState, float3(u, u, u), float(-1), int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.SampleBias(samplerState, float3(u, u, u), float(-1), int3(1, 1, 1))) // METAL: t2DArray{{.*}}.sample({{.*}} // METALLIB: call {{.*}}.sample_texture_2d_array.v4f32( - && all(Tvn(T(0)) == t2DArray.SampleBias(samplerState, float3(u, u, 0), float(-1), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleBias(samplerState, float3(u, u, 0), float(-1), int2(1, 1))) // =================================== // T SampleLevel() @@ -529,23 +528,23 @@ bool TEST_texture( // METAL: t2D{{.*}}.sample({{.*}} level( // METALLIB: call {{.*}}.sample_texture_2d.v4f32( - && all(Tvn(T(0)) == t2D.SampleLevel(samplerState, float2(u, u), 0)) + && all(Tvn(T.Element(0)) == t2D.SampleLevel(samplerState, float2(u, u), 0)) // METAL: t3D{{.*}}.sample({{.*}} level( // METALLIB: call {{.*}}.sample_texture_3d.v4f32( - && all(Tvn(T(0)) == t3D.SampleLevel(samplerState, float3(u, u, u), 0)) + && all(Tvn(T.Element(0)) == t3D.SampleLevel(samplerState, float3(u, u, u), 0)) // METAL: tCube{{.*}}.sample({{.*}} level( // METALLIB: call {{.*}}.sample_texture_cube.v4f32( - && all(Tvn(T(0)) == tCube.SampleLevel(samplerState, normalize(float3(u, 1 - u, u)), 0)) + && all(Tvn(T.Element(0)) == tCube.SampleLevel(samplerState, normalize(float3(u, 1 - u, u)), 0)) // METAL: t2DArray{{.*}}.sample({{.*}} level( // METALLIB: call {{.*}}.sample_texture_2d_array.v4f32( - && all(Tvn(T(0)) == t2DArray.SampleLevel(samplerState, float3(u, u, 0), 0)) + && all(Tvn(T.Element(0)) == t2DArray.SampleLevel(samplerState, float3(u, u, 0), 0)) // METAL: tCubeArray{{.*}}.sample({{.*}} level( // METALLIB: call {{.*}}.sample_texture_cube_array.v4f32( - && all(Tvn(T(0)) == tCubeArray.SampleLevel(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), 0)) + && all(Tvn(T.Element(0)) == tCubeArray.SampleLevel(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), 0)) // Offset variant @@ -553,15 +552,15 @@ bool TEST_texture( // METAL: t2D{{.*}}.sample({{.*}} level( // METALLIB: call {{.*}}.sample_texture_2d.v4f32( - && all(Tvn(T(0)) == t2D.SampleLevel(samplerState, float2(u, u), 0, int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.SampleLevel(samplerState, float2(u, u), 0, int2(1, 1))) // METAL: t3D{{.*}}.sample({{.*}} level( // METALLIB: call {{.*}}.sample_texture_3d.v4f32( - && all(Tvn(T(0)) == t3D.SampleLevel(samplerState, float3(u, u, u), 0, int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.SampleLevel(samplerState, float3(u, u, u), 0, int3(1, 1, 1))) // METAL: t2DArray{{.*}}.sample({{.*}} level( // METALLIB: call {{.*}}.sample_texture_2d_array.v4f32( - && all(Tvn(T(0)) == t2DArray.SampleLevel(samplerState, float3(u, u, 0), 0, int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleLevel(samplerState, float3(u, u, 0), 0, int2(1, 1))) // ================== // float SampleCmp() @@ -650,29 +649,29 @@ bool TEST_texture( // METAL: t2D{{.*}}.gather( // METALLIB: call {{.*}}.gather_texture_2d.v4f32( - && all(Tv4(T(0)) == t2D.Gather(samplerState, float2(u, u))) + && all(Tv4(T.Element(0)) == t2D.Gather(samplerState, float2(u, u))) // METAL: tCube{{.*}}.gather( // METALLIB: call {{.*}}.gather_texture_cube.v4f32( - && all(Tv4(T(0)) == tCube.Gather(samplerState, normalize(float3(u, 1 - u, u)))) + && all(Tv4(T.Element(0)) == tCube.Gather(samplerState, normalize(float3(u, 1 - u, u)))) // METAL: t2DArray{{.*}}.gather( // METALLIB: call {{.*}}.gather_texture_2d_array.v4f32( - && all(Tv4(T(0)) == t2DArray.Gather(samplerState, float3(u, u, 0))) + && all(Tv4(T.Element(0)) == t2DArray.Gather(samplerState, float3(u, u, 0))) // METAL: tCubeArray{{.*}}.gather( // METALLIB: call {{.*}}.gather_texture_cube_array.v4f32( - && all(Tv4(T(0)) == tCubeArray.Gather(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tv4(T.Element(0)) == tCubeArray.Gather(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) // Offset variant // METAL: t2D{{.*}}.gather( // METALLIB: call {{.*}}.gather_texture_2d.v4f32( - && all(Tv4(T(0)) == t2D.Gather(samplerState, float2(u2, u), int2(0, 0))) + && all(Tv4(T.Element(0)) == t2D.Gather(samplerState, float2(u2, u), int2(0, 0))) // METAL: t2DArray{{.*}}.gather( // METALLIB: call {{.*}}.gather_texture_2d_array.v4f32( - && all(Tv4(T(0)) == t2DArray.Gather(samplerState, float3(u2, u, 0), int2(0, 0))) + && all(Tv4(T.Element(0)) == t2DArray.Gather(samplerState, float3(u2, u, 0), int2(0, 0))) // ===================================== // T SampleGrad() @@ -682,33 +681,33 @@ bool TEST_texture( // METAL: t2D{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_2d_grad.v4f32( - && all(Tvn(T(0)) == t2D.SampleGrad(samplerState, float2(u, u), float2(ddx, ddx), float2(ddy, ddy))) + && all(Tvn(T.Element(0)) == t2D.SampleGrad(samplerState, float2(u, u), float2(ddx, ddx), float2(ddy, ddy))) // METAL: t3D{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_3d_grad.v4f32( - && all(Tvn(T(0)) == t3D.SampleGrad(samplerState, float3(u, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) + && all(Tvn(T.Element(0)) == t3D.SampleGrad(samplerState, float3(u, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) // METAL: tCube{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_cube_grad.v4f32( - && all(Tvn(T(0)) == tCube.SampleGrad(samplerState, normalize(float3(u, 1 - u, u)), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) + && all(Tvn(T.Element(0)) == tCube.SampleGrad(samplerState, normalize(float3(u, 1 - u, u)), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) // METAL: t2DArray{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_2d_array_grad.v4f32( - && all(Tvn(T(0)) == t2DArray.SampleGrad(samplerState, float3(u, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy))) + && all(Tvn(T.Element(0)) == t2DArray.SampleGrad(samplerState, float3(u, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy))) // Offset variant // METAL: t2D{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_2d_grad.v4f32( - && all(Tvn(T(0)) == t2D.SampleGrad(samplerState, float2(u2, u), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) + && all(Tvn(T.Element(0)) == t2D.SampleGrad(samplerState, float2(u2, u), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) // METAL: t3D{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_3d_grad.v4f32( - && all(Tvn(T(0)) == t3D.SampleGrad(samplerState, float3(u2, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy), int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == t3D.SampleGrad(samplerState, float3(u2, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy), int3(0, 0, 0))) // METAL: t2DArray{{.*}}.sample( // METALLIB: call {{.*}}.sample_texture_2d_array_grad.v4f32( - && all(Tvn(T(0)) == t2DArray.SampleGrad(samplerState, float3(u2, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) + && all(Tvn(T.Element(0)) == t2DArray.SampleGrad(samplerState, float3(u2, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) // =================== // T Load() @@ -716,23 +715,23 @@ bool TEST_texture( // METAL: t1D{{.*}}.read( // METALLIB: call {{.*}}.read_texture_1d.v4f32( - && all(Tvn(T(0)) == t1D.Load(int2(0, 0))) + && all(Tvn(T.Element(0)) == t1D.Load(int2(0, 0))) // METAL: t2D{{.*}}.read( // METALLIB: call {{.*}}.read_texture_2d.v4f32( - && all(Tvn(T(0)) == t2D.Load(int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == t2D.Load(int3(0, 0, 0))) // METAL: t3D{{.*}}.read( // METALLIB: call {{.*}}.read_texture_3d.v4f32( - && all(Tvn(T(0)) == t3D.Load(int4(0, 0, 0, 0))) + && all(Tvn(T.Element(0)) == t3D.Load(int4(0, 0, 0, 0))) // METAL: t1DArray{{.*}}.read( // METALLIB: call {{.*}}.read_texture_1d_array.v4f32( - && all(Tvn(T(0)) == t1DArray.Load(int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == t1DArray.Load(int3(0, 0, 0))) // METAL: t2DArray{{.*}}.read( // METALLIB: call {{.*}}.read_texture_2d_array.v4f32( - && all(Tvn(T(0)) == t2DArray.Load(int4(0, 0, 0, 0))) + && all(Tvn(T.Element(0)) == t2DArray.Load(int4(0, 0, 0, 0))) // Offset variant @@ -749,7 +748,7 @@ void computeMain() // HLSL: void computeMain( bool result = true - && TEST_texture( + && TEST_texture( t1D_f32v3, t2D_f32v3, t3D_f32v3, @@ -765,7 +764,7 @@ void computeMain() // - If T is uint, Tv is uint4. // - If T is short, Tv is short4. // - If T is ushort, Tv is ushort4." - && TEST_texture( + && TEST_texture( t1D_f32, t2D_f32, t3D_f32, @@ -774,7 +773,7 @@ void computeMain() t2DArray_f32, tCubeArray_f32) #if !defined(EXCLUDE_HALF_TYPE) - && TEST_texture( + && TEST_texture( t1D_f16, t2D_f16, t3D_f16, @@ -784,7 +783,7 @@ void computeMain() tCubeArray_f16) #endif #if !defined(EXCLUDE_INTEGER_TYPE) - && TEST_texture( + && TEST_texture( t1D_i32, t2D_i32, t3D_i32, @@ -792,7 +791,7 @@ void computeMain() t1DArray_i32, t2DArray_i32, tCubeArray_i32) - && TEST_texture( + && TEST_texture( t1D_u32, t2D_u32, t3D_u32, @@ -801,7 +800,7 @@ void computeMain() t2DArray_u32, tCubeArray_u32) #if !defined(EXCLUDE_SHORT_TYPE) - && TEST_texture( + && TEST_texture( t1D_i16, t2D_i16, t3D_i16, @@ -809,7 +808,7 @@ void computeMain() t1DArray_i16, t2DArray_i16, tCubeArray_i16) - && TEST_texture( + && TEST_texture( t1D_u16, t2D_u16, t3D_u16, diff --git a/tests/wgsl/texture-gather.slang b/tests/wgsl/texture-gather.slang index b77ec9f8f0..d0772994e4 100644 --- a/tests/wgsl/texture-gather.slang +++ b/tests/wgsl/texture-gather.slang @@ -135,15 +135,15 @@ SamplerState samplerState; SamplerComparisonState depthSampler; -__generic -bool TEST_textureGather( - Texture2D> t2D, - TextureCube> tCube, - Texture2DArray> t2DArray, - TextureCubeArray> tCubeArray) +bool TEST_textureGather( + Texture2D t2D, + TextureCube tCube, + Texture2DArray t2DArray, + TextureCubeArray tCubeArray) + where T:IArithmetic,ITexelElement { // WGSL-LABEL: TEST_textureGather - typealias Tv4 = vector; + typealias Tv4 = vector; float u = 0; float u2 = 0.5; @@ -155,90 +155,90 @@ bool TEST_textureGather( // ================================== // WGSL-COUNT-2: textureGather({{.*}}0{{.*}}t2D - && all(Tv4(T(0)) == t2D.Gather(samplerState, float2(u, u))) - && all(Tv4(T(0)) == t2D.Gather(samplerState, float2(u2, u), int2(0,0))) + && all(Tv4(T.Element(0)) == t2D.Gather(samplerState, float2(u, u))) + && all(Tv4(T.Element(0)) == t2D.Gather(samplerState, float2(u2, u), int2(0,0))) // WGSL: textureGather({{.*}}0{{.*}}tCube - && all(Tv4(T(0)) == tCube.Gather(samplerState, normalize(float3(u, 1 - u, u)))) + && all(Tv4(T.Element(0)) == tCube.Gather(samplerState, normalize(float3(u, 1 - u, u)))) // WGSL-COUNT-2: textureGather({{.*}}0{{.*}}t2DArray - && all(Tv4(T(0)) == t2DArray.Gather(samplerState, float3(u, u, 0))) - && all(Tv4(T(0)) == t2DArray.Gather(samplerState, float3(u2, u, 0), int2(0,0))) + && all(Tv4(T.Element(0)) == t2DArray.Gather(samplerState, float3(u, u, 0))) + && all(Tv4(T.Element(0)) == t2DArray.Gather(samplerState, float3(u2, u, 0), int2(0,0))) // WGSL: textureGather({{.*}}0{{.*}}tCubeArray - && all(Tv4(T(0)) == tCubeArray.Gather(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tv4(T.Element(0)) == tCubeArray.Gather(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) // ================================== // vector GatherRed() // ================================== // WGSL-COUNT-2: textureGather({{.*}}0{{.*}}t2D - && all(Tv4(T(0)) == t2D.GatherRed(samplerState, float2(u, u))) - && all(Tv4(T(0)) == t2D.GatherRed(samplerState, float2(u2, u), int2(0,0))) + && all(Tv4(T.Element(0)) == t2D.GatherRed(samplerState, float2(u, u))) + && all(Tv4(T.Element(0)) == t2D.GatherRed(samplerState, float2(u2, u), int2(0,0))) // WGSL: textureGather({{.*}}0{{.*}}tCube - && all(Tv4(T(0)) == tCube.GatherRed(samplerState, normalize(float3(u, 1 - u, u)))) + && all(Tv4(T.Element(0)) == tCube.GatherRed(samplerState, normalize(float3(u, 1 - u, u)))) // WGSL-COUNT-2: textureGather({{.*}}0{{.*}}t2DArray - && all(Tv4(T(0)) == t2DArray.GatherRed(samplerState, float3(u, u, 0))) - && all(Tv4(T(0)) == t2DArray.GatherRed(samplerState, float3(u2, u, 0), int2(0,0))) + && all(Tv4(T.Element(0)) == t2DArray.GatherRed(samplerState, float3(u, u, 0))) + && all(Tv4(T.Element(0)) == t2DArray.GatherRed(samplerState, float3(u2, u, 0), int2(0,0))) // WGSL: textureGather({{.*}}0{{.*}}tCubeArray - && all(Tv4(T(0)) == tCubeArray.GatherRed(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tv4(T.Element(0)) == tCubeArray.GatherRed(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) // ================================== // vector GatherGreen() // ================================== // WGSL-COUNT-2: textureGather({{.*}}1{{.*}}t2D - && all(Tv4(T(0)) == t2D.GatherGreen(samplerState, float2(u, u))) - && all(Tv4(T(0)) == t2D.GatherGreen(samplerState, float2(u2, u), int2(0,0))) + && all(Tv4(T.Element(0)) == t2D.GatherGreen(samplerState, float2(u, u))) + && all(Tv4(T.Element(0)) == t2D.GatherGreen(samplerState, float2(u2, u), int2(0,0))) // WGSL: textureGather({{.*}}1{{.*}}tCube - && all(Tv4(T(0)) == tCube.GatherGreen(samplerState, normalize(float3(u, 1 - u, u)))) + && all(Tv4(T.Element(0)) == tCube.GatherGreen(samplerState, normalize(float3(u, 1 - u, u)))) // WGSL-COUNT-2: textureGather({{.*}}1{{.*}}t2DArray - && all(Tv4(T(0)) == t2DArray.GatherGreen(samplerState, float3(u, u, 0))) - && all(Tv4(T(0)) == t2DArray.GatherGreen(samplerState, float3(u2, u, 0), int2(0,0))) + && all(Tv4(T.Element(0)) == t2DArray.GatherGreen(samplerState, float3(u, u, 0))) + && all(Tv4(T.Element(0)) == t2DArray.GatherGreen(samplerState, float3(u2, u, 0), int2(0,0))) // WGSL: textureGather({{.*}}1{{.*}}tCubeArray - && all(Tv4(T(0)) == tCubeArray.GatherGreen(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tv4(T.Element(0)) == tCubeArray.GatherGreen(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) // ================================== // vector GatherBlue() // ================================== // WGSL-COUNT-2: textureGather({{.*}}2{{.*}}t2D - && all(Tv4(T(0)) == t2D.GatherBlue(samplerState, float2(u, u))) - && all(Tv4(T(0)) == t2D.GatherBlue(samplerState, float2(u2, u), int2(0,0))) + && all(Tv4(T.Element(0)) == t2D.GatherBlue(samplerState, float2(u, u))) + && all(Tv4(T.Element(0)) == t2D.GatherBlue(samplerState, float2(u2, u), int2(0,0))) // WGSL: textureGather({{.*}}2{{.*}}tCube - && all(Tv4(T(0)) == tCube.GatherBlue(samplerState, normalize(float3(u, 1 - u, u)))) + && all(Tv4(T.Element(0)) == tCube.GatherBlue(samplerState, normalize(float3(u, 1 - u, u)))) // WGSL-COUNT-2: textureGather({{.*}}2{{.*}}t2DArray - && all(Tv4(T(0)) == t2DArray.GatherBlue(samplerState, float3(u, u, 0))) - && all(Tv4(T(0)) == t2DArray.GatherBlue(samplerState, float3(u2, u, 0), int2(0,0))) + && all(Tv4(T.Element(0)) == t2DArray.GatherBlue(samplerState, float3(u, u, 0))) + && all(Tv4(T.Element(0)) == t2DArray.GatherBlue(samplerState, float3(u2, u, 0), int2(0,0))) // WGSL: textureGather({{.*}}2{{.*}}tCubeArray - && all(Tv4(T(0)) == tCubeArray.GatherBlue(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tv4(T.Element(0)) == tCubeArray.GatherBlue(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) // ================================== // vector GatherAlpha() // ================================== // WGSL-COUNT-2: textureGather({{.*}}3{{.*}}t2D - && all(Tv4(T(0)) == t2D.GatherAlpha(samplerState, float2(u, u))) - && all(Tv4(T(0)) == t2D.GatherAlpha(samplerState, float2(u2, u), int2(0,0))) + && all(Tv4(T.Element(0)) == t2D.GatherAlpha(samplerState, float2(u, u))) + && all(Tv4(T.Element(0)) == t2D.GatherAlpha(samplerState, float2(u2, u), int2(0,0))) // WGSL: textureGather({{.*}}3{{.*}}tCube - && all(Tv4(T(0)) == tCube.GatherAlpha(samplerState, normalize(float3(u, 1 - u, u)))) + && all(Tv4(T.Element(0)) == tCube.GatherAlpha(samplerState, normalize(float3(u, 1 - u, u)))) // WGSL-COUNT-2: textureGather({{.*}}3{{.*}}t2DArray - && all(Tv4(T(0)) == t2DArray.GatherAlpha(samplerState, float3(u, u, 0))) - && all(Tv4(T(0)) == t2DArray.GatherAlpha(samplerState, float3(u2, u, 0), int2(0,0))) + && all(Tv4(T.Element(0)) == t2DArray.GatherAlpha(samplerState, float3(u, u, 0))) + && all(Tv4(T.Element(0)) == t2DArray.GatherAlpha(samplerState, float3(u2, u, 0), int2(0,0))) // WGSL: textureGather({{.*}}3{{.*}}tCubeArray - && all(Tv4(T(0)) == tCubeArray.GatherAlpha(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tv4(T.Element(0)) == tCubeArray.GatherAlpha(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) ; } @@ -318,12 +318,12 @@ bool TEST_textureGather_depth() void fragMain() { bool result = true - && TEST_textureGather(t2D_f32v3, tCube_f32v3, t2DArray_f32v3, tCubeArray_f32v3) - && TEST_textureGather(t2D_f32v4, tCube_f32v4, t2DArray_f32v4, tCubeArray_f32v4) - && TEST_textureGather(t2D_i32v3, tCube_i32v3, t2DArray_i32v3, tCubeArray_i32v3) - && TEST_textureGather(t2D_i32v4, tCube_i32v4, t2DArray_i32v4, tCubeArray_i32v4) - && TEST_textureGather(t2D_u32v3, tCube_u32v3, t2DArray_u32v3, tCubeArray_u32v3) - && TEST_textureGather(t2D_u32v4, tCube_u32v4, t2DArray_u32v4, tCubeArray_u32v4) + && TEST_textureGather(t2D_f32v3, tCube_f32v3, t2DArray_f32v3, tCubeArray_f32v3) + && TEST_textureGather(t2D_f32v4, tCube_f32v4, t2DArray_f32v4, tCubeArray_f32v4) + && TEST_textureGather(t2D_i32v3, tCube_i32v3, t2DArray_i32v3, tCubeArray_i32v3) + && TEST_textureGather(t2D_i32v4, tCube_i32v4, t2DArray_i32v4, tCubeArray_i32v4) + && TEST_textureGather(t2D_u32v3, tCube_u32v3, t2DArray_u32v3, tCubeArray_u32v3) + && TEST_textureGather(t2D_u32v4, tCube_u32v4, t2DArray_u32v4, tCubeArray_u32v4) && TEST_textureGather_depth() ; diff --git a/tests/wgsl/texture-load.slang b/tests/wgsl/texture-load.slang index 3e69ac5cf6..9be7cd2eb3 100644 --- a/tests/wgsl/texture-load.slang +++ b/tests/wgsl/texture-load.slang @@ -127,16 +127,16 @@ Depth2DMS d2DMS; Depth2DArray d2DArray; -__generic -bool TEST_textureLoad( - Texture1D> t1D, - Texture2D> t2D, - Texture3D> t3D, - Texture2DMS> t2DMS, - Texture2DArray> t2DArray) +bool TEST_textureLoad( + Texture1D t1D, + Texture2D t2D, + Texture3D t3D, + Texture2DMS t2DMS, + Texture2DArray t2DArray) + where T : ITexelElement, IArithmetic { // WGSL-LABEL: TEST_textureLoad - typealias Tvn = vector; + typealias Tvn = T; return true // =================== @@ -145,19 +145,19 @@ bool TEST_textureLoad( // =================== // WGSL: textureLoad({{\(*}}t1D - && all(Tvn(T(0)) == t1D.Load(int2(0, 0))) + && all(Tvn(T.Element(0)) == t1D.Load(int2(0, 0))) // WGSL: textureLoad({{\(*}}t2D - && all(Tvn(T(0)) == t2D.Load(int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == t2D.Load(int3(0, 0, 0))) // WGSL: textureLoad({{\(*}}t3D - && all(Tvn(T(0)) == t3D.Load(int4(0, 0, 0, 0))) + && all(Tvn(T.Element(0)) == t3D.Load(int4(0, 0, 0, 0))) // WGSL: textureLoad({{\(*}}t2DMS - && all(Tvn(T(0)) == t2DMS.Load(int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == t2DMS.Load(int3(0, 0, 0))) // WGSL: textureLoad({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.Load(int4(0, 0, 0, 0))) + && all(Tvn(T.Element(0)) == t2DArray.Load(int4(0, 0, 0, 0))) ; } @@ -185,12 +185,12 @@ bool TEST_textureLoad_depth() void fragMain() { bool result = true - && TEST_textureLoad(t1D_f32v3, t2D_f32v3, t3D_f32v3, t2DMS_f32v3, t2DArray_f32v3) - && TEST_textureLoad(t1D_f32v4, t2D_f32v4, t3D_f32v4, t2DMS_f32v4, t2DArray_f32v4) - && TEST_textureLoad(t1D_i32v3, t2D_i32v3, t3D_i32v3, t2DMS_i32v3, t2DArray_i32v3) - && TEST_textureLoad(t1D_i32v4, t2D_i32v4, t3D_i32v4, t2DMS_i32v4, t2DArray_i32v4) - && TEST_textureLoad(t1D_u32v3, t2D_u32v3, t3D_u32v3, t2DMS_u32v3, t2DArray_u32v3) - && TEST_textureLoad(t1D_u32v4, t2D_u32v4, t3D_u32v4, t2DMS_u32v4, t2DArray_u32v4) + && TEST_textureLoad(t1D_f32v3, t2D_f32v3, t3D_f32v3, t2DMS_f32v3, t2DArray_f32v3) + && TEST_textureLoad(t1D_f32v4, t2D_f32v4, t3D_f32v4, t2DMS_f32v4, t2DArray_f32v4) + && TEST_textureLoad(t1D_i32v3, t2D_i32v3, t3D_i32v3, t2DMS_i32v3, t2DArray_i32v3) + && TEST_textureLoad(t1D_i32v4, t2D_i32v4, t3D_i32v4, t2DMS_i32v4, t2DArray_i32v4) + && TEST_textureLoad(t1D_u32v3, t2D_u32v3, t3D_u32v3, t2DMS_u32v3, t2DArray_u32v3) + && TEST_textureLoad(t1D_u32v4, t2D_u32v4, t3D_u32v4, t2DMS_u32v4, t2DArray_u32v4) && TEST_textureLoad_depth() ; diff --git a/tests/wgsl/texture-sampler-less.slang b/tests/wgsl/texture-sampler-less.slang index 2498035267..7868a9bcfe 100644 --- a/tests/wgsl/texture-sampler-less.slang +++ b/tests/wgsl/texture-sampler-less.slang @@ -43,7 +43,7 @@ Sampler2DArray t2DArray_f32v4; //TEST_INPUT: TextureSamplerCube(size=4, content = zero, arrayLength=2):name tCubeArray_f32v4 SamplerCubeArray tCubeArray_f32v4; -__generic +__generic typealias CombinedDepth2d = _Texture< T, __Shape2D, @@ -56,7 +56,7 @@ typealias CombinedDepth2d = _Texture< format >; -__generic +__generic typealias CombinedDepth2d_array = _Texture< T, __Shape2D, @@ -69,7 +69,7 @@ typealias CombinedDepth2d_array = _Texture< format >; -__generic +__generic typealias CombinedDepthcube = _Texture< T, __ShapeCube, @@ -82,7 +82,7 @@ typealias CombinedDepthcube = _Texture< format >; -__generic +__generic typealias CombinedDepthcube_array = _Texture< T, __ShapeCube, @@ -108,20 +108,19 @@ CombinedDepthcube_array dCubeArray; RWStructuredBuffer outputBuffer; -__generic -bool TEST_texture( - Sampler1D> t1D, - Sampler2D> t2D, - Sampler3D> t3D, - SamplerCube> tCube, - Sampler1DArray> t1DArray, - Sampler2DArray> t2DArray, - SamplerCubeArray> tCubeArray -) +bool TEST_texture( + Sampler1D t1D, + Sampler2D t2D, + Sampler3D t3D, + SamplerCube tCube, + Sampler1DArray t1DArray, + Sampler2DArray t2DArray, + SamplerCubeArray tCubeArray +) where T:ITexelElement,IArithmetic { // WGSL-LABEL: TEST_texture - typealias Tvn = vector; - typealias Tv4 = vector; + typealias Tvn = T; + typealias Tv4 = vector; float u = 0; float u2 = 0.5; @@ -145,41 +144,41 @@ bool TEST_texture( // =========== // WGSL: textureSample({{\(*}}t1D - && all(Tvn(T(0)) == t1D.Sample(u)) + && all(Tvn(T.Element(0)) == t1D.Sample(u)) // WGSL: textureSample({{\(*}}t2D - && all(Tvn(T(0)) == t2D.Sample(float2(u, u))) + && all(Tvn(T.Element(0)) == t2D.Sample(float2(u, u))) // WGSL: textureSample({{\(*}}t3D - && all(Tvn(T(0)) == t3D.Sample(float3(u, u, u))) + && all(Tvn(T.Element(0)) == t3D.Sample(float3(u, u, u))) // WGSL: textureSample({{\(*}}tCube - && all(Tvn(T(0)) == tCube.Sample(normalize(float3(u, 1 - u, u)))) + && all(Tvn(T.Element(0)) == tCube.Sample(normalize(float3(u, 1 - u, u)))) // WGSL doesn't support textureSample for 1d_array and 3d_array; only 2d and cube // WGSL: textureSample({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.Sample(float3(u, u, 0))) + && all(Tvn(T.Element(0)) == t2DArray.Sample(float3(u, u, 0))) // WGSL: textureSample({{\(*}}tCubeArray - && all(Tvn(T(0)) == tCubeArray.Sample(float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tvn(T.Element(0)) == tCubeArray.Sample(float4(normalize(float3(u, 1 - u, u)), 0))) #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // Offset variant // WGSL: textureSample({{\(*}}t1D - && all(Tvn(T(0)) == t1D.Sample(u, 1)) + && all(Tvn(T.Element(0)) == t1D.Sample(u, 1)) // WGSL: textureSample({{\(*}}t2D - && all(Tvn(T(0)) == t2D.Sample(float2(u, u), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.Sample(float2(u, u), int2(1, 1))) // WGSL: textureSample({{\(*}}t3D - && all(Tvn(T(0)) == t3D.Sample(float3(u, u, u), int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.Sample(float3(u, u, u), int3(1, 1, 1))) // WGSL doesn't support offset variant for cube and cube_array // WGSL: textureSample({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.Sample(float3(u, u, 0), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.Sample(float3(u, u, 0), int2(1, 1))) #endif // #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // =============== @@ -190,31 +189,31 @@ bool TEST_texture( // WGSL doesn't support Bias for 1D texture // WGSL: textureSampleBias({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleBias(float2(u, u), float(-1))) + && all(Tvn(T.Element(0)) == t2D.SampleBias(float2(u, u), float(-1))) // WGSL: textureSampleBias({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleBias(float3(u, u, u), float(-1))) + && all(Tvn(T.Element(0)) == t3D.SampleBias(float3(u, u, u), float(-1))) // WGSL: textureSampleBias({{\(*}}tCube - && all(Tvn(T(0)) == tCube.SampleBias(normalize(float3(u, 1 - u, u)), float(-1))) + && all(Tvn(T.Element(0)) == tCube.SampleBias(normalize(float3(u, 1 - u, u)), float(-1))) // WGSL: textureSampleBias({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleBias(float3(u, u, 0), float(-1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleBias(float3(u, u, 0), float(-1))) // WGSL: textureSampleBias({{\(*}}tCubeArray - && all(Tvn(T(0)) == tCubeArray.SampleBias(float4(normalize(float3(u, 1 - u, u)), 0), float(-1))) + && all(Tvn(T.Element(0)) == tCubeArray.SampleBias(float4(normalize(float3(u, 1 - u, u)), 0), float(-1))) #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // Offset variant // W-GSL: textureSampleBias({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleBias(float2(u, u), float(-1), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.SampleBias(float2(u, u), float(-1), int2(1, 1))) // W-GSL: textureSampleBias({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleBias(float3(u, u, u), float(-1), int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.SampleBias(float3(u, u, u), float(-1), int3(1, 1, 1))) // W-GSL: textureSampleBias({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleBias(float3(u, u, 0), float(-1), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleBias(float3(u, u, 0), float(-1), int2(1, 1))) #endif // #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // =================================== @@ -225,31 +224,31 @@ bool TEST_texture( // WGSL doesn't support textureSampleLevel for 1D texture // WGSL: textureSampleLevel({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleLevel(float2(u, u), 0)) + && all(Tvn(T.Element(0)) == t2D.SampleLevel(float2(u, u), 0)) // WGSL: textureSampleLevel({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleLevel(float3(u, u, u), 0)) + && all(Tvn(T.Element(0)) == t3D.SampleLevel(float3(u, u, u), 0)) // WGSL: textureSampleLevel({{\(*}}tCube - && all(Tvn(T(0)) == tCube.SampleLevel(normalize(float3(u, 1 - u, u)), 0)) + && all(Tvn(T.Element(0)) == tCube.SampleLevel(normalize(float3(u, 1 - u, u)), 0)) // WGSL: textureSampleLevel({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleLevel(float3(u, u, 0), 0)) + && all(Tvn(T.Element(0)) == t2DArray.SampleLevel(float3(u, u, 0), 0)) // WGSL: textureSampleLevel({{\(*}}tCubeArray - && all(Tvn(T(0)) == tCubeArray.SampleLevel(float4(normalize(float3(u, 1 - u, u)), 0), 0)) + && all(Tvn(T.Element(0)) == tCubeArray.SampleLevel(float4(normalize(float3(u, 1 - u, u)), 0), 0)) #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // Offset variant // W-GSL: textureSampleLevel({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleLevel(float2(u, u), 0, int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.SampleLevel(float2(u, u), 0, int2(1, 1))) // W-GSL: textureSampleLevel({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleLevel(float3(u, u, u), 0, int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.SampleLevel(float3(u, u, u), 0, int3(1, 1, 1))) // W-GSL: textureSampleLevel({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleLevel(float3(u, u, 0), 0, int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleLevel(float3(u, u, 0), 0, int2(1, 1))) #endif // #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // ================== @@ -312,25 +311,25 @@ bool TEST_texture( // ================================== // WGSL: textureGather({{.*}}t2D - && all(Tv4(T(0)) == t2D.Gather(float2(u, u))) + && all(Tv4(T.Element(0)) == t2D.Gather(float2(u, u))) // WGSL: textureGather({{.*}}tCube - && all(Tv4(T(0)) == tCube.Gather(normalize(float3(u, 1 - u, u)))) + && all(Tv4(T.Element(0)) == tCube.Gather(normalize(float3(u, 1 - u, u)))) // WGSL: textureGather({{.*}}t2DArray - && all(Tv4(T(0)) == t2DArray.Gather(float3(u, u, 0))) + && all(Tv4(T.Element(0)) == t2DArray.Gather(float3(u, u, 0))) // WGSL: textureGather({{.*}}tCubeArray - && all(Tv4(T(0)) == tCubeArray.Gather(float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tv4(T.Element(0)) == tCubeArray.Gather(float4(normalize(float3(u, 1 - u, u)), 0))) #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // Offset variant // W-GSL: textureGather({{.*}}t2D - && all(Tv4(T(0)) == t2D.Gather(float2(u2, u), int2(0, 0))) + && all(Tv4(T.Element(0)) == t2D.Gather(float2(u2, u), int2(0, 0))) // W-GSL: textureGather({{.*}}t2DArray - && all(Tv4(T(0)) == t2DArray.Gather(float3(u2, u, 0), int2(0, 0))) + && all(Tv4(T.Element(0)) == t2DArray.Gather(float3(u2, u, 0), int2(0, 0))) #endif // #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // ===================================== @@ -341,31 +340,31 @@ bool TEST_texture( // WGSL doesn't support textureSampleGrad for 1D textures // WGSL: textureSampleGrad({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleGrad(float2(u, u), float2(ddx, ddx), float2(ddy, ddy))) + && all(Tvn(T.Element(0)) == t2D.SampleGrad(float2(u, u), float2(ddx, ddx), float2(ddy, ddy))) // WGSL: textureSampleGrad({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleGrad(float3(u, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) + && all(Tvn(T.Element(0)) == t3D.SampleGrad(float3(u, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) // WGSL: textureSampleGrad({{\(*}}tCube - && all(Tvn(T(0)) == tCube.SampleGrad(normalize(float3(u, 1 - u, u)), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) + && all(Tvn(T.Element(0)) == tCube.SampleGrad(normalize(float3(u, 1 - u, u)), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) // WGSL: textureSampleGrad({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleGrad(float3(u, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy))) + && all(Tvn(T.Element(0)) == t2DArray.SampleGrad(float3(u, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy))) // WGSL: textureSampleGrad({{\(*}}tCubeArray - && all(Tvn(T(0)) == tCubeArray.SampleGrad(float4(normalize(float3(u, 1 - u, u)), 0), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) + && all(Tvn(T.Element(0)) == tCubeArray.SampleGrad(float4(normalize(float3(u, 1 - u, u)), 0), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET // Offset variant // W-GSL: textureSampleGrad({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleGrad(float2(u2, u), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) + && all(Tvn(T.Element(0)) == t2D.SampleGrad(float2(u2, u), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) // W-GSL: textureSampleGrad({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleGrad(float3(u2, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy), int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == t3D.SampleGrad(float3(u2, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy), int3(0, 0, 0))) // W-GSL: textureSampleGrad({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleGrad(float3(u2, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) + && all(Tvn(T.Element(0)) == t2DArray.SampleGrad(float3(u2, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) #endif // #if TEST_WHEN_CONSTEXPR_WORKS_FOR_OFFSET ; @@ -375,7 +374,7 @@ bool TEST_texture( void fragMain() { bool result = true - && TEST_texture( + && TEST_texture( t1D_f32v3, t2D_f32v3, t3D_f32v3, @@ -383,7 +382,7 @@ void fragMain() t1DArray_f32v3, t2DArray_f32v3, tCubeArray_f32v3) - && TEST_texture( + && TEST_texture( t1D_f32v4, t2D_f32v4, t3D_f32v4, diff --git a/tests/wgsl/texture-storage.slang b/tests/wgsl/texture-storage.slang index 84921870b6..1109dbfb18 100644 --- a/tests/wgsl/texture-storage.slang +++ b/tests/wgsl/texture-storage.slang @@ -73,15 +73,16 @@ RWStructuredBuffer outputBuffer; [format("rgba16ui")] RWTexture2DArray w2DArray_u32v4; -__generic +__generic [ForceInline] // Workaround for a WGSL requirement that `texture_storage_Xd` must always be a global variable bool TEST_textureStorage_StoreLoad( - RWTexture1D, sampleIndex, format> w1D, - RWTexture2D, sampleIndex, format> w2D, - RWTexture3D, sampleIndex, format> w3D, - RWTexture2DArray, sampleIndex, format> w2DArray) + RWTexture1D w1D, + RWTexture2D w2D, + RWTexture3D w3D, + RWTexture2DArray w2DArray) + where T:ITexelElement, IArithmetic { - typealias Tvn = vector; + typealias Tvn = T; // =================== // o[i] = v; @@ -90,16 +91,16 @@ bool TEST_textureStorage_StoreLoad( // TODO: store before load // WGSL: textureStore({{\(*}}w1D - w1D[0] = Tvn(T(1)); + w1D[0] = Tvn(T.Element(1)); // WGSL: textureStore({{\(*}}w2D - w2D[0] = Tvn(T(1)); + w2D[0] = Tvn(T.Element(1)); // WGSL: textureStore({{\(*}}w3D - w3D[0] = Tvn(T(1)); + w3D[0] = Tvn(T.Element(1)); // WGSL: textureStore({{\(*}}w2DArray - w2DArray[0] = Tvn(T(1)); + w2DArray[0] = Tvn(T.Element(1)); return true // =================== @@ -108,28 +109,28 @@ bool TEST_textureStorage_StoreLoad( // =================== // WGSL: textureLoad({{\(*}}w1D - && all(Tvn(T(0)) == w1D.Load(0)) + && all(Tvn(T.Element(0)) == w1D.Load(0)) // WGSL: textureLoad({{\(*}}w2D - && all(Tvn(T(0)) == w2D.Load(int2(0, 0))) + && all(Tvn(T.Element(0)) == w2D.Load(int2(0, 0))) // WGSL: textureLoad({{\(*}}w3D - && all(Tvn(T(0)) == w3D.Load(int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == w3D.Load(int3(0, 0, 0))) // WGSL: textureLoad({{\(*}}w2DArray - && all(Tvn(T(0)) == w2DArray.Load(int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == w2DArray.Load(int3(0, 0, 0))) ; } void fragMain() { bool result = true - && TEST_textureStorage_StoreLoad(w1D_f32v2, w2D_f32v2, w3D_f32v2, w2DArray_f32v2) - && TEST_textureStorage_StoreLoad(w1D_f32v4, w2D_f32v4, w3D_f32v4, w2DArray_f32v4) - && TEST_textureStorage_StoreLoad(w1D_i32v2, w2D_i32v2, w3D_i32v2, w2DArray_i32v2) - && TEST_textureStorage_StoreLoad(w1D_i32v4, w2D_i32v4, w3D_i32v4, w2DArray_i32v4) - && TEST_textureStorage_StoreLoad(w1D_u32v2, w2D_u32v2, w3D_u32v2, w2DArray_u32v2) - && TEST_textureStorage_StoreLoad(w1D_u32v4, w2D_u32v4, w3D_u32v4, w2DArray_u32v4) + && TEST_textureStorage_StoreLoad(w1D_f32v2, w2D_f32v2, w3D_f32v2, w2DArray_f32v2) + && TEST_textureStorage_StoreLoad(w1D_f32v4, w2D_f32v4, w3D_f32v4, w2DArray_f32v4) + && TEST_textureStorage_StoreLoad(w1D_i32v2, w2D_i32v2, w3D_i32v2, w2DArray_i32v2) + && TEST_textureStorage_StoreLoad(w1D_i32v4, w2D_i32v4, w3D_i32v4, w2DArray_i32v4) + && TEST_textureStorage_StoreLoad(w1D_u32v2, w2D_u32v2, w3D_u32v2, w2DArray_u32v2) + && TEST_textureStorage_StoreLoad(w1D_u32v4, w2D_u32v4, w3D_u32v4, w2DArray_u32v4) ; outputBuffer[0] = int(result); diff --git a/tests/wgsl/texture.slang b/tests/wgsl/texture.slang index be098c9ca1..1a14fec328 100644 --- a/tests/wgsl/texture.slang +++ b/tests/wgsl/texture.slang @@ -35,7 +35,7 @@ Texture2DArray t2DArray_f32v4; //TEST_INPUT: TextureCube(size=4, content = zero, arrayLength=2):name tCubeArray_f32v4 TextureCubeArray tCubeArray_f32v4; -__generic +__generic typealias depth2d = _Texture< T, __Shape2D, @@ -48,7 +48,7 @@ typealias depth2d = _Texture< format >; -__generic +__generic typealias depth2d_array = _Texture< T, __Shape2D, @@ -61,7 +61,7 @@ typealias depth2d_array = _Texture< format >; -__generic +__generic typealias depthcube = _Texture< T, __ShapeCube, @@ -74,7 +74,7 @@ typealias depthcube = _Texture< format >; -__generic +__generic typealias depthcube_array = _Texture< T, __ShapeCube, @@ -101,21 +101,19 @@ SamplerState samplerState; //TEST_INPUT: Sampler:name shadowSampler SamplerComparisonState shadowSampler; - -__generic -bool TEST_texture( - Texture1D> t1D, - Texture2D> t2D, - Texture3D> t3D, - TextureCube> tCube, - Texture1DArray> t1DArray, - Texture2DArray> t2DArray, - TextureCubeArray> tCubeArray -) +bool TEST_texture( + Texture1D t1D, + Texture2D t2D, + Texture3D t3D, + TextureCube tCube, + Texture1DArray t1DArray, + Texture2DArray t2DArray, + TextureCubeArray tCubeArray +) where T:ITexelElement, IArithmetic { // WGSL-LABEL: TEST_texture - typealias Tvn = vector; - typealias Tv4 = vector; + typealias Tvn = T; + typealias Tv4 = vector; float u = 0; float u2 = 0.5; @@ -185,40 +183,40 @@ bool TEST_texture( // =========== // WGSL: textureSample({{\(*}}t1D - && all(Tvn(T(0)) == t1D.Sample(samplerState, u)) + && all(Tvn(T.Element(0)) == t1D.Sample(samplerState, u)) // WGSL: textureSample({{\(*}}t2D - && all(Tvn(T(0)) == t2D.Sample(samplerState, float2(u, u))) + && all(Tvn(T.Element(0)) == t2D.Sample(samplerState, float2(u, u))) // WGSL: textureSample({{\(*}}t3D - && all(Tvn(T(0)) == t3D.Sample(samplerState, float3(u, u, u))) + && all(Tvn(T.Element(0)) == t3D.Sample(samplerState, float3(u, u, u))) // WGSL: textureSample({{\(*}}tCube - && all(Tvn(T(0)) == tCube.Sample(samplerState, normalize(float3(u, 1 - u, u)))) + && all(Tvn(T.Element(0)) == tCube.Sample(samplerState, normalize(float3(u, 1 - u, u)))) // WGSL doesn't support textureSample for 1d_array and 3d_array; only 2d and cube // WGSL: textureSample({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.Sample(samplerState, float3(u, u, 0))) + && all(Tvn(T.Element(0)) == t2DArray.Sample(samplerState, float3(u, u, 0))) // WGSL: textureSample({{\(*}}tCubeArray - && all(Tvn(T(0)) == tCubeArray.Sample(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) + && all(Tvn(T.Element(0)) == tCubeArray.Sample(samplerState, float4(normalize(float3(u, 1 - u, u)), 0))) // Offset variant // WGSL: textureSample({{\(*}}t1D - && all(Tvn(T(0)) == t1D.Sample(samplerState, u, 1)) + && all(Tvn(T.Element(0)) == t1D.Sample(samplerState, u, 1)) // WGSL: textureSample({{\(*}}t2D - && all(Tvn(T(0)) == t2D.Sample(samplerState, float2(u, u), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.Sample(samplerState, float2(u, u), int2(1, 1))) // WGSL: textureSample({{\(*}}t3D - && all(Tvn(T(0)) == t3D.Sample(samplerState, float3(u, u, u), int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.Sample(samplerState, float3(u, u, u), int3(1, 1, 1))) // WGSL doesn't support offset variant for cube and cube_array // WGSL: textureSample({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.Sample(samplerState, float3(u, u, 0), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.Sample(samplerState, float3(u, u, 0), int2(1, 1))) // Clamp variant // WGSL doesn't support clamp variants for `textureSample()` @@ -231,30 +229,30 @@ bool TEST_texture( // WGSL doesn't support Bias for 1D texture // WGSL: textureSampleBias({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleBias(samplerState, float2(u, u), float(-1))) + && all(Tvn(T.Element(0)) == t2D.SampleBias(samplerState, float2(u, u), float(-1))) // WGSL: textureSampleBias({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleBias(samplerState, float3(u, u, u), float(-1))) + && all(Tvn(T.Element(0)) == t3D.SampleBias(samplerState, float3(u, u, u), float(-1))) // WGSL: textureSampleBias({{\(*}}tCube - && all(Tvn(T(0)) == tCube.SampleBias(samplerState, normalize(float3(u, 1 - u, u)), float(-1))) + && all(Tvn(T.Element(0)) == tCube.SampleBias(samplerState, normalize(float3(u, 1 - u, u)), float(-1))) // WGSL: textureSampleBias({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleBias(samplerState, float3(u, u, 0), float(-1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleBias(samplerState, float3(u, u, 0), float(-1))) // WGSL: textureSampleBias({{\(*}}tCubeArray - && all(Tvn(T(0)) == tCubeArray.SampleBias(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), float(-1))) + && all(Tvn(T.Element(0)) == tCubeArray.SampleBias(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), float(-1))) // Offset variant // WGSL: textureSampleBias({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleBias(samplerState, float2(u, u), float(-1), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.SampleBias(samplerState, float2(u, u), float(-1), int2(1, 1))) // WGSL: textureSampleBias({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleBias(samplerState, float3(u, u, u), float(-1), int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.SampleBias(samplerState, float3(u, u, u), float(-1), int3(1, 1, 1))) // WGSL: textureSampleBias({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleBias(samplerState, float3(u, u, 0), float(-1), int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleBias(samplerState, float3(u, u, 0), float(-1), int2(1, 1))) // =================================== // T SampleLevel() @@ -264,30 +262,30 @@ bool TEST_texture( // WGSL doesn't support textureSampleLevel for 1D texture // WGSL: textureSampleLevel({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleLevel(samplerState, float2(u, u), 0)) + && all(Tvn(T.Element(0)) == t2D.SampleLevel(samplerState, float2(u, u), 0)) // WGSL: textureSampleLevel({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleLevel(samplerState, float3(u, u, u), 0)) + && all(Tvn(T.Element(0)) == t3D.SampleLevel(samplerState, float3(u, u, u), 0)) // WGSL: textureSampleLevel({{\(*}}tCube - && all(Tvn(T(0)) == tCube.SampleLevel(samplerState, normalize(float3(u, 1 - u, u)), 0)) + && all(Tvn(T.Element(0)) == tCube.SampleLevel(samplerState, normalize(float3(u, 1 - u, u)), 0)) // WGSL: textureSampleLevel({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleLevel(samplerState, float3(u, u, 0), 0)) + && all(Tvn(T.Element(0)) == t2DArray.SampleLevel(samplerState, float3(u, u, 0), 0)) // WGSL: textureSampleLevel({{\(*}}tCubeArray - && all(Tvn(T(0)) == tCubeArray.SampleLevel(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), 0)) + && all(Tvn(T.Element(0)) == tCubeArray.SampleLevel(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), 0)) // Offset variant // WGSL: textureSampleLevel({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleLevel(samplerState, float2(u, u), 0, int2(1, 1))) + && all(Tvn(T.Element(0)) == t2D.SampleLevel(samplerState, float2(u, u), 0, int2(1, 1))) // WGSL: textureSampleLevel({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleLevel(samplerState, float3(u, u, u), 0, int3(1, 1, 1))) + && all(Tvn(T.Element(0)) == t3D.SampleLevel(samplerState, float3(u, u, u), 0, int3(1, 1, 1))) // WGSL: textureSampleLevel({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleLevel(samplerState, float3(u, u, 0), 0, int2(1, 1))) + && all(Tvn(T.Element(0)) == t2DArray.SampleLevel(samplerState, float3(u, u, 0), 0, int2(1, 1))) // ================== // float SampleCmp() @@ -349,30 +347,30 @@ bool TEST_texture( // WGSL doesn't support textureSampleGrad for 1D textures // WGSL: textureSampleGrad({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleGrad(samplerState, float2(u, u), float2(ddx, ddx), float2(ddy, ddy))) + && all(Tvn(T.Element(0)) == t2D.SampleGrad(samplerState, float2(u, u), float2(ddx, ddx), float2(ddy, ddy))) // WGSL: textureSampleGrad({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleGrad(samplerState, float3(u, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) + && all(Tvn(T.Element(0)) == t3D.SampleGrad(samplerState, float3(u, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) // WGSL: textureSampleGrad({{\(*}}tCube - && all(Tvn(T(0)) == tCube.SampleGrad(samplerState, normalize(float3(u, 1 - u, u)), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) + && all(Tvn(T.Element(0)) == tCube.SampleGrad(samplerState, normalize(float3(u, 1 - u, u)), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) // WGSL: textureSampleGrad({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleGrad(samplerState, float3(u, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy))) + && all(Tvn(T.Element(0)) == t2DArray.SampleGrad(samplerState, float3(u, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy))) // WGSL: textureSampleGrad({{\(*}}tCubeArray - && all(Tvn(T(0)) == tCubeArray.SampleGrad(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) + && all(Tvn(T.Element(0)) == tCubeArray.SampleGrad(samplerState, float4(normalize(float3(u, 1 - u, u)), 0), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy))) // Offset variant // WGSL: textureSampleGrad({{\(*}}t2D - && all(Tvn(T(0)) == t2D.SampleGrad(samplerState, float2(u2, u), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) + && all(Tvn(T.Element(0)) == t2D.SampleGrad(samplerState, float2(u2, u), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) // WGSL: textureSampleGrad({{\(*}}t3D - && all(Tvn(T(0)) == t3D.SampleGrad(samplerState, float3(u2, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy), int3(0, 0, 0))) + && all(Tvn(T.Element(0)) == t3D.SampleGrad(samplerState, float3(u2, u, u), float3(ddx, ddx, ddx), float3(ddy, ddy, ddy), int3(0, 0, 0))) // WGSL: textureSampleGrad({{\(*}}t2DArray - && all(Tvn(T(0)) == t2DArray.SampleGrad(samplerState, float3(u2, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) + && all(Tvn(T.Element(0)) == t2DArray.SampleGrad(samplerState, float3(u2, u, 0.0f), float2(ddx, ddx), float2(ddy, ddy), int2(0, 0))) ; return result; @@ -381,7 +379,7 @@ bool TEST_texture( void fragMain() { bool result = true - && TEST_texture( + && TEST_texture( t1D_f32v3, t2D_f32v3, t3D_f32v3, @@ -389,7 +387,7 @@ void fragMain() t1DArray_f32v3, t2DArray_f32v3, tCubeArray_f32v3) - && TEST_texture( + && TEST_texture( t1D_f32v4, t2D_f32v4, t3D_f32v4, From d1a13a730406646029cedd018bb9806943209baa Mon Sep 17 00:00:00 2001 From: Yong He Date: Fri, 17 Jan 2025 14:43:31 -0800 Subject: [PATCH 20/23] Allow __subscript syntax. (#6124) * Allow __subscript syntax. * Fix. --- source/slang/slang-parser.cpp | 44 ++++++++++++------- .../generics/generic-subscript.slang | 24 ++++++++++ 2 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 tests/language-feature/generics/generic-subscript.slang diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 6ae41a2eb9..98cfd121c0 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -3932,28 +3932,38 @@ static void parseStorageDeclBody(Parser* parser, ContainerDecl* decl) static NodeBase* parseSubscriptDecl(Parser* parser, void* /*userData*/) { - SubscriptDecl* decl = parser->astBuilder->create(); - parser->FillPosition(decl); - parser->PushScope(decl); + return parseOptGenericDecl( + parser, + [&](GenericDecl* genericParent) + { + SubscriptDecl* decl = parser->astBuilder->create(); + parser->FillPosition(decl); + parser->PushScope(decl); - // TODO: the use of this name here is a bit magical... - decl->nameAndLoc.name = getName(parser, "operator[]"); + // TODO: the use of this name here is a bit magical... + decl->nameAndLoc.name = getName(parser, "operator[]"); - parseParameterList(parser, decl); + parseParameterList(parser, decl); - if (AdvanceIf(parser, TokenType::RightArrow)) - { - decl->returnType = parser->ParseTypeExp(); - } - else - { - decl->returnType.exp = parser->astBuilder->create(); - } + if (AdvanceIf(parser, TokenType::RightArrow)) + { + decl->returnType = parser->ParseTypeExp(); + } + else + { + decl->returnType.exp = parser->astBuilder->create(); + } - parseStorageDeclBody(parser, decl); + auto funcScope = parser->currentScope; + parser->PopScope(); + maybeParseGenericConstraints(parser, genericParent); + parser->PushScope(funcScope); - parser->PopScope(); - return decl; + parseStorageDeclBody(parser, decl); + + parser->PopScope(); + return decl; + }); } /// Peek in the token stream and return `true` if it looks like a modern-style variable declaration diff --git a/tests/language-feature/generics/generic-subscript.slang b/tests/language-feature/generics/generic-subscript.slang new file mode 100644 index 0000000000..87eec3045c --- /dev/null +++ b/tests/language-feature/generics/generic-subscript.slang @@ -0,0 +1,24 @@ +//TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK): -output-using-type + +//TEST_INPUT: set output = out ubuffer(data=[0 0 0 0], stride=4) +RWStructuredBuffer output; + +struct Tx +{ + float x; + __subscript(I index) -> float + where I:IInteger + { + get { return x + index.toInt(); } + set { x = newValue;} + } +} + +[numthreads(1,1,1)] +void computeMain() +{ + Tx obj; + obj[0] = 3.0; + // CHECK: 5.0 + output[0] = obj[2]; +} \ No newline at end of file From dc69d85f89e42eb2fe914e1105a8cbb68e9a8ca4 Mon Sep 17 00:00:00 2001 From: Yong He Date: Fri, 17 Jan 2025 14:51:51 -0800 Subject: [PATCH 21/23] Add groupshared atomic array test. (#6107) * Add groupshared atomic array test. * Fix test. --- tests/spirv/groupshared-array-atomic.slang | 19 +++++++++++++++++++ tools/render-test/render-test-main.cpp | 7 ++++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/spirv/groupshared-array-atomic.slang diff --git a/tests/spirv/groupshared-array-atomic.slang b/tests/spirv/groupshared-array-atomic.slang new file mode 100644 index 0000000000..242996dfa1 --- /dev/null +++ b/tests/spirv/groupshared-array-atomic.slang @@ -0,0 +1,19 @@ +//TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj -vk -emit-spirv-directly +groupshared Atomic values[10]; + +//TEST_INPUT: set outputAddr = out ubuffer(data=[0 0 0 0], stride=4) +uniform uint64_t outputAddr; + +[numthreads(4,1,1)] +void computeMain(int i : SV_DispatchThreadID) +{ + uint* output = (uint*)(outputAddr); + values[i] = 0; + values[i] += 1; + output[i] = values[i].load(); + + // CHECK: 1 + // CHECK: 1 + // CHECK: 1 + // CHECK: 1 +} \ No newline at end of file diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index 812a753e3d..648e5a4bd1 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -226,13 +226,18 @@ struct AssignValsFromLayoutContext device, bufferResource)); - if (dstCursor.getTypeLayout()->getType()->getKind() == slang::TypeReflection::Kind::Pointer) + if ((dstCursor.getTypeLayout()->getType()->getKind() == + slang::TypeReflection::Kind::Scalar && + dstCursor.getTypeLayout()->getType()->getScalarType() == + slang::TypeReflection::ScalarType::UInt64) || + dstCursor.getTypeLayout()->getType()->getKind() == slang::TypeReflection::Kind::Pointer) { // dstCursor is pointer to an ordinary uniform data field, // we should write bufferResource as a pointer. uint64_t addr = bufferResource->getDeviceAddress(); dstCursor.setData(&addr, sizeof(addr)); resourceContext.resources.add(ComPtr(bufferResource.get())); + maybeAddOutput(dstCursor, srcVal, bufferResource); return SLANG_OK; } From d046082c501d3501a24c143dff2cfa42939549b9 Mon Sep 17 00:00:00 2001 From: Yong He Date: Fri, 17 Jan 2025 14:52:28 -0800 Subject: [PATCH 22/23] Add diagnostic for function body followed by a `;`;. (#6122) --- source/slang/slang-diagnostic-defs.h | 6 ++- source/slang/slang-parser.cpp | 17 ++++++++- .../empty-struct-method.slang | 37 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/language-feature/empty-struct-method.slang diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 7eddc16a93..915ed9b425 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -487,7 +487,11 @@ DIAGNOSTIC( Warning, unintendedEmptyStatement, "potentially unintended empty statement at this location; use {} instead.") - +DIAGNOSTIC( + 20102, + Error, + unexpectedBodyAfterSemicolon, + "unexpected function body after signature declaration, is this ';' a typo?") DIAGNOSTIC(30102, Error, declNotAllowed, "$0 is not allowed here.") // 29xxx - Snippet parsing and inline asm diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 98cfd121c0..81619b7002 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -1802,9 +1802,17 @@ class ReplaceScopeVisitor : public ExprVisitor /// Parse an optional body statement for a declaration that can have a body. static Stmt* parseOptBody(Parser* parser) { - if (AdvanceIf(parser, TokenType::Semicolon)) + Token semiColonToken; + if (AdvanceIf(parser, TokenType::Semicolon, &semiColonToken)) { // empty body + // if we see a `{` after a `;`, it is very likely an user error to + // have the `;`, so we will provide a better diagnostic for it. + if (peekTokenType(parser) == TokenType::LBrace) + { + parser->sink->diagnose(semiColonToken.loc, Diagnostics::unexpectedBodyAfterSemicolon); + return parser->parseBlockStatement(); + } return nullptr; } else @@ -4841,6 +4849,13 @@ static DeclBase* ParseDeclWithModifiers( // We shouldn't be seeing an LBrace or an LParent when expecting a decl. // However recovery logic may lead us here. In this case we just // skip the whole `{}` block and return an empty decl. + if (!parser->isRecovering) + { + parser->sink->diagnose( + loc, + Diagnostics::unexpectedToken, + parser->tokenReader.peekToken()); + } SkipBalancedToken(&parser->tokenReader); decl = parser->astBuilder->create(); decl->loc = loc; diff --git a/tests/language-feature/empty-struct-method.slang b/tests/language-feature/empty-struct-method.slang new file mode 100644 index 0000000000..2467013ac3 --- /dev/null +++ b/tests/language-feature/empty-struct-method.slang @@ -0,0 +1,37 @@ +//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK):-target spirv + +struct Light +{ + float3 position; + float radius; + + float3 color; + float intensity; +}; + +[vk::binding(0, 0)] +StructuredBuffer globalLightList; + +struct Lighting +{ + //CHECK: ([[# @LINE+1]]): error 20102 + float3 DoLighting(Light light); + { + // Not emitted + return float3(1.0, 1.0, 1.0); + } +}; + +[shader("fragment")] +float4 fragment(float4 color: COLOR0) +{ + float4 albedo = color; + + if (albedo.a < 0.025) + discard; + + Lighting light = Lighting(); + albedo.xyz = light.DoLighting(globalLightList[0]); + + return albedo; +} \ No newline at end of file From 9d99976703872d7608415da5a90d070bd5dec5b7 Mon Sep 17 00:00:00 2001 From: Sai Praveen Bangaru <31557731+saipraveenb25@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:53:02 -0500 Subject: [PATCH 23/23] Fix circularity issue when checking multiple generic interface constraints. (#6121) * Fix circularity issue with checking multiple generic interface constraints * Create multi-generic-interface-constraint.slang * Update multi-generic-interface-constraint.slang * Update slang-check-inheritance.cpp --------- Co-authored-by: Yong He --- source/slang/slang-check-inheritance.cpp | 15 ++++++-- .../multi-generic-interface-constraint.slang | 36 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 tests/language-feature/generics/multi-generic-interface-constraint.slang diff --git a/source/slang/slang-check-inheritance.cpp b/source/slang/slang-check-inheritance.cpp index 7af2d2e811..b448588746 100644 --- a/source/slang/slang-check-inheritance.cpp +++ b/source/slang/slang-check-inheritance.cpp @@ -466,10 +466,14 @@ InheritanceInfo SharedSemanticsContext::_calcInheritanceInfo( if (constraintDeclRef.getDecl()->checkState.isBeingChecked()) continue; - ensureDecl(&visitor, constraintDeclRef.getDecl(), DeclCheckState::CanSpecializeGeneric); + ensureDecl(&visitor, constraintDeclRef.getDecl(), DeclCheckState::ScopesWired); - auto subType = getSub(astBuilder, constraintDeclRef); - auto superType = getSup(astBuilder, constraintDeclRef); + // Check only the sub-type. + visitor.CheckConstraintSubType(constraintDeclRef.getDecl()->sub); + auto sub = constraintDeclRef.getDecl()->sub; + if (!sub.type) + sub = visitor.TranslateTypeNodeForced(sub); + auto subType = constraintDeclRef.substitute(astBuilder, sub.type); // We only consider constraints where the type represented // by `declRef` is the subtype, since those @@ -489,6 +493,11 @@ InheritanceInfo SharedSemanticsContext::_calcInheritanceInfo( if (subDeclRefType->getDeclRef() != declRef) continue; + // Further check the constraint, since we now need the sup-type. + ensureDecl(&visitor, constraintDeclRef.getDecl(), DeclCheckState::CanSpecializeGeneric); + + auto superType = getSup(astBuilder, constraintDeclRef); + // Because the constraint is a declared inheritance relationship, // adding the base to our list of direct bases is as straightforward // as in all the preceding cases. diff --git a/tests/language-feature/generics/multi-generic-interface-constraint.slang b/tests/language-feature/generics/multi-generic-interface-constraint.slang new file mode 100644 index 0000000000..9c6918ed23 --- /dev/null +++ b/tests/language-feature/generics/multi-generic-interface-constraint.slang @@ -0,0 +1,36 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj -output-using-type +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -vk -shaderobj -output-using-type + +interface IFoo +{ + bool requirement(T v1); +} + +struct Bar, B : IFoo> +{ + [Differentiable] + T doThing(T a) + { + return a * T(2.0); + } +} + +struct Foo : IFoo +{ + bool requirement(float v) + { + return v > 0.5; + } +} + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +[numthreads(1, 1, 1)] +void computeMain(int3 dispatchThreadID : SV_DispatchThreadID) +{ + int tid = dispatchThreadID.x; + Bar obj; + outputBuffer[tid] = obj.doThing(2.0f); + // CHECK: 4 +}