From 69c5af4455f0360c1e4fe68100b5fc44ab6a30ad Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 11 Jan 2018 17:11:51 +0100 Subject: [PATCH] [dxbc] Implemented append/consume functionality Nier will now render the bullets properly. --- src/dxbc/dxbc_compiler.cpp | 120 +++++++++++++++++++++++++++++++++---- src/dxbc/dxbc_compiler.h | 11 ++++ src/dxbc/dxbc_decoder.h | 1 + src/dxbc/dxbc_defs.cpp | 10 +++- src/dxbc/dxbc_defs.h | 1 + 5 files changed, 131 insertions(+), 12 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 3fc42dd69..1b17d4576 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -65,6 +65,9 @@ namespace dxvk { case DxbcInstClass::Atomic: return this->emitAtomic(ins); + case DxbcInstClass::AtomicCounter: + return this->emitAtomicCounter(ins); + case DxbcInstClass::Barrier: return this->emitBarrier(ins); @@ -718,6 +721,7 @@ namespace dxvk { uav.type = DxbcResourceType::Typed; uav.imageInfo = typeInfo; uav.varId = varId; + uav.ctrId = 0; uav.specId = specConstId; uav.sampledType = sampledType; uav.sampledTypeId = sampledTypeId; @@ -828,6 +832,7 @@ namespace dxvk { uav.type = resType; uav.imageInfo = typeInfo; uav.varId = varId; + uav.ctrId = 0; uav.specId = specConstId; uav.sampledType = sampledType; uav.sampledTypeId = sampledTypeId; @@ -958,7 +963,49 @@ namespace dxvk { m_module.setLocalSize(m_entryPointId, ins.imm[0].u32, ins.imm[1].u32, ins.imm[2].u32); } + + + uint32_t DxbcCompiler::emitDclUavCounter(uint32_t regId) { + // Declare a structure type which holds the UAV counter + if (m_uavCtrStructType == 0) { + const uint32_t t_u32 = m_module.defIntType(32, 0); + const uint32_t t_struct = m_module.defStructTypeUnique(1, &t_u32); + + m_module.decorate(t_struct, spv::DecorationBufferBlock); + m_module.memberDecorateOffset(t_struct, 0, 0); + + m_module.setDebugName (t_struct, "uav_meta"); + m_module.setDebugMemberName(t_struct, 0, "ctr"); + + m_uavCtrStructType = t_struct; + m_uavCtrPointerType = m_module.defPointerType( + t_struct, spv::StorageClassUniform); + } + // Declare the buffer variable + const uint32_t varId = m_module.newVar( + m_uavCtrPointerType, spv::StorageClassUniform); + + m_module.setDebugName(varId, + str::format("u", regId, "_meta").c_str()); + + const uint32_t bindingId = computeResourceSlotId( + m_version.type(), DxbcBindingType::UavCounter, + regId); + + m_module.decorateDescriptorSet(varId, 0); + m_module.decorateBinding(varId, bindingId); + + // Declare the storage buffer binding + DxvkResourceSlot resource; + resource.slot = bindingId; + resource.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM; + m_resourceSlots.push_back(resource); + + return varId; + } + void DxbcCompiler::emitDclImmediateConstantBuffer(const DxbcShaderInstruction& ins) { if (m_immConstBuf != 0) @@ -1543,16 +1590,8 @@ namespace dxvk { // (dst0) The destination register // (src0) The register to shift // (src1) The shift amount (scalar) - const DxbcRegisterValue shiftReg = emitRegisterLoad( - ins.src[0], ins.dst[0].mask); - - DxbcRegisterValue countReg = emitRegisterLoad( - ins.src[1], DxbcRegMask(true, false, false, false)); - - // Unlike in DXBC, SPIR-V shift operations allow different - // shift amounts per component, so we'll extend the count - // register to a vector. - countReg = emitRegisterExtend(countReg, shiftReg.type.ccount); + const DxbcRegisterValue shiftReg = emitRegisterLoad(ins.src[0], ins.dst[0].mask); + const DxbcRegisterValue countReg = emitRegisterLoad(ins.src[1], ins.dst[0].mask); DxbcRegisterValue result; result.type.ctype = ins.dst[0].dataType; @@ -1756,6 +1795,67 @@ namespace dxvk { } + void DxbcCompiler::emitAtomicCounter(const DxbcShaderInstruction& ins) { + // imm_atomic_alloc and imm_atomic_consume have the following operands: + // (dst0) The register that will hold the old counter value + // (dst1) The UAV whose counter is going to be modified + // TODO check if the corresponding UAV is bound + const uint32_t registerId = ins.dst[1].idx[0].offset; + + if (m_uavs.at(registerId).ctrId == 0) + m_uavs.at(registerId).ctrId = emitDclUavCounter(registerId); + + // Get a pointer to the atomic counter in question + DxbcRegisterInfo ptrType; + ptrType.type.ctype = DxbcScalarType::Uint32; + ptrType.type.ccount = 1; + ptrType.type.alength = 0; + ptrType.sclass = spv::StorageClassUniform; + + const uint32_t zeroId = m_module.consti32(0); + const uint32_t ptrId = m_module.opAccessChain( + getPointerTypeId(ptrType), + m_uavs.at(registerId).ctrId, + 1, &zeroId); + + // Define memory scope and semantics based on the operands + uint32_t scope = spv::ScopeDevice; + uint32_t semantics = spv::MemorySemanticsUniformMemoryMask + | spv::MemorySemanticsAcquireReleaseMask; + + const uint32_t scopeId = m_module.constu32(scope); + const uint32_t semanticsId = m_module.constu32(semantics); + + // Compute the result value + DxbcRegisterValue value; + value.type.ctype = DxbcScalarType::Uint32; + value.type.ccount = 1; + + const uint32_t typeId = getVectorTypeId(value.type); + + switch (ins.op) { + case DxbcOpcode::ImmAtomicAlloc: + value.id = m_module.opAtomicIAdd(typeId, ptrId, + scopeId, semanticsId, m_module.constu32(1)); + break; + + case DxbcOpcode::ImmAtomicConsume: + value.id = m_module.opAtomicISub(typeId, ptrId, + scopeId, semanticsId, m_module.constu32(1)); + break; + + default: + Logger::warn(str::format( + "DxbcCompiler: Unhandled instruction: ", + ins.op)); + return; + } + + // Store the result + emitRegisterStore(ins.dst[0], value); + } + + void DxbcCompiler::emitBarrier(const DxbcShaderInstruction& ins) { // sync takes no operands. Instead, the synchronization // scope is defined by the operand control bits. diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 6bbbcbc60..1af4a4cc4 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -300,6 +300,11 @@ namespace dxvk { // an array of four-component uint32 vectors. uint32_t m_immConstBuf = 0; + //////////////////////////////////////////// + // Struct type used for UAV counter buffers + uint32_t m_uavCtrStructType = 0; + uint32_t m_uavCtrPointerType = 0; + /////////////////////////////////////////////////// // Entry point description - we'll need to declare // the function ID and all input/output variables. @@ -371,6 +376,9 @@ namespace dxvk { void emitDclThreadGroup( const DxbcShaderInstruction& ins); + uint32_t emitDclUavCounter( + uint32_t regId); + //////////////////////// // Custom data handlers void emitDclImmediateConstantBuffer( @@ -414,6 +422,9 @@ namespace dxvk { void emitAtomic( const DxbcShaderInstruction& ins); + void emitAtomicCounter( + const DxbcShaderInstruction& ins); + void emitBarrier( const DxbcShaderInstruction& ins); diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index dffc87f4c..131ff729c 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -93,6 +93,7 @@ namespace dxvk { DxbcResourceType type = DxbcResourceType::Typed; DxbcImageInfo imageInfo; uint32_t varId = 0; + uint32_t ctrId = 0; uint32_t specId = 0; DxbcScalarType sampledType = DxbcScalarType::Float32; uint32_t sampledTypeId = 0; diff --git a/src/dxbc/dxbc_defs.cpp b/src/dxbc/dxbc_defs.cpp index bb5f4d894..a7038f190 100644 --- a/src/dxbc/dxbc_defs.cpp +++ b/src/dxbc/dxbc_defs.cpp @@ -792,9 +792,15 @@ namespace dxvk { { DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 }, } }, /* ImmAtomicAlloc */ - { }, + { 2, DxbcInstClass::AtomicCounter, { + { DxbcOperandKind::DstReg, DxbcScalarType::Uint32 }, + { DxbcOperandKind::DstReg, DxbcScalarType::Uint32 }, + } }, /* ImmAtomicConsume */ - { }, + { 2, DxbcInstClass::AtomicCounter, { + { DxbcOperandKind::DstReg, DxbcScalarType::Uint32 }, + { DxbcOperandKind::DstReg, DxbcScalarType::Uint32 }, + } }, /* ImmAtomicIAdd */ { 4, DxbcInstClass::Atomic, { { DxbcOperandKind::DstReg, DxbcScalarType::Uint32 }, diff --git a/src/dxbc/dxbc_defs.h b/src/dxbc/dxbc_defs.h index abebfdf29..99bae6c41 100644 --- a/src/dxbc/dxbc_defs.h +++ b/src/dxbc/dxbc_defs.h @@ -33,6 +33,7 @@ namespace dxvk { ControlFlow, ///< Control flow instructions GeometryEmit, ///< Special geometry shader instructions Atomic, ///< Atomic operations + AtomicCounter, ///< Atomic counter operations Barrier, ///< Execution or memory barrier BitExtract, ///< Bit field extract operations BitInsert, ///< Bit field insert operations