From 3d213efe536291efeb07296044dc1fb13ee81836 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 28 Oct 2019 14:17:46 +0100 Subject: [PATCH] [dxbc] Use SSBOs for dynamically indexed constant buffers if needed SSBOs are tightly bound-checked on all Nvidia GPUs, so this allows for a more accurate workaround for games relying on OOB access behaviour. --- src/d3d11/d3d11_buffer.cpp | 3 ++- src/dxbc/dxbc_compiler.cpp | 24 ++++++++++++++++++------ src/dxbc/dxbc_compiler.h | 3 ++- src/dxbc/dxbc_options.cpp | 2 +- src/dxbc/dxbc_options.h | 4 ++++ 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/d3d11/d3d11_buffer.cpp b/src/d3d11/d3d11_buffer.cpp index 28f97484..bb7e9809 100644 --- a/src/d3d11/d3d11_buffer.cpp +++ b/src/d3d11/d3d11_buffer.cpp @@ -34,7 +34,8 @@ namespace dxvk { } if (pDesc->BindFlags & D3D11_BIND_CONSTANT_BUFFER) { - info.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + info.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT + | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; info.stages |= m_device->GetEnabledShaderStages(); info.access |= VK_ACCESS_UNIFORM_READ_BIT; } diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 4ea2eff1..7742208c 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -755,16 +755,20 @@ namespace dxvk { // (1) Number of constants in the buffer const uint32_t bufferId = ins.dst[0].idx[0].offset; const uint32_t elementCount = ins.dst[0].idx[1].offset; + + bool asSsbo = m_moduleInfo.options.dynamicIndexedConstantBufferAsSsbo + && ins.controls.accessType() == DxbcConstantBufferAccessType::DynamicallyIndexed; this->emitDclConstantBufferVar(bufferId, elementCount, - str::format("cb", bufferId).c_str()); + str::format("cb", bufferId).c_str(), asSsbo); } void DxbcCompiler::emitDclConstantBufferVar( uint32_t regIdx, uint32_t numConstants, - const char* name) { + const char* name, + bool asSsbo) { // Uniform buffer data is stored as a fixed-size array // of 4x32-bit vectors. SPIR-V requires explicit strides. const uint32_t arrayType = m_module.defArrayTypeUnique( @@ -776,7 +780,9 @@ namespace dxvk { // struct and decorate that struct as a block. const uint32_t structType = m_module.defStructTypeUnique(1, &arrayType); - m_module.decorateBlock (structType); + m_module.decorate(structType, asSsbo + ? spv::DecorationBufferBlock + : spv::DecorationBlock); m_module.memberDecorateOffset(structType, 0, 0); m_module.setDebugName (structType, str::format(name, "_t").c_str()); @@ -796,7 +802,10 @@ namespace dxvk { m_module.decorateDescriptorSet(varId, 0); m_module.decorateBinding(varId, bindingId); - + + if (asSsbo) + m_module.decorate(varId, spv::DecorationNonWritable); + // Declare a specialization constant which will // store whether or not the resource is bound. const uint32_t specConstId = m_module.specConstBool(true); @@ -813,7 +822,9 @@ namespace dxvk { // Store descriptor info for the shader interface DxvkResourceSlot resource; resource.slot = bindingId; - resource.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + resource.type = asSsbo + ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM; resource.access = VK_ACCESS_UNIFORM_READ_BIT; m_resourceSlots.push_back(resource); @@ -1505,7 +1516,8 @@ namespace dxvk { void DxbcCompiler::emitDclImmediateConstantBufferUbo( uint32_t dwordCount, const uint32_t* dwordArray) { - this->emitDclConstantBufferVar(Icb_BindingSlotId, dwordCount / 4, "icb"); + this->emitDclConstantBufferVar(Icb_BindingSlotId, dwordCount / 4, "icb", + m_moduleInfo.options.dynamicIndexedConstantBufferAsSsbo); m_immConstData = DxvkShaderConstData(dwordCount, dwordArray); } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index dc1267e4..075892b5 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -575,7 +575,8 @@ namespace dxvk { void emitDclConstantBufferVar( uint32_t regIdx, uint32_t numConstants, - const char* name); + const char* name, + bool asSsbo); void emitDclSampler( const DxbcShaderInstruction& ins); diff --git a/src/dxbc/dxbc_options.cpp b/src/dxbc/dxbc_options.cpp index 9d402484..aae391d2 100644 --- a/src/dxbc/dxbc_options.cpp +++ b/src/dxbc/dxbc_options.cpp @@ -41,7 +41,7 @@ namespace dxvk { zeroInitWorkgroupMemory = options.zeroInitWorkgroupMemory; if (DxvkGpuVendor(devInfo.core.properties.vendorID) != DxvkGpuVendor::Amd) - constantBufferRangeCheck = options.constantBufferRangeCheck; + dynamicIndexedConstantBufferAsSsbo = options.constantBufferRangeCheck; // Disable early discard on RADV (with LLVM) due to GPU hangs // Disable early discard on Nvidia because it may hurt performance diff --git a/src/dxbc/dxbc_options.h b/src/dxbc/dxbc_options.h index 8aefce49..4872ea34 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -39,6 +39,10 @@ namespace dxvk { // Fixes issues in some games, breaks others. bool constantBufferRangeCheck = false; + /// Implement dynamically indexed uniform buffers + /// with storage buffers for tight bounds checking + bool dynamicIndexedConstantBufferAsSsbo = false; + /// Clear thread-group shared memory to zero bool zeroInitWorkgroupMemory = false;