diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/glsl.meta.slang | 9 | ||||
| -rw-r--r-- | source/slang/slang-ir-entry-point-uniforms.cpp | 105 | ||||
| -rw-r--r-- | source/slang/slang-ir-explicit-global-context.cpp | 6 | ||||
| -rw-r--r-- | source/slang/slang-ir-glsl-legalize.cpp | 36 | ||||
| -rw-r--r-- | source/slang/slang-ir-legalize-varying-params.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-ir-translate-global-varying-var.cpp | 169 |
6 files changed, 258 insertions, 71 deletions
diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang index 88c90a777..9b0a28a31 100644 --- a/source/slang/glsl.meta.slang +++ b/source/slang/glsl.meta.slang @@ -4764,7 +4764,12 @@ public property uint3 gl_LaunchSizeEXT } } -internal in int __gl_PrimitiveID : SV_PrimitiveID; +// While the glsl spec defines gl_PrimitiveID as an "in int", +// we need to use uint here and cast to an int below. This is due +// __gl_PrimitiveID being picked up as a global variable regardless +// of the stage used below. Using int __gl_PrimitiveID leads to a +// casting conflict due to the spirv implementation of PrimitiveIndex(). +internal in uint __gl_PrimitiveID : SV_PrimitiveID; public property int gl_PrimitiveID { @@ -4779,7 +4784,7 @@ public property int gl_PrimitiveID setupExtForRayTracingBuiltIn(); return PrimitiveIndex(); default: - return __gl_PrimitiveID; + return __intCast<int32_t>(__gl_PrimitiveID); } } } diff --git a/source/slang/slang-ir-entry-point-uniforms.cpp b/source/slang/slang-ir-entry-point-uniforms.cpp index 12dcd187a..c5e91c54d 100644 --- a/source/slang/slang-ir-entry-point-uniforms.cpp +++ b/source/slang/slang-ir-entry-point-uniforms.cpp @@ -101,43 +101,76 @@ namespace Slang // whether a parameter represents a varying input rather than // a uniform parameter. - -// In order to determine whether a parameter is varying based on its -// layout, we need to know which resource kinds represent varying -// shader parameters. +// Setup some flags that will be used below to check for varying +// resource kinds. Ordinary varying input/output + Ray-tracing shader +// input/output kinds will be considered varying. // -bool isVaryingResourceKind(LayoutResourceKind kind) +// Note: The set of cases that are considered +// varying here would need to be extended if we +// add more fine-grained resource kinds (e.g., +// if we ever add an explicit resource kind +// for geometry shader output streams). +static const LayoutResourceKindFlags flags = + LayoutResourceKindFlag::make(LayoutResourceKind::VaryingInput) | + LayoutResourceKindFlag::make(LayoutResourceKind::VaryingOutput) | + LayoutResourceKindFlag::make(LayoutResourceKind::CallablePayload) | + LayoutResourceKindFlag::make(LayoutResourceKind::HitAttributes) | + LayoutResourceKindFlag::make(LayoutResourceKind::RayPayload); + +bool isVaryingParameter(IRTypeLayout* typeLayout) { - switch (kind) + if (!typeLayout) + return false; + + // If we're looking at a struct type layout, we need to check each of the fields. + // If any of the fields is not a varying resource kind, then we consider the + // whole struct to be uniform (and thus not varying). + if (auto structTypeLayout = as<IRStructTypeLayout>(typeLayout)) + { + for (auto fieldAttr : structTypeLayout->getFieldLayoutAttrs()) + { + if (!isVaryingParameter(fieldAttr->getLayout())) + return false; + } + // If we made it here, all struct fields were varying. + return true; + } + + // If we're looking at a parameter group type layout, we should return false + // as these should always be associtated with LayoutResourceKind::Uniform. + else if (as<IRParameterGroupTypeLayout>(typeLayout)) { - default: return false; + } - // Note: The set of cases that are considered - // varying here would need to be extended if we - // add more fine-grained resource kinds (e.g., - // if we ever add an explicit resource kind - // for geometry shader output streams). - // - // Ordinary varying input/output: - case LayoutResourceKind::VaryingInput: - case LayoutResourceKind::VaryingOutput: - // - // Ray-tracing shader input/output: - case LayoutResourceKind::CallablePayload: - case LayoutResourceKind::HitAttributes: - case LayoutResourceKind::RayPayload: + // If we're looking at an array type layout, we need to check the element's + // type layout. + else if (auto arrayTypeLayout = as<IRArrayTypeLayout>(typeLayout)) + { + return isVaryingParameter(arrayTypeLayout->getElementTypeLayout()); + } + + // If we didn't match a type layout above, try finding a kind from sizeAttrs. + else + { + // If all the element type's sizeAttrs have a varying kind matching our + // flags above, return true. + for (auto sizeAttr : typeLayout->getSizeAttrs()) + { + if ((flags & LayoutResourceKindFlag::make(sizeAttr->getResourceKind())) == 0) + return false; + } return true; } } -bool isVaryingParameter(IRTypeLayout* typeLayout) +bool isVaryingParameter(IRVarLayout* varLayout) { // If *any* of the resources consumed by the parameter type // is *not* a varying resource kind, then we consider the // whole parameter to be uniform (and thus not varying). // - // Note that this means that an empty type will always + // Note that this means that some empty types will always // be considered varying, even if it had been explicitly // marked `uniform`. // @@ -149,19 +182,25 @@ bool isVaryingParameter(IRTypeLayout* typeLayout) // reosurce kind, so they show up as empty. Simply // adding `LayoutResourceKind`s for system-value inputs // and outputs would allow for simpler logic here. - // - for (auto sizeAttr : typeLayout->getSizeAttrs()) - { - if (!isVaryingResourceKind(sizeAttr->getResourceKind())) - return false; - } - return true; -} -bool isVaryingParameter(IRVarLayout* varLayout) -{ if (!varLayout) return false; + + // System-value parameters currently do not have kinds setup. + // As a WAR, if we see a system-value attr just assume varying + // for now. + if (varLayout->findAttr<IRSystemValueSemanticAttr>()) + { + return true; + } + + if (varLayout->usesResourceFromKinds(flags)) + { + return true; + } + + // If the base cases above failed, we need to check if we are dealing with + // an IRVarLayout that could have "nested" IRVarLayouts, such as an IRStructTypeLayout. return isVaryingParameter(varLayout->getTypeLayout()); } diff --git a/source/slang/slang-ir-explicit-global-context.cpp b/source/slang/slang-ir-explicit-global-context.cpp index f235ba3e4..9fae4ce15 100644 --- a/source/slang/slang-ir-explicit-global-context.cpp +++ b/source/slang/slang-ir-explicit-global-context.cpp @@ -661,6 +661,12 @@ struct IntroduceExplicitGlobalContextPass ptr = builder.emitLoad(ptr); use->set(ptr); } + + // We've replaced all uses of the global var so we should remove it. + // We leave decorations on the global var above, so if we do not remove it + // here, the global var will not be processed for elimination in + // eliminateDeadCode. + globalVar->removeAndDeallocate(); } IRInst* findOrCreateContextPtrForInst(IRInst* inst) diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index c9791880f..fa7e69fa4 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -3575,28 +3575,40 @@ void legalizeEntryPointParameterForGLSL( auto key = dec->getOperand(1); IRInst* realGlobalVar = nullptr; - if (globalValue.flavor != ScalarizedVal::Flavor::tuple) - continue; - if (auto tupleVal = as<ScalarizedTupleValImpl>(globalValue.impl)) + + // When we relate a "global variable" to a "global parameter" using + // kIROp_GlobalVariableShadowingGlobalParameterDecoration, the globalValue flavor + // is dependent on the global parameter's type. Struct types for example will relate + // to the tuple flavor, while vector types will be related to the address flavor. + if (globalValue.flavor == ScalarizedVal::Flavor::tuple) { - for (auto elem : tupleVal->elements) + if (auto tupleVal = as<ScalarizedTupleValImpl>(globalValue.impl)) { - if (elem.key == key) + for (auto elem : tupleVal->elements) { - realGlobalVar = elem.val.irValue; - if (!realGlobalVar && - ScalarizedVal::Flavor::typeAdapter == elem.val.flavor) + if (elem.key == key) { - if (auto typeAdapterVal = - as<ScalarizedTypeAdapterValImpl>(elem.val.impl)) + realGlobalVar = elem.val.irValue; + if (!realGlobalVar && + ScalarizedVal::Flavor::typeAdapter == elem.val.flavor) { - realGlobalVar = typeAdapterVal->val.irValue; + if (auto typeAdapterVal = + as<ScalarizedTypeAdapterValImpl>(elem.val.impl)) + { + realGlobalVar = typeAdapterVal->val.irValue; + } } + break; } - break; } } } + else if (globalValue.flavor == ScalarizedVal::Flavor::address) + { + realGlobalVar = globalValue.irValue; + } + else + continue; SLANG_ASSERT(realGlobalVar); // Remove all stores into the global var introduced during diff --git a/source/slang/slang-ir-legalize-varying-params.cpp b/source/slang/slang-ir-legalize-varying-params.cpp index f8a9839d2..e5b3de90f 100644 --- a/source/slang/slang-ir-legalize-varying-params.cpp +++ b/source/slang/slang-ir-legalize-varying-params.cpp @@ -1891,6 +1891,10 @@ private: structTypeLayout = as<IRStructTypeLayout>(varLayout->getTypeLayout()); Index fieldIndex = 0; List<IRInst*> fieldParams; + // TODO: We currently lose some decorations from the struct that should possibly be + // transfered + // to the new params here, like + // kIROp_GlobalVariableShadowingGlobalParameterDecoration. for (auto field : structType->getFields()) { auto fieldParam = builder.emitParam(field->getFieldType()); diff --git a/source/slang/slang-ir-translate-global-varying-var.cpp b/source/slang/slang-ir-translate-global-varying-var.cpp index 80f5c42c3..57b277d25 100644 --- a/source/slang/slang-ir-translate-global-varying-var.cpp +++ b/source/slang/slang-ir-translate-global-varying-var.cpp @@ -75,8 +75,80 @@ struct GlobalVarTranslationContext builder.setInsertBefore(entryPointFunc); auto inputStructType = builder.createStructType(); IRStructTypeLayout::Builder inputStructTypeLayoutBuilder(&builder); + + // Go through the existing parameter layouts and add them to the + // replacement struct type first. + // + // We'll also need to fix up the offsets of the global varing inputs + // being added if we have any varing input entry point params. Set + // these to some defaults for now. + UInt nextOffset = 0; + IRVarLayout* lastFieldVarLayout = nullptr; + + // If we have an existing entry point struct layout, we need to go through it and + // add any of the struct field layout attributes as fields in the new struct type + // layout that we are building to combine global varing ins and entry point ins. + IRLayoutDecoration* entryPointLayoutDecor = + entryPointFunc->findDecoration<IRLayoutDecoration>(); + IREntryPointLayout* entryPointLayout = nullptr; + IRStructTypeLayout* entryPointParamsStructLayout = nullptr; + + if (entryPointLayoutDecor) + { + entryPointLayout = as<IREntryPointLayout>(entryPointLayoutDecor->getLayout()); + if (entryPointLayout) + { + // The parameter layout for an entry point will either be a structure + // type layout, or a parameter group type layout. + entryPointParamsStructLayout = getScopeStructLayout(entryPointLayout); + if (entryPointParamsStructLayout) + { + for (auto attr : entryPointParamsStructLayout->getFieldLayoutAttrs()) + { + // Add existing parameter layouts to the replacement struct type layout. + IRVarLayout* fieldLayout = attr->getLayout(); + IRInst* key = attr->getFieldKey(); + inputStructTypeLayoutBuilder.addField(key, fieldLayout); + + // Save the last VaryingInput type. This will be used to help + // recalculate offsets for the global varying inputs. + if (fieldLayout->usesResourceKind(LayoutResourceKind::VaryingInput)) + lastFieldVarLayout = fieldLayout; + } + + // Calculate the nextOffset if necessary. If entry params had no varying + // inputs nextOffset will just be 0. + if (lastFieldVarLayout) + { + // Find the size and offset of the last varying "in" kind + // param in the entry point param struct. Adding these will give us + // the starting offset that should be used for the first global. + if (auto sizeAttr = lastFieldVarLayout->getTypeLayout()->findSizeAttr( + LayoutResourceKind::VaryingInput)) + { + size_t finiteSize = sizeAttr->getFiniteSize(); + if (auto offsetAttr = lastFieldVarLayout->findOffsetAttr( + LayoutResourceKind::VaryingInput)) + { + UInt lastFieldOffset = offsetAttr->getOffset(); + nextOffset = finiteSize + lastFieldOffset; + } + } + } + } + } + } + + // Add the global vars to the replacement struct and add new params to + // the entry point func based on the global input vars. + List<IRInst*> newParams; UInt inputVarIndex = 0; List<IRStructKey*> inputKeys; + + // Setup the location where we will insert the new params + auto firstBlock = entryPointFunc->getFirstBlock(); + builder.setInsertInto(firstBlock); + for (auto input : inputVars) { auto inputType = cast<IRPtrTypeBase>(input->getDataType())->getValueType(); @@ -118,14 +190,19 @@ struct GlobalVarTranslationContext LayoutResourceKind::VaryingInput, LayoutSize(1)); } + + // Start off the offset as nextOffset. If the global "in"s have existing + // offsetAttr's, we will add the offsets from those as well. + auto resInfo = + varLayoutBuilder.findOrAddResourceInfo(LayoutResourceKind::VaryingInput); + resInfo->offset = nextOffset; + if (auto layoutDecor = findVarLayout(input)) { if (auto offsetAttr = layoutDecor->findOffsetAttr(LayoutResourceKind::VaryingInput)) { - varLayoutBuilder - .findOrAddResourceInfo(LayoutResourceKind::VaryingInput) - ->offset = (UInt)offsetAttr->getOffset(); + resInfo->offset += (UInt)offsetAttr->getOffset(); } } if (entryPointDecor->getProfile().getStage() == Stage::Fragment) @@ -138,41 +215,60 @@ struct GlobalVarTranslationContext } inputVarIndex++; } - inputStructTypeLayoutBuilder.addField(key, varLayoutBuilder.build()); + auto varLayout = varLayoutBuilder.build(); + inputStructTypeLayoutBuilder.addField(key, varLayout); input->transferDecorationsTo(key); + + // Emit a new param here to represent the global input var. + auto inputParam = builder.emitParam( + builder.getPtrType(kIROp_ConstRefType, inputType, AddressSpace::Input)); + + // Copy the global input vars original decorations onto the new param. + // We need to do this to ensure that we can do things like get system + // value info in later passes like when we legalize entry points for WGSL. + IRCloneEnv cloneEnv; + cloneInstDecorationsAndChildren(&cloneEnv, builder.getModule(), key, inputParam); + + // Add the layout to the new param + builder.addLayoutDecoration(inputParam, varLayout); + + // Add the param to our list of new params. This will allow us + // to connect the old "global variable" to a "global parameter" + // later on. + newParams.add(inputParam); } + + // Build the new layout that will replace the old entryPointLayout. auto paramTypeLayout = inputStructTypeLayoutBuilder.build(); IRVarLayout::Builder paramVarLayoutBuilder(&builder, paramTypeLayout); paramLayout = paramVarLayoutBuilder.build(); - // Add an entry point parameter for all the inputs. - auto firstBlock = entryPointFunc->getFirstBlock(); - builder.setInsertInto(firstBlock); - auto inputParam = builder.emitParam( - builder.getPtrType(kIROp_ConstRefType, inputStructType, AddressSpace::Input)); - builder.addLayoutDecoration(inputParam, paramLayout); - // Initialize all global variables in the order of struct member declaration. for (Index i = inputVars.getCount() - 1; i >= 0; i--) { auto input = inputVars[i]; setInsertBeforeOrdinaryInst(&builder, firstBlock->getFirstOrdinaryInst()); - auto inputType = cast<IRPtrTypeBase>(input->getDataType())->getValueType(); + // TODO: Does the below TODO apply if we no longer use emitFieldExtract? +#if 0 // TODO: This could be more efficient as a Load(FieldAddress(inputParam, i)) // operation instead of a FieldExtract(Load(inputParam)). builder.emitStore( input, builder .emitFieldExtract(inputType, builder.emitLoad(inputParam), inputKeys[i])); - // Relate "global variable" to a "global parameter" for use later in compilation - // to resolve a "global variable" shadowing a "global parameter" relationship. +#endif + builder.emitStore(input, builder.emitLoad(newParams[i])); + + // Relate the old "global variable" to a "global parameter" for use later in + // compilation to resolve a "global variable" shadowing a "global parameter" + // relationship. builder.addGlobalVariableShadowingGlobalParameterDecoration( - inputParam, + newParams[i], input, inputKeys[i]); } - // For each entry point, introduce a new parameter to represent each input parameter, + // For each entry point, introduce a new parameter to represent each output parameter, // and return all outputs via a struct value. if (hasOutput) { @@ -256,18 +352,43 @@ struct GlobalVarTranslationContext } } } - if (auto entryPointLayoutDecor = entryPointFunc->findDecoration<IRLayoutDecoration>()) + if (entryPointLayout) { - if (auto entryPointLayout = - as<IREntryPointLayout>(entryPointLayoutDecor->getLayout())) + if (paramLayout) { - if (paramLayout) - builder.replaceOperand(entryPointLayout->getOperands(), paramLayout); - if (resultVarLayout) + // We try to respect the original entry point type layout when replacing it + // below. The IRParameterGroupTypeLayout type layout is used when there are + // things like uniform entry point params, otherwise the IRStructTypeLayout type + // layout will normally represent the entry point type layout. + if (auto paramGroupTypeLayout = + as<IRParameterGroupTypeLayout>(entryPointParamsStructLayout)) + { + // Build a new IRParameterGroupTypeLayout to replace the old one + // representing the entryPointLayout. The ContainerVarLayout shouldn't have + // changed, so resue that. Replace the rest with the new paramTypeLayout and + // paramLayout that we calculated above + IRParameterGroupTypeLayout::Builder paramGroupTypeLayoutBuilder(&builder); + paramGroupTypeLayoutBuilder.setContainerVarLayout( + paramGroupTypeLayout->getContainerVarLayout()); + paramGroupTypeLayoutBuilder.setElementVarLayout(paramLayout); + paramGroupTypeLayoutBuilder.setOffsetElementTypeLayout(paramTypeLayout); + builder.replaceOperand( + entryPointLayout->getParamsLayout()->getOperands(), + paramGroupTypeLayoutBuilder.build()); + } + else if (as<IRStructTypeLayout>(entryPointParamsStructLayout)) + { builder.replaceOperand( - entryPointLayout->getOperands() + 1, - resultVarLayout); + entryPointLayout->getParamsLayout()->getOperands(), + paramTypeLayout); + } + else + { + SLANG_UNEXPECTED("uhandled global-scope binding layout"); + } } + if (resultVarLayout) + builder.replaceOperand(entryPointLayout->getOperands() + 1, resultVarLayout); } // Update func type for the entry point. List<IRType*> paramTypes; |
