From bdfbd3e81ca9de65edf870ab3573f926b3c4743c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 28 Feb 2018 12:39:16 +0100 Subject: [PATCH] [dxvk] Implemented new workaround for query-related lockups vkGetQueryPoolResults may never return if VK_QUERY_RESULT_WAIT_BIT is set, and may return queries as not available when they should in fact be available. This would cause indefinite hangs, so instead we return fake data to query objects if retrieving query data fails. --- src/dxvk/dxvk_context.cpp | 3 -- src/dxvk/dxvk_query_pool.cpp | 57 +++++++++++++++++++++++------------- src/dxvk/dxvk_queue.cpp | 3 +- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 9e9d7156a..dd55780a8 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -70,9 +70,6 @@ namespace dxvk { void DxvkContext::beginQuery(const DxvkQueryRevision& query) { DxvkQueryHandle handle = this->allocQuery(query); - if (query.query->type() == VK_QUERY_TYPE_OCCLUSION) - this->renderPassBegin(); - m_cmd->cmdBeginQuery( handle.queryPool, handle.queryId, diff --git a/src/dxvk/dxvk_query_pool.cpp b/src/dxvk/dxvk_query_pool.cpp index ac4f5e664..b0a5972cb 100644 --- a/src/dxvk/dxvk_query_pool.cpp +++ b/src/dxvk/dxvk_query_pool.cpp @@ -15,7 +15,7 @@ namespace dxvk { info.pNext = nullptr; info.flags = 0; info.queryType = queryType; - info.queryCount = MaxNumQueryCountPerPool; + info.queryCount = queryCount; info.pipelineStatistics = 0; if (queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS) { @@ -47,20 +47,19 @@ namespace dxvk { DxvkQueryHandle DxvkQueryPool::allocQuery(const DxvkQueryRevision& query) { const uint32_t queryIndex = m_queryRangeOffset + m_queryRangeLength; - if (queryIndex < m_queryCount) { - DxvkQueryHandle result; - result.queryPool = m_queryPool; - result.queryId = queryIndex; - result.flags = query.query->flags(); - - query.query->associateQuery(query.revision, result); - m_queries.at(queryIndex) = query; - - m_queryRangeLength += 1; - return result; - } else { + if (queryIndex >= m_queryCount) return DxvkQueryHandle(); - } + + DxvkQueryHandle result; + result.queryPool = m_queryPool; + result.queryId = queryIndex; + result.flags = query.query->flags(); + + query.query->associateQuery(query.revision, result); + m_queries.at(queryIndex) = query; + + m_queryRangeLength += 1; + return result; } @@ -69,18 +68,36 @@ namespace dxvk { uint32_t queryCount) { std::array results; + // We cannot use VK_QUERY_RESULT_WAIT_BIT here since that + // may stall the calling thread indefinitely. Instead, we + // just assume that all queries should be available after + // waiting for the fence that protects the command buffer. const VkResult status = m_vkd->vkGetQueryPoolResults( m_vkd->device(), m_queryPool, queryIndex, queryCount, - sizeof(DxvkQueryData) * MaxNumQueryCountPerPool, - results.data(), sizeof(DxvkQueryData), - VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + sizeof(DxvkQueryData) * queryCount, results.data(), + sizeof(DxvkQueryData), VK_QUERY_RESULT_64_BIT); - if (status != VK_SUCCESS) - return status; + if (status != VK_SUCCESS) { + Logger::warn(str::format( + "DxvkQueryPool: Failed to get query data for ", queryIndex, + ":", queryCount, " with: ", status)); + + // If retrieving query data failed, we need to fake query + // data. In case of occlusion queries, we should return a + // non-zero value for samples passed, so that games do not + // accidentally omit certain geometry because of this. + for (uint32_t i = 0; i < queryCount; i++) { + results[i] = DxvkQueryData(); + + if (m_queryType == VK_QUERY_TYPE_OCCLUSION) + results[i].occlusion.samplesPassed = 1; + } + } + // Forward query data to the query objects for (uint32_t i = 0; i < queryCount; i++) { const DxvkQueryRevision& query = m_queries.at(queryIndex + i); - query.query->updateData(query.revision, results.at(i)); + query.query->updateData(query.revision, results[i]); } return VK_SUCCESS; diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp index b1497ce2c..8ab6f8cae 100644 --- a/src/dxvk/dxvk_queue.cpp +++ b/src/dxvk/dxvk_queue.cpp @@ -55,7 +55,8 @@ namespace dxvk { m_condOnTake.notify_one(); if (entry.fence != nullptr) { - entry.fence->wait(std::numeric_limits::max()); + while (!entry.fence->wait(1'000'000'000ull)) + continue; entry.cmdList->writeQueryData(); entry.cmdList->signalEvents();