diff options
Diffstat (limited to 'source/slang/slang-ir-link.cpp')
| -rw-r--r-- | source/slang/slang-ir-link.cpp | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 18cb850c0..a74c0c8f2 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -8,6 +8,7 @@ #include "slang-ir-string-hash.h" #include "slang-ir-autodiff.h" #include "slang-ir-specialize-target-switch.h" +#include "slang-ir-layout.h" #include "slang-module-library.h" #include "../core/slang-performance-profiler.h" @@ -1520,6 +1521,138 @@ static void diagnoseUnresolvedSymbols(TargetRequest* req, DiagnosticSink* sink, } } +void convertAtomicToStorageBuffer( + IRSpecContext* context, + Dictionary<int, List<IRInst*>>& bindingToInstMapUnsorted) +{ + // Atomic_uint definitions needs to become a storage buffer to follow GL_EXT_vulkan_glsl_relaxed + // and to allow translation of atomic_uint into SPIRV + + IRBuilder builder = *context->builder; + + for (auto& bindingToInstList : bindingToInstMapUnsorted) + { + int64_t maxOffset = 0; + for (auto& i : bindingToInstList.second) + { + int64_t currOffset = int64_t(i->findDecoration<IRGLSLOffsetDecoration>()->getOffset()->getValue()); + maxOffset = (maxOffset < currOffset) ? currOffset : maxOffset; + } + auto instToSwitch = *bindingToInstList.second.begin(); + builder.setInsertBefore(instToSwitch); + + auto elementType = builder.getArrayType( + builder.getUIntType(), + builder.getIntValue(builder.getUIntType(), (maxOffset / sizeof(uint32_t))+1) + ); + + StringBuilder nameStruct; + nameStruct << "atomic_uints"; + nameStruct << bindingToInstList.first; + nameStruct << "_t"; + nameStruct << "_paramGroup"; + auto structType = builder.createStructType(); + builder.addNameHintDecoration(structType, nameStruct.produceString().getUnownedSlice()); + + auto elementBufferKey = builder.createStructKey(); + builder.addNameHintDecoration(elementBufferKey, UnownedStringSlice("_data")); + auto elementBufferType = elementType; + auto _dataField = builder.createStructField(structType, elementBufferKey, elementBufferType); + + auto std430 = builder._createInst(sizeof(IRTypeLayoutRules), builder.getType(kIROp_Std430BufferLayoutType), kIROp_Std430BufferLayoutType); + IRGLSLShaderStorageBufferType* storageBuffer; + { + IRInst* ops[] = { structType, std430 }; + storageBuffer = builder.createGLSLShaderStorableBufferType(2, ops); + } + + instToSwitch->setFullType(storageBuffer); + + // All references to a atomic_uint need to be an element ref. to emulate storage buffer usage + // All function calls must be inlined since storage buffers cannot pass as parameters to atomic methods + for (auto& i : bindingToInstList.second) + { + int64_t currOffset = int64_t(i->findDecoration<IRGLSLOffsetDecoration>()->getOffset()->getValue()); + + // we need a next node to be stored since the following code + // changes IRUse* of the use->user node, meaning we will lose + // our IRUse list of the atomic_uint being swapped out + IRUse* next = nullptr; + for (auto use = i->firstUse; use; use = next) + { + next = use->nextUse; + auto user = use->user; + + switch (user->getOp()) + { + case kIROp_StructFieldLayoutAttr: + { + // Definitions do nothing if unused + break; + } + case kIROp_Call: + { + builder.setInsertBefore(user); + auto fieldAddress = builder.emitFieldAddress( + builder.getPtrType(_dataField->getFieldType()), + instToSwitch, + _dataField->getKey() + ); + auto elementAddr = builder.emitElementAddress( + builder.getPtrType(builder.getUIntType()), + fieldAddress, + builder.getIntValue(builder.getIntType(), currOffset/4)); + + user->setOperand(1, elementAddr); + auto funcTypeInst = (user->getOperand(0)); + auto funcType = funcTypeInst->getFullType(); + + auto paramReplacment = builder.getInOutType(builder.getUIntType()); + funcType->getOperand(1)->replaceUsesWith(paramReplacment); + builder.addForceInlineDecoration(funcTypeInst); + + break; + } + } + } + if (i->typeUse.usedValue->getOp() == kIROp_GLSLAtomicUintType) + { + i->removeAndDeallocate(); + } + } + } +} + +void GLSLReplaceAtomicUint(IRSpecContext* context, TargetProgram* targetProgram, IRModule* irModule) +{ + if (!targetProgram->getOptionSet().getBoolOption(CompilerOptionName::AllowGLSL)) return; + + Dictionary<int, List<IRInst*>> bindingToInstMapUnsorted; + for (auto inst : irModule->getGlobalInsts()) + { + if (inst->typeUse.usedValue) + { + switch (inst->typeUse.usedValue->getOp()) + { + case kIROp_GLSLAtomicUintType: + { + // atomic_uint are supported by GLSL->VK through converting to a different type (GL_EXT_vulkan_glsl_relaxed). + // atomic_uint are not supported by SPIR-V->VK; this means that to get SPIR-V to work we must convert the type ourselves + // to an equivlent representation (storage buffer); the added benifit is that then HLSL is possible to emit as a target as well + // since atomic_uint is not an HLSL concept, but storageBuffer->RWBuffer is and HLSL concept + auto layout = inst->findDecoration<IRLayoutDecoration>()->getLayout(); + auto layoutVal = as<IRVarOffsetAttr>(layout->getOperand(1)); + assert(layoutVal != nullptr); + bindingToInstMapUnsorted.getOrAddValue(uint32_t(layoutVal->getOffset()), List<IRInst*>()).add(inst); + break; + } + }; + } + } + + convertAtomicToStorageBuffer(context, bindingToInstMapUnsorted); +} + LinkedIR linkIR( CodeGenContext* codeGenContext) { @@ -1728,6 +1861,11 @@ LinkedIR linkIR( // definition. diagnoseUnresolvedSymbols(targetReq, codeGenContext->getSink(), state->irModule); + // type-use reformatter of GLSL types (only if compiler is set to AllowGLSL mode) + // which are not supported by SPIRV->Vulkan but is supported by GLSL->Vulkan through + // compiler magic tricks + GLSLReplaceAtomicUint(context, targetProgram, state->irModule); + // TODO: *technically* we should consider the case where // we have global variables with initializers, since // these should get run whether or not the entry point |
