[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.
This commit is contained in:
Philip Rebohle 2018-02-28 12:39:16 +01:00
parent a5706254d5
commit bdfbd3e81c
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
3 changed files with 39 additions and 24 deletions

View File

@ -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,

View File

@ -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<DxvkQueryData, MaxNumQueryCountPerPool> 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;

View File

@ -55,7 +55,8 @@ namespace dxvk {
m_condOnTake.notify_one();
if (entry.fence != nullptr) {
entry.fence->wait(std::numeric_limits<uint64_t>::max());
while (!entry.fence->wait(1'000'000'000ull))
continue;
entry.cmdList->writeQueryData();
entry.cmdList->signalEvents();