BsVulkanQueryManager.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsVulkanQueryManager.h"
  4. #include "BsVulkanEventQuery.h"
  5. #include "BsVulkanTimerQuery.h"
  6. #include "BsVulkanOcclusionQuery.h"
  7. #include "BsVulkanRenderAPI.h"
  8. #include "BsVulkanDevice.h"
  9. #include "BsVulkanCommandBuffer.h"
  10. namespace BansheeEngine
  11. {
  12. VulkanQueryPool::VulkanQueryPool(VulkanDevice& device)
  13. :mDevice(device)
  14. {
  15. Lock(mMutex);
  16. allocatePool(VK_QUERY_TYPE_TIMESTAMP);
  17. allocatePool(VK_QUERY_TYPE_OCCLUSION);
  18. }
  19. VulkanQueryPool::~VulkanQueryPool()
  20. {
  21. Lock(mMutex);
  22. for (auto& entry : mTimerQueries)
  23. entry->destroy();
  24. for (auto& entry : mOcclusionQueries)
  25. entry->destroy();
  26. for (auto& entry : mTimerPools)
  27. vkDestroyQueryPool(mDevice.getLogical(), entry.pool, gVulkanAllocator);
  28. for (auto& entry : mOcclusionPools)
  29. vkDestroyQueryPool(mDevice.getLogical(), entry.pool, gVulkanAllocator);
  30. }
  31. VulkanQueryPool::PoolInfo& VulkanQueryPool::allocatePool(VkQueryType type)
  32. {
  33. VkQueryPoolCreateInfo queryPoolCI;
  34. queryPoolCI.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
  35. queryPoolCI.pNext = nullptr;
  36. queryPoolCI.flags = 0;
  37. queryPoolCI.pipelineStatistics = 0;
  38. queryPoolCI.queryCount = NUM_QUERIES_PER_POOL;
  39. queryPoolCI.queryType = type;
  40. PoolInfo poolInfo;
  41. VkResult result = vkCreateQueryPool(mDevice.getLogical(), &queryPoolCI, gVulkanAllocator, &poolInfo.pool);
  42. assert(result == VK_SUCCESS);
  43. Vector<PoolInfo>& poolInfos = type == VK_QUERY_TYPE_TIMESTAMP ? mTimerPools : mOcclusionPools;
  44. poolInfo.startIdx = (UINT32)poolInfos.size() * NUM_QUERIES_PER_POOL;
  45. poolInfos.push_back(poolInfo);
  46. Vector<VulkanQuery*>& queries = type == VK_QUERY_TYPE_TIMESTAMP ? mTimerQueries : mOcclusionQueries;
  47. for (UINT32 i = 0; i < NUM_QUERIES_PER_POOL; i++)
  48. queries.push_back(nullptr);
  49. return poolInfos.back();
  50. }
  51. VulkanQuery* VulkanQueryPool::getQuery(VkQueryType type)
  52. {
  53. Vector<VulkanQuery*>& queries = type == VK_QUERY_TYPE_TIMESTAMP ? mTimerQueries : mOcclusionQueries;
  54. Vector<PoolInfo>& poolInfos = type == VK_QUERY_TYPE_TIMESTAMP ? mTimerPools : mOcclusionPools;
  55. for (UINT32 i = 0; i < (UINT32)queries.size(); i++)
  56. {
  57. VulkanQuery* curQuery = queries[i];
  58. if (curQuery == nullptr)
  59. {
  60. div_t divResult = std::div(i, (INT32)NUM_QUERIES_PER_POOL);
  61. UINT32 poolIdx = (UINT32)divResult.quot;
  62. UINT32 queryIdx = (UINT32)divResult.rem;
  63. curQuery = mDevice.getResourceManager().create<VulkanQuery>(poolInfos[poolIdx].pool, queryIdx);
  64. queries[i] = curQuery;
  65. return curQuery;
  66. }
  67. else if (!curQuery->isBound() && curQuery->mFree)
  68. return curQuery;
  69. }
  70. PoolInfo& poolInfo = allocatePool(type);
  71. UINT32 queryIdx = poolInfo.startIdx % NUM_QUERIES_PER_POOL;
  72. VulkanQuery* query = mDevice.getResourceManager().create<VulkanQuery>(poolInfo.pool, queryIdx);
  73. queries[poolInfo.startIdx] = query;
  74. return query;
  75. }
  76. VulkanQuery* VulkanQueryPool::beginTimerQuery(VulkanCmdBuffer* cb)
  77. {
  78. Lock(mMutex);
  79. VulkanQuery* query = getQuery(VK_QUERY_TYPE_TIMESTAMP);
  80. query->mFree = false;
  81. VkCommandBuffer vkCmdBuf = cb->getHandle();
  82. vkCmdResetQueryPool(vkCmdBuf, query->mPool, query->mQueryIdx, 1);
  83. vkCmdWriteTimestamp(vkCmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, query->mPool, query->mQueryIdx);
  84. // Note: Must happen only here because we need to check VulkanResource::isBound under the same mutex
  85. cb->registerResource(query, VulkanUseFlag::Write);
  86. return query;
  87. }
  88. VulkanQuery* VulkanQueryPool::beginOcclusionQuery(VulkanCmdBuffer* cb, bool precise)
  89. {
  90. Lock(mMutex);
  91. VulkanQuery* query = getQuery(VK_QUERY_TYPE_TIMESTAMP);
  92. query->mFree = false;
  93. VkCommandBuffer vkCmdBuf = cb->getHandle();
  94. vkCmdResetQueryPool(vkCmdBuf, query->mPool, query->mQueryIdx, 1);
  95. vkCmdBeginQuery(vkCmdBuf, query->mPool, query->mQueryIdx, precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
  96. // Note: Must happen only here because we need to check VulkanResource::isBound under the same mutex
  97. cb->registerResource(query, VulkanUseFlag::Write);
  98. return query;
  99. }
  100. void VulkanQueryPool::endOcclusionQuery(VulkanQuery* query, VulkanCmdBuffer* cb)
  101. {
  102. Lock(mMutex);
  103. VkCommandBuffer vkCmdBuf = cb->getHandle();
  104. vkCmdEndQuery(vkCmdBuf, query->mPool, query->mQueryIdx);
  105. }
  106. void VulkanQueryPool::releaseQuery(VulkanQuery* query)
  107. {
  108. Lock(mMutex);
  109. query->mFree = true;
  110. }
  111. VulkanQueryManager::VulkanQueryManager(VulkanRenderAPI& rapi)
  112. :mRenderAPI(rapi)
  113. { }
  114. VulkanQueryManager::~VulkanQueryManager()
  115. {
  116. }
  117. SPtr<EventQuery> VulkanQueryManager::createEventQuery(UINT32 deviceIdx) const
  118. {
  119. SPtr<VulkanDevice> device = mRenderAPI._getDevice(deviceIdx);
  120. SPtr<EventQuery> query = SPtr<VulkanEventQuery>(bs_new<VulkanEventQuery>(*device),
  121. &QueryManager::deleteEventQuery, StdAlloc<VulkanEventQuery>());
  122. mEventQueries.push_back(query.get());
  123. return query;
  124. }
  125. SPtr<TimerQuery> VulkanQueryManager::createTimerQuery(UINT32 deviceIdx) const
  126. {
  127. SPtr<VulkanDevice> device = mRenderAPI._getDevice(deviceIdx);
  128. SPtr<TimerQuery> query = SPtr<VulkanTimerQuery>(bs_new<VulkanTimerQuery>(*device),
  129. &QueryManager::deleteTimerQuery, StdAlloc<VulkanTimerQuery>());
  130. mTimerQueries.push_back(query.get());
  131. return query;
  132. }
  133. SPtr<OcclusionQuery> VulkanQueryManager::createOcclusionQuery(bool binary, UINT32 deviceIdx) const
  134. {
  135. SPtr<VulkanDevice> device = mRenderAPI._getDevice(deviceIdx);
  136. SPtr<OcclusionQuery> query = SPtr<VulkanOcclusionQuery>(bs_new<VulkanOcclusionQuery>(*device, binary),
  137. &QueryManager::deleteOcclusionQuery, StdAlloc<VulkanOcclusionQuery>());
  138. mOcclusionQueries.push_back(query.get());
  139. return query;
  140. }
  141. VulkanQuery::VulkanQuery(VulkanResourceManager* owner, VkQueryPool pool, UINT32 queryIdx)
  142. :VulkanResource(owner, false), mPool(pool), mQueryIdx(queryIdx), mFree(true)
  143. {
  144. }
  145. bool VulkanQuery::getResult(UINT64& result) const
  146. {
  147. // Note: A potentially better approach to get results is to make the query pool a VulkanResource, which we attach
  148. // to a command buffer upon use. Then when CB finishes executing we perform vkGetQueryPoolResults on all queries
  149. // in the pool at once.
  150. VkDevice vkDevice = mOwner->getDevice().getLogical();
  151. VkResult vkResult = vkGetQueryPoolResults(vkDevice, mPool, 0, 1, sizeof(result), &result, sizeof(result),
  152. VK_QUERY_RESULT_64_BIT);
  153. assert(vkResult == VK_SUCCESS || vkResult == VK_NOT_READY);
  154. return vkResult == VK_SUCCESS;
  155. }
  156. }