[dxbc] Enable Vulkan memory model

This commit is contained in:
Philip Rebohle 2022-09-08 23:59:06 +02:00
parent 68cfc84329
commit 8176101228
3 changed files with 112 additions and 33 deletions

View File

@ -50,9 +50,12 @@ namespace dxvk {
} }
// Set the memory model. This is the same for all shaders. // Set the memory model. This is the same for all shaders.
m_module.enableCapability(
spv::CapabilityVulkanMemoryModel);
m_module.setMemoryModel( m_module.setMemoryModel(
spv::AddressingModelLogical, spv::AddressingModelLogical,
spv::MemoryModelGLSL450); spv::MemoryModelVulkan);
// Make sure our interface registers are clear // Make sure our interface registers are clear
for (uint32_t i = 0; i < DxbcMaxInterfaceRegs; i++) { for (uint32_t i = 0; i < DxbcMaxInterfaceRegs; i++) {
@ -1013,9 +1016,6 @@ namespace dxvk {
m_module.decorateDescriptorSet(varId, 0); m_module.decorateDescriptorSet(varId, 0);
m_module.decorateBinding(varId, bindingId); m_module.decorateBinding(varId, bindingId);
if (ins.controls.uavFlags().test(DxbcUavFlag::GloballyCoherent))
m_module.decorate(varId, spv::DecorationCoherent);
// Declare a specialization constant which will // Declare a specialization constant which will
// store whether or not the resource is bound. // store whether or not the resource is bound.
if (isUav) { if (isUav) {
@ -1028,6 +1028,7 @@ namespace dxvk {
uav.sampledTypeId = sampledTypeId; uav.sampledTypeId = sampledTypeId;
uav.imageTypeId = imageTypeId; uav.imageTypeId = imageTypeId;
uav.structStride = 0; uav.structStride = 0;
uav.coherence = getUavCoherence(registerId, ins.controls.uavFlags());
uav.isRawSsbo = false; uav.isRawSsbo = false;
m_uavs.at(registerId) = uav; m_uavs.at(registerId) = uav;
} else { } else {
@ -1168,9 +1169,6 @@ namespace dxvk {
m_module.decorateDescriptorSet(varId, 0); m_module.decorateDescriptorSet(varId, 0);
m_module.decorateBinding(varId, bindingId); m_module.decorateBinding(varId, bindingId);
if (ins.controls.uavFlags().test(DxbcUavFlag::GloballyCoherent))
m_module.decorate(varId, spv::DecorationCoherent);
if (isUav) { if (isUav) {
DxbcUav uav; DxbcUav uav;
uav.type = resType; uav.type = resType;
@ -1181,6 +1179,7 @@ namespace dxvk {
uav.sampledTypeId = sampledTypeId; uav.sampledTypeId = sampledTypeId;
uav.imageTypeId = resTypeId; uav.imageTypeId = resTypeId;
uav.structStride = resStride; uav.structStride = resStride;
uav.coherence = getUavCoherence(registerId, ins.controls.uavFlags());
uav.isRawSsbo = useRawSsbo; uav.isRawSsbo = useRawSsbo;
m_uavs.at(registerId) = uav; m_uavs.at(registerId) = uav;
} else { } else {
@ -2323,7 +2322,7 @@ namespace dxvk {
uint32_t semantics = 0; uint32_t semantics = 0;
if (isUav) { if (isUav) {
scope = spv::ScopeDevice; scope = spv::ScopeQueueFamily;
semantics = spv::MemorySemanticsAcquireReleaseMask; semantics = spv::MemorySemanticsAcquireReleaseMask;
semantics |= isSsbo semantics |= isSsbo
@ -2334,7 +2333,7 @@ namespace dxvk {
semantics = spv::MemorySemanticsWorkgroupMemoryMask semantics = spv::MemorySemanticsWorkgroupMemoryMask
| spv::MemorySemanticsAcquireReleaseMask; | spv::MemorySemanticsAcquireReleaseMask;
} }
const uint32_t scopeId = m_module.constu32(scope); const uint32_t scopeId = m_module.constu32(scope);
const uint32_t semanticsId = m_module.constu32(semantics); const uint32_t semanticsId = m_module.constu32(semantics);
@ -2506,7 +2505,7 @@ namespace dxvk {
1, &zeroId); 1, &zeroId);
// Define memory scope and semantics based on the operands // Define memory scope and semantics based on the operands
uint32_t scope = spv::ScopeDevice; uint32_t scope = spv::ScopeQueueFamily;
uint32_t semantics = spv::MemorySemanticsUniformMemoryMask uint32_t semantics = spv::MemorySemanticsUniformMemoryMask
| spv::MemorySemanticsAcquireReleaseMask; | spv::MemorySemanticsAcquireReleaseMask;
@ -2579,7 +2578,9 @@ namespace dxvk {
if (flags.test(DxbcSyncFlag::ThreadGroupSharedMemory)) { if (flags.test(DxbcSyncFlag::ThreadGroupSharedMemory)) {
memoryScope = spv::ScopeWorkgroup; memoryScope = spv::ScopeWorkgroup;
memorySemantics |= spv::MemorySemanticsWorkgroupMemoryMask memorySemantics |= spv::MemorySemanticsWorkgroupMemoryMask
| spv::MemorySemanticsAcquireReleaseMask; | spv::MemorySemanticsAcquireReleaseMask
| spv::MemorySemanticsMakeAvailableMask
| spv::MemorySemanticsMakeVisibleMask;
} }
if (flags.test(DxbcSyncFlag::UavMemoryGroup)) { if (flags.test(DxbcSyncFlag::UavMemoryGroup)) {
@ -2590,7 +2591,7 @@ namespace dxvk {
} }
if (flags.test(DxbcSyncFlag::UavMemoryGlobal)) { if (flags.test(DxbcSyncFlag::UavMemoryGlobal)) {
memoryScope = spv::ScopeDevice; memoryScope = spv::ScopeQueueFamily;
memorySemantics |= spv::MemorySemanticsImageMemoryMask memorySemantics |= spv::MemorySemanticsImageMemoryMask
| spv::MemorySemanticsUniformMemoryMask | spv::MemorySemanticsUniformMemoryMask
| spv::MemorySemanticsAcquireReleaseMask; | spv::MemorySemanticsAcquireReleaseMask;
@ -3774,6 +3775,12 @@ namespace dxvk {
SpirvImageOperands imageOperands; SpirvImageOperands imageOperands;
imageOperands.sparse = ins.dstCount == 2; imageOperands.sparse = ins.dstCount == 2;
if (uavInfo.coherence) {
imageOperands.flags |= spv::ImageOperandsNonPrivateTexelMask
| spv::ImageOperandsMakeTexelVisibleMask;
imageOperands.makeVisible = m_module.constu32(uavInfo.coherence);
}
DxbcVectorType texelType; DxbcVectorType texelType;
texelType.ctype = uavInfo.sampledType; texelType.ctype = uavInfo.sampledType;
texelType.ccount = 4; texelType.ccount = 4;
@ -3813,7 +3820,16 @@ namespace dxvk {
// (src0) The texture or buffer coordinates // (src0) The texture or buffer coordinates
// (src1) The value to store // (src1) The value to store
const DxbcBufferInfo uavInfo = getBufferInfo(ins.dst[0]); const DxbcBufferInfo uavInfo = getBufferInfo(ins.dst[0]);
// Set image operands for coherent access if necessary
SpirvImageOperands imageOperands;
if (uavInfo.coherence) {
imageOperands.flags |= spv::ImageOperandsNonPrivateTexelMask
| spv::ImageOperandsMakeTexelAvailableMask;
imageOperands.makeAvailable = m_module.constu32(uavInfo.coherence);
}
// Load texture coordinates // Load texture coordinates
DxbcRegisterValue texCoord = emitLoadTexCoord(ins.src[0], uavInfo.image); DxbcRegisterValue texCoord = emitLoadTexCoord(ins.src[0], uavInfo.image);
@ -3826,7 +3842,7 @@ namespace dxvk {
// Write the given value to the image // Write the given value to the image
m_module.opImageWrite( m_module.opImageWrite(
m_module.opLoad(uavInfo.typeId, uavInfo.varId), m_module.opLoad(uavInfo.typeId, uavInfo.varId),
texCoord.id, texValue.id, SpirvImageOperands()); texCoord.id, texValue.id, imageOperands);
} }
@ -5170,9 +5186,23 @@ namespace dxvk {
// The sparse feedback ID will be non-zero for sparse // The sparse feedback ID will be non-zero for sparse
// instructions on input. We need to reset it to 0. // instructions on input. We need to reset it to 0.
SpirvMemoryOperands memoryOperands;
SpirvImageOperands imageOperands; SpirvImageOperands imageOperands;
imageOperands.sparse = sparseFeedbackId != 0; imageOperands.sparse = sparseFeedbackId != 0;
if (isTgsm)
memoryOperands.flags = spv::MemoryAccessNonPrivatePointerMask;
if (bufferInfo.coherence) {
memoryOperands.flags = spv::MemoryAccessNonPrivatePointerMask
| spv::MemoryAccessMakePointerVisibleMask;
memoryOperands.makeVisible = m_module.constu32(bufferInfo.coherence);
imageOperands.flags = spv::ImageOperandsNonPrivateTexelMask
| spv::ImageOperandsMakeTexelVisibleMask;
imageOperands.makeVisible = m_module.constu32(bufferInfo.coherence);
}
sparseFeedbackId = 0; sparseFeedbackId = 0;
for (uint32_t i = 0; i < 4; i++) { for (uint32_t i = 0; i < 4; i++) {
@ -5192,12 +5222,14 @@ namespace dxvk {
if (isTgsm) { if (isTgsm) {
ccomps[sindex] = m_module.opLoad(scalarTypeId, ccomps[sindex] = m_module.opLoad(scalarTypeId,
m_module.opAccessChain(bufferInfo.typeId, m_module.opAccessChain(bufferInfo.typeId,
bufferInfo.varId, 1, &elementIndexAdjusted)); bufferInfo.varId, 1, &elementIndexAdjusted),
memoryOperands);
} else if (isSsbo) { } else if (isSsbo) {
uint32_t indices[2] = { m_module.constu32(0), elementIndexAdjusted }; uint32_t indices[2] = { m_module.constu32(0), elementIndexAdjusted };
ccomps[sindex] = m_module.opLoad(scalarTypeId, ccomps[sindex] = m_module.opLoad(scalarTypeId,
m_module.opAccessChain(bufferInfo.typeId, m_module.opAccessChain(bufferInfo.typeId,
bufferInfo.varId, 2, indices)); bufferInfo.varId, 2, indices),
memoryOperands);
} else { } else {
uint32_t resultTypeId = vectorTypeId; uint32_t resultTypeId = vectorTypeId;
uint32_t resultId = 0; uint32_t resultId = 0;
@ -5271,7 +5303,24 @@ namespace dxvk {
uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 }); uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 });
uint32_t srcComponentIndex = 0; uint32_t srcComponentIndex = 0;
// Set memory operands according to resource properties
SpirvMemoryOperands memoryOperands;
SpirvImageOperands imageOperands;
if (isTgsm)
memoryOperands.flags = spv::MemoryAccessNonPrivatePointerMask;
if (bufferInfo.coherence) {
memoryOperands.flags = spv::MemoryAccessNonPrivatePointerMask
| spv::MemoryAccessMakePointerAvailableMask;
memoryOperands.makeAvailable = m_module.constu32(bufferInfo.coherence);
imageOperands.flags = spv::ImageOperandsNonPrivateTexelMask
| spv::ImageOperandsMakeTexelAvailableMask;
imageOperands.makeAvailable = m_module.constu32(bufferInfo.coherence);
}
for (uint32_t i = 0; i < 4; i++) { for (uint32_t i = 0; i < 4; i++) {
if (operand.mask[i]) { if (operand.mask[i]) {
uint32_t srcComponentId = value.type.ccount > 1 uint32_t srcComponentId = value.type.ccount > 1
@ -5289,13 +5338,13 @@ namespace dxvk {
m_module.opStore( m_module.opStore(
m_module.opAccessChain(bufferInfo.typeId, m_module.opAccessChain(bufferInfo.typeId,
bufferInfo.varId, 1, &elementIndexAdjusted), bufferInfo.varId, 1, &elementIndexAdjusted),
srcComponentId); srcComponentId, memoryOperands);
} else if (isSsbo) { } else if (isSsbo) {
uint32_t indices[2] = { m_module.constu32(0), elementIndexAdjusted }; uint32_t indices[2] = { m_module.constu32(0), elementIndexAdjusted };
m_module.opStore( m_module.opStore(
m_module.opAccessChain(bufferInfo.typeId, m_module.opAccessChain(bufferInfo.typeId,
bufferInfo.varId, 2, indices), bufferInfo.varId, 2, indices),
srcComponentId); srcComponentId, memoryOperands);
} else if (operand.type == DxbcOperandType::UnorderedAccessView) { } else if (operand.type == DxbcOperandType::UnorderedAccessView) {
const std::array<uint32_t, 4> srcVectorIds = { const std::array<uint32_t, 4> srcVectorIds = {
srcComponentId, srcComponentId, srcComponentId, srcComponentId,
@ -5306,7 +5355,7 @@ namespace dxvk {
bufferId, elementIndexAdjusted, bufferId, elementIndexAdjusted,
m_module.opCompositeConstruct(vectorTypeId, m_module.opCompositeConstruct(vectorTypeId,
4, srcVectorIds.data()), 4, srcVectorIds.data()),
SpirvImageOperands()); imageOperands);
} else { } else {
throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw store"); throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw store");
} }
@ -5315,15 +5364,6 @@ namespace dxvk {
srcComponentIndex += 1; srcComponentIndex += 1;
} }
} }
// Make sure that shared memory stores are made visible in
// case the game does not synchronize invocations properly
if (isTgsm && m_moduleInfo.options.forceTgsmBarriers) {
m_module.opMemoryBarrier(
m_module.constu32(spv::ScopeWorkgroup),
m_module.constu32(spv::MemorySemanticsWorkgroupMemoryMask
| spv::MemorySemanticsAcquireReleaseMask));
}
} }
@ -5949,6 +5989,11 @@ namespace dxvk {
void DxbcCompiler::emitInitWorkgroupMemory() { void DxbcCompiler::emitInitWorkgroupMemory() {
bool hasTgsm = false; bool hasTgsm = false;
SpirvMemoryOperands memoryOperands;
memoryOperands.flags = spv::MemoryAccessNonPrivatePointerMask
| spv::MemoryAccessMakePointerAvailableMask;
memoryOperands.makeAvailable = m_module.constu32(spv::ScopeWorkgroup);
for (uint32_t i = 0; i < m_gRegs.size(); i++) { for (uint32_t i = 0; i < m_gRegs.size(); i++) {
if (!m_gRegs[i].varId) if (!m_gRegs[i].varId)
continue; continue;
@ -5989,7 +6034,7 @@ namespace dxvk {
uint32_t ptrId = m_module.opAccessChain( uint32_t ptrId = m_module.opAccessChain(
ptrTypeId, m_gRegs[i].varId, 1, &ofsId); ptrTypeId, m_gRegs[i].varId, 1, &ofsId);
m_module.opStore(ptrId, zeroId); m_module.opStore(ptrId, zeroId, memoryOperands);
} }
if (numElementsRemaining) { if (numElementsRemaining) {
@ -6013,7 +6058,7 @@ namespace dxvk {
uint32_t ptrId = m_module.opAccessChain( uint32_t ptrId = m_module.opAccessChain(
ptrTypeId, m_gRegs[i].varId, 1, &ofsId); ptrTypeId, m_gRegs[i].varId, 1, &ofsId);
m_module.opStore(ptrId, zeroId); m_module.opStore(ptrId, zeroId, memoryOperands);
m_module.opBranch(cond.labelEnd); m_module.opBranch(cond.labelEnd);
m_module.opLabel (cond.labelEnd); m_module.opLabel (cond.labelEnd);
@ -7204,8 +7249,12 @@ namespace dxvk {
void DxbcCompiler::emitHsPhaseBarrier() { void DxbcCompiler::emitHsPhaseBarrier() {
uint32_t exeScopeId = m_module.constu32(spv::ScopeWorkgroup); uint32_t exeScopeId = m_module.constu32(spv::ScopeWorkgroup);
uint32_t memScopeId = m_module.constu32(spv::ScopeInvocation); uint32_t memScopeId = m_module.constu32(spv::ScopeWorkgroup);
uint32_t semanticId = m_module.constu32(spv::MemorySemanticsMaskNone); uint32_t semanticId = m_module.constu32(
spv::MemorySemanticsOutputMemoryMask |
spv::MemorySemanticsAcquireReleaseMask |
spv::MemorySemanticsMakeAvailableMask |
spv::MemorySemanticsMakeVisibleMask);
m_module.opControlBarrier(exeScopeId, memScopeId, semanticId); m_module.opControlBarrier(exeScopeId, memScopeId, semanticId);
} }
@ -7518,6 +7567,7 @@ namespace dxvk {
result.typeId = texture.imageTypeId; result.typeId = texture.imageTypeId;
result.varId = texture.varId; result.varId = texture.varId;
result.stride = texture.structStride; result.stride = texture.structStride;
result.coherence = 0;
result.isSsbo = texture.isRawSsbo; result.isSsbo = texture.isRawSsbo;
return result; return result;
} break; } break;
@ -7532,6 +7582,7 @@ namespace dxvk {
result.typeId = uav.imageTypeId; result.typeId = uav.imageTypeId;
result.varId = uav.varId; result.varId = uav.varId;
result.stride = uav.structStride; result.stride = uav.structStride;
result.coherence = uav.coherence;
result.isSsbo = uav.isRawSsbo; result.isSsbo = uav.isRawSsbo;
return result; return result;
} break; } break;
@ -7546,6 +7597,7 @@ namespace dxvk {
spv::StorageClassWorkgroup); spv::StorageClassWorkgroup);
result.varId = m_gRegs.at(registerId).varId; result.varId = m_gRegs.at(registerId).varId;
result.stride = m_gRegs.at(registerId).elementStride; result.stride = m_gRegs.at(registerId).elementStride;
result.coherence = 0;
result.isSsbo = false; result.isSsbo = false;
return result; return result;
} break; } break;
@ -7734,6 +7786,27 @@ namespace dxvk {
} }
uint32_t DxbcCompiler::getUavCoherence(uint32_t registerId, DxbcUavFlags flags) const {
// Ignore any resources that can't both be read and written in
// the current shader, explicit availability/visibility operands
// are not useful in that case.
if (m_analysis->uavInfos[registerId].accessFlags != (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT))
return 0;
// If the globally coherent flag is set, the resource must be
// coherent across multiple workgroups of the same dispatch
if (flags.test(DxbcUavFlag::GloballyCoherent))
return spv::ScopeQueueFamily;
// In compute shaders, UAVs are implicitly workgroup coherent.
// In all other stage, there are no coherence guarantees.
if (m_programInfo.type() == DxbcProgramType::ComputeShader)
return spv::ScopeWorkgroup;
return 0;
}
uint32_t DxbcCompiler::getScalarTypeId(DxbcScalarType type) { uint32_t DxbcCompiler::getScalarTypeId(DxbcScalarType type) {
if (type == DxbcScalarType::Float64) if (type == DxbcScalarType::Float64)
m_module.enableCapability(spv::CapabilityFloat64); m_module.enableCapability(spv::CapabilityFloat64);

View File

@ -347,6 +347,7 @@ namespace dxvk {
uint32_t typeId; uint32_t typeId;
uint32_t varId; uint32_t varId;
uint32_t stride; uint32_t stride;
uint32_t coherence;
bool isSsbo; bool isSsbo;
}; };
@ -1222,6 +1223,10 @@ namespace dxvk {
bool caseBlockIsFallthrough() const; bool caseBlockIsFallthrough() const;
uint32_t getUavCoherence(
uint32_t registerId,
DxbcUavFlags flags) const;
/////////////////////////// ///////////////////////////
// Type definition methods // Type definition methods
uint32_t getScalarTypeId( uint32_t getScalarTypeId(

View File

@ -100,6 +100,7 @@ namespace dxvk {
uint32_t sampledTypeId = 0; uint32_t sampledTypeId = 0;
uint32_t imageTypeId = 0; uint32_t imageTypeId = 0;
uint32_t structStride = 0; uint32_t structStride = 0;
uint32_t coherence = 0;
bool isRawSsbo = false; bool isRawSsbo = false;
}; };