1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-03-04 10:29:15 +01:00

[dxvk] Optimize barrier logic

The is*Dirty methods can exit early if the resource to check
is only used for reading. Only call get*Access to check for
write-after-write scenarios.
This commit is contained in:
Philip Rebohle 2022-08-10 20:45:05 +02:00
parent 11fbcd3131
commit ad020c23f9
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
2 changed files with 81 additions and 65 deletions

View File

@ -5443,13 +5443,13 @@ namespace dxvk {
const DxvkBindingInfo& binding = layout.getBinding(i, j); const DxvkBindingInfo& binding = layout.getBinding(i, j);
const DxvkShaderResourceSlot& slot = m_rc[binding.resourceBinding]; const DxvkShaderResourceSlot& slot = m_rc[binding.resourceBinding];
DxvkAccessFlags srcAccess = 0; bool requiresBarrier = false;
switch (binding.descriptorType) { switch (binding.descriptorType) {
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
if (likely(slot.bufferSlice.length())) { if (likely(slot.bufferSlice.length())) {
srcAccess = this->checkBufferBarrier<DoEmit>(slot.bufferSlice, requiresBarrier = this->checkBufferBarrier<DoEmit>(slot.bufferSlice,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, binding.access); VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, binding.access);
} }
break; break;
@ -5457,7 +5457,7 @@ namespace dxvk {
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
if (likely(slot.bufferView != nullptr)) { if (likely(slot.bufferView != nullptr)) {
srcAccess = this->checkBufferViewBarrier<DoEmit>(slot.bufferView, requiresBarrier = this->checkBufferViewBarrier<DoEmit>(slot.bufferView,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, binding.access); VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, binding.access);
} }
break; break;
@ -5466,7 +5466,7 @@ namespace dxvk {
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
if (likely(slot.imageView != nullptr)) { if (likely(slot.imageView != nullptr)) {
srcAccess = this->checkImageViewBarrier<DoEmit>(slot.imageView, requiresBarrier = this->checkImageViewBarrier<DoEmit>(slot.imageView,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, binding.access); VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, binding.access);
} }
break; break;
@ -5475,21 +5475,7 @@ namespace dxvk {
/* nothing to do */; /* nothing to do */;
} }
if (DoEmit || srcAccess == 0) if (requiresBarrier) {
continue;
// Skip write-after-write barriers if explicitly requested
DxvkAccessFlags dstAccess = DxvkBarrierSet::getAccessTypes(binding.access);
VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
if ((m_barrierControl.test(DxvkBarrierControl::IgnoreWriteAfterWrite))
&& (!(m_execBarriers.getSrcStages() & ~stageMask))
&& ((srcAccess | dstAccess) == DxvkAccess::Write))
continue;
if ((srcAccess | dstAccess).test(DxvkAccess::Write)) {
m_execBarriers.recordCommands(m_cmd); m_execBarriers.recordCommands(m_cmd);
return; return;
} }
@ -5520,7 +5506,7 @@ namespace dxvk {
&& (slices[i]->bufferInfo().access & storageBufferAccess)) { && (slices[i]->bufferInfo().access & storageBufferAccess)) {
requiresBarrier = this->checkBufferBarrier<DoEmit>(*slices[i], requiresBarrier = this->checkBufferBarrier<DoEmit>(*slices[i],
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT,
VK_ACCESS_INDIRECT_COMMAND_READ_BIT).test(DxvkAccess::Write); VK_ACCESS_INDIRECT_COMMAND_READ_BIT);
} }
} }
} }
@ -5534,7 +5520,7 @@ namespace dxvk {
&& (indexBufferSlice.bufferInfo().access & storageBufferAccess)) { && (indexBufferSlice.bufferInfo().access & storageBufferAccess)) {
requiresBarrier = this->checkBufferBarrier<DoEmit>(indexBufferSlice, requiresBarrier = this->checkBufferBarrier<DoEmit>(indexBufferSlice,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
VK_ACCESS_INDEX_READ_BIT).test(DxvkAccess::Write); VK_ACCESS_INDEX_READ_BIT);
} }
} }
@ -5550,7 +5536,7 @@ namespace dxvk {
&& (vertexBufferSlice.bufferInfo().access & storageBufferAccess)) { && (vertexBufferSlice.bufferInfo().access & storageBufferAccess)) {
requiresBarrier = this->checkBufferBarrier<DoEmit>(vertexBufferSlice, requiresBarrier = this->checkBufferBarrier<DoEmit>(vertexBufferSlice,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT).test(DxvkAccess::Write); VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT);
} }
} }
} }
@ -5566,14 +5552,14 @@ namespace dxvk {
if (xfbBufferSlice.length()) { if (xfbBufferSlice.length()) {
requiresBarrier = this->checkBufferBarrier<DoEmit>(xfbBufferSlice, requiresBarrier = this->checkBufferBarrier<DoEmit>(xfbBufferSlice,
VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT) != 0; VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT);
if (xfbCounterSlice.length()) { if (xfbCounterSlice.length()) {
requiresBarrier |= this->checkBufferBarrier<DoEmit>(xfbCounterSlice, requiresBarrier |= this->checkBufferBarrier<DoEmit>(xfbCounterSlice,
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT |
VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT |
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT) != 0; VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT);
} }
} }
} }
@ -5582,22 +5568,19 @@ namespace dxvk {
// Check shader resources on every draw to handle WAW hazards // Check shader resources on every draw to handle WAW hazards
auto layout = m_state.gp.pipeline->getBindings()->layout(); auto layout = m_state.gp.pipeline->getBindings()->layout();
for (uint32_t i = 0; i < DxvkDescriptorSets::SetCount; i++) { for (uint32_t i = 0; i < DxvkDescriptorSets::SetCount && !requiresBarrier; i++) {
uint32_t bindingCount = layout.getBindingCount(i); uint32_t bindingCount = layout.getBindingCount(i);
for (uint32_t j = 0; j < bindingCount && !requiresBarrier; j++) { for (uint32_t j = 0; j < bindingCount && !requiresBarrier; j++) {
const DxvkBindingInfo& binding = layout.getBinding(i, j); const DxvkBindingInfo& binding = layout.getBinding(i, j);
const DxvkShaderResourceSlot& slot = m_rc[binding.resourceBinding]; const DxvkShaderResourceSlot& slot = m_rc[binding.resourceBinding];
DxvkAccessFlags dstAccess = DxvkBarrierSet::getAccessTypes(binding.access);
DxvkAccessFlags srcAccess = 0;
switch (binding.descriptorType) { switch (binding.descriptorType) {
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
if ((slot.bufferSlice.length()) if ((slot.bufferSlice.length())
&& (slot.bufferSlice.bufferInfo().access & storageBufferAccess)) { && (slot.bufferSlice.bufferInfo().access & storageBufferAccess)) {
srcAccess = this->checkBufferBarrier<DoEmit>(slot.bufferSlice, requiresBarrier = this->checkBufferBarrier<DoEmit>(slot.bufferSlice,
util::pipelineStages(binding.stages), binding.access); util::pipelineStages(binding.stages), binding.access);
} }
break; break;
@ -5606,7 +5589,7 @@ namespace dxvk {
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
if ((slot.bufferView != nullptr) if ((slot.bufferView != nullptr)
&& (slot.bufferView->bufferInfo().access & storageBufferAccess)) { && (slot.bufferView->bufferInfo().access & storageBufferAccess)) {
srcAccess = this->checkBufferViewBarrier<DoEmit>(slot.bufferView, requiresBarrier = this->checkBufferViewBarrier<DoEmit>(slot.bufferView,
util::pipelineStages(binding.stages), binding.access); util::pipelineStages(binding.stages), binding.access);
} }
break; break;
@ -5616,7 +5599,7 @@ namespace dxvk {
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
if ((slot.imageView != nullptr) if ((slot.imageView != nullptr)
&& (slot.imageView->imageInfo().access & storageImageAccess)) { && (slot.imageView->imageInfo().access & storageImageAccess)) {
srcAccess = this->checkImageViewBarrier<DoEmit>(slot.imageView, requiresBarrier = this->checkImageViewBarrier<DoEmit>(slot.imageView,
util::pipelineStages(binding.stages), binding.access); util::pipelineStages(binding.stages), binding.access);
} }
break; break;
@ -5624,16 +5607,6 @@ namespace dxvk {
default: default:
/* nothing to do */; /* nothing to do */;
} }
if (srcAccess == 0)
continue;
// Skip write-after-write barriers if explicitly requested
if ((m_barrierControl.test(DxvkBarrierControl::IgnoreWriteAfterWrite))
&& ((srcAccess | dstAccess) == DxvkAccess::Write))
continue;
requiresBarrier = (srcAccess | dstAccess).test(DxvkAccess::Write);
} }
} }
@ -5646,44 +5619,61 @@ namespace dxvk {
template<bool DoEmit> template<bool DoEmit>
DxvkAccessFlags DxvkContext::checkBufferBarrier( bool DxvkContext::checkBufferBarrier(
const DxvkBufferSlice& slice, const DxvkBufferSlice& bufferSlice,
VkPipelineStageFlags stages, VkPipelineStageFlags stages,
VkAccessFlags access) { VkAccessFlags access) {
if constexpr (DoEmit) { if constexpr (DoEmit) {
m_execBarriers.accessBuffer( m_execBarriers.accessBuffer(
slice.getSliceHandle(), bufferSlice.getSliceHandle(),
stages, access, stages, access,
slice.bufferInfo().stages, bufferSlice.bufferInfo().stages,
slice.bufferInfo().access); bufferSlice.bufferInfo().access);
return DxvkAccessFlags(); return false;
} else { } else {
return m_execBarriers.getBufferAccess(slice.getSliceHandle()); DxvkAccessFlags dstAccess = DxvkBarrierSet::getAccessTypes(access);
bool dirty = m_execBarriers.isBufferDirty(
bufferSlice.getSliceHandle(), dstAccess);
if (!dirty || dstAccess.test(DxvkAccess::Read) || !this->canIgnoreWawHazards(stages))
return dirty;
DxvkAccessFlags srcAccess = m_execBarriers.getBufferAccess(bufferSlice.getSliceHandle());
return srcAccess.test(DxvkAccess::Read);
} }
} }
template<bool DoEmit> template<bool DoEmit>
DxvkAccessFlags DxvkContext::checkBufferViewBarrier( bool DxvkContext::checkBufferViewBarrier(
const Rc<DxvkBufferView>& view, const Rc<DxvkBufferView>& bufferView,
VkPipelineStageFlags stages, VkPipelineStageFlags stages,
VkAccessFlags access) { VkAccessFlags access) {
if constexpr (DoEmit) { if constexpr (DoEmit) {
m_execBarriers.accessBuffer( m_execBarriers.accessBuffer(
view->getSliceHandle(), bufferView->getSliceHandle(),
stages, access, stages, access,
view->bufferInfo().stages, bufferView->bufferInfo().stages,
view->bufferInfo().access); bufferView->bufferInfo().access);
return DxvkAccessFlags(); return false;
} else { } else {
return m_execBarriers.getBufferAccess( DxvkAccessFlags dstAccess = DxvkBarrierSet::getAccessTypes(access);
view->getSliceHandle());
bool dirty = m_execBarriers.isBufferDirty(
bufferView->getSliceHandle(), dstAccess);
if (!dirty || dstAccess.test(DxvkAccess::Read) || !this->canIgnoreWawHazards(stages))
return dirty;
DxvkAccessFlags srcAccess = m_execBarriers.getBufferAccess(bufferView->getSliceHandle());
return srcAccess.test(DxvkAccess::Read);
} }
} }
template<bool DoEmit> template<bool DoEmit>
DxvkAccessFlags DxvkContext::checkImageViewBarrier( bool DxvkContext::checkImageViewBarrier(
const Rc<DxvkImageView>& imageView, const Rc<DxvkImageView>& imageView,
VkPipelineStageFlags stages, VkPipelineStageFlags stages,
VkAccessFlags access) { VkAccessFlags access) {
@ -5696,15 +5686,38 @@ namespace dxvk {
imageView->imageInfo().layout, imageView->imageInfo().layout,
imageView->imageInfo().stages, imageView->imageInfo().stages,
imageView->imageInfo().access); imageView->imageInfo().access);
return DxvkAccessFlags(); return false;
} else { } else {
return m_execBarriers.getImageAccess( DxvkAccessFlags dstAccess = DxvkBarrierSet::getAccessTypes(access);
bool dirty = m_execBarriers.isImageDirty(
imageView->image(), imageView->image(),
imageView->imageSubresources()); imageView->imageSubresources(),
dstAccess);
if (!dirty || dstAccess.test(DxvkAccess::Read) || !this->canIgnoreWawHazards(stages))
return dirty;
DxvkAccessFlags srcAccess = m_execBarriers.getImageAccess(
imageView->image(), imageView->imageSubresources());
return srcAccess.test(DxvkAccess::Read);
} }
} }
bool DxvkContext::canIgnoreWawHazards(VkPipelineStageFlags stages) {
if (!m_barrierControl.test(DxvkBarrierControl::IgnoreWriteAfterWrite))
return false;
if (stages & VK_SHADER_STAGE_COMPUTE_BIT) {
VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
return !(m_execBarriers.getSrcStages() & ~stageMask);
}
return true;
}
void DxvkContext::emitMemoryBarrier( void DxvkContext::emitMemoryBarrier(
VkPipelineStageFlags srcStages, VkPipelineStageFlags srcStages,
VkAccessFlags srcAccess, VkAccessFlags srcAccess,

View File

@ -1544,23 +1544,26 @@ namespace dxvk {
void commitGraphicsBarriers(); void commitGraphicsBarriers();
template<bool DoEmit> template<bool DoEmit>
DxvkAccessFlags checkBufferBarrier( bool checkBufferBarrier(
const DxvkBufferSlice& slice, const DxvkBufferSlice& bufferSlice,
VkPipelineStageFlags stages, VkPipelineStageFlags stages,
VkAccessFlags access); VkAccessFlags access);
template<bool DoEmit> template<bool DoEmit>
DxvkAccessFlags checkBufferViewBarrier( bool checkBufferViewBarrier(
const Rc<DxvkBufferView>& slice, const Rc<DxvkBufferView>& bufferView,
VkPipelineStageFlags stages, VkPipelineStageFlags stages,
VkAccessFlags access); VkAccessFlags access);
template<bool DoEmit> template<bool DoEmit>
DxvkAccessFlags checkImageViewBarrier( bool checkImageViewBarrier(
const Rc<DxvkImageView>& imageView, const Rc<DxvkImageView>& imageView,
VkPipelineStageFlags stages, VkPipelineStageFlags stages,
VkAccessFlags access); VkAccessFlags access);
bool canIgnoreWawHazards(
VkPipelineStageFlags stages);
void emitMemoryBarrier( void emitMemoryBarrier(
VkPipelineStageFlags srcStages, VkPipelineStageFlags srcStages,
VkAccessFlags srcAccess, VkAccessFlags srcAccess,