BearishSun vor 9 Jahren
Ursprung
Commit
8e8f590033

+ 4 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanDevice.h

@@ -64,6 +64,9 @@ namespace BansheeEngine
 		/** Returns a pool that can be used for allocating command buffers for all queues on this device. */
 		/** Returns a pool that can be used for allocating command buffers for all queues on this device. */
 		VulkanCmdBufferPool& getCmdBufferPool() const { return *mCommandBufferPool; }
 		VulkanCmdBufferPool& getCmdBufferPool() const { return *mCommandBufferPool; }
 
 
+		/** Returns a pool that can be used for allocating queries on this device. */
+		VulkanQueryPool& getQueryPool() const { return *mQueryPool; }
+
 		/** Returns a manager that can be used for allocating descriptor layouts and sets. */
 		/** Returns a manager that can be used for allocating descriptor layouts and sets. */
 		VulkanDescriptorManager& getDescriptorManager() const { return *mDescriptorManager; }
 		VulkanDescriptorManager& getDescriptorManager() const { return *mDescriptorManager; }
 
 
@@ -106,6 +109,7 @@ namespace BansheeEngine
 		UINT32 mDeviceIdx;
 		UINT32 mDeviceIdx;
 
 
 		VulkanCmdBufferPool* mCommandBufferPool;
 		VulkanCmdBufferPool* mCommandBufferPool;
+		VulkanQueryPool* mQueryPool;
 		VulkanDescriptorManager* mDescriptorManager;
 		VulkanDescriptorManager* mDescriptorManager;
 		VulkanResourceManager* mResourceManager;
 		VulkanResourceManager* mResourceManager;
 
 

+ 7 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanOcclusionQuery.h

@@ -32,6 +32,13 @@ namespace BansheeEngine
 
 
 	private:
 	private:
 		friend class QueryManager;
 		friend class QueryManager;
+
+		VulkanDevice& mDevice;
+		VulkanQuery* mQuery;
+
+		UINT64 mNumSamples;
+		bool mQueryEndCalled : 1;
+		bool mQueryFinalized : 1;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 1 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanPrerequisites.h

@@ -59,6 +59,7 @@ namespace BansheeEngine
 	class VulkanTransferBuffer;
 	class VulkanTransferBuffer;
 	class VulkanEvent;
 	class VulkanEvent;
 	class VulkanQuery;
 	class VulkanQuery;
+	class VulkanQueryPool;
 
 
 	VkAllocationCallbacks* gVulkanAllocator = nullptr;
 	VkAllocationCallbacks* gVulkanAllocator = nullptr;
 
 

+ 85 - 4
Source/BansheeVulkanRenderAPI/Include/BsVulkanQueryManager.h

@@ -12,6 +12,76 @@ namespace BansheeEngine
 	 *  @{
 	 *  @{
 	 */
 	 */
 
 
+	/** 
+	 * Pool that allocates and distributes Vulkan queries. 
+	 * 
+	 * @note	Thread safe.
+	 */
+	class VulkanQueryPool
+	{
+	public:
+		VulkanQueryPool(VulkanDevice& device);
+		~VulkanQueryPool();
+
+		/** 
+		 * Begins a timer query on the provided command buffer. 
+		 * 
+		 * @param[in]	cb			Command buffer to begin the query on.
+		 * @return					Relevant query object that was queued. It must be released via releaseQuery() once the
+		 *							caller is done accessing it.
+		 */
+		VulkanQuery* beginTimerQuery(VulkanCmdBuffer* cb);
+
+		/** 
+		 * Begins an occlusion query on the provided command buffer. Must be followed with a call to endOcclusionQuery
+		 * on the same command buffer, before the command buffer gets submitted.
+		 * 
+		 * @param[in]	cb			Command buffer to begin the query on.
+		 * @param[in]	precise		When true the query will be able to return the precise number of processed samples, 
+		 *							otherwise it just returns a boolean value if anything was drawn.
+		 * @return					Relevant query object that was queued. It must be released via releaseQuery() once the
+		 *							caller is done accessing it.
+		 */
+		VulkanQuery* beginOcclusionQuery(VulkanCmdBuffer* cb, bool precise);
+
+		/** 
+		 * End am occlusion query query on the provided command buffer. 
+		 * 
+		 * @param[in]	query		Query previously begun with beginOcclusionQuery().
+		 * @param[in]	cb			Command buffer to end the query on.
+		 */
+		void endOcclusionQuery(VulkanQuery* query, VulkanCmdBuffer* cb);
+
+		/** Releases a previously retrieved query, ensuring it can be re-used. */
+		void releaseQuery(VulkanQuery* query);
+
+	private:
+		/** Query buffer pool and related information. */
+		struct PoolInfo
+		{
+			VkQueryPool pool = VK_NULL_HANDLE;
+			UINT32 startIdx;
+		};
+
+		/** Attempts to find a free query of the specified type, or allocates a new one. Creates new pools as needed. */
+		VulkanQuery* getQuery(VkQueryType type);
+
+		/** Creates a new Vulkan query pool object. */
+		PoolInfo& allocatePool(VkQueryType type);
+
+		static const UINT32 NUM_QUERIES_PER_POOL = 16;
+
+		VulkanDevice& mDevice;
+
+		Vector<VulkanQuery*> mTimerQueries;
+		Vector<VulkanQuery*> mOcclusionQueries;
+
+		Vector<PoolInfo> mTimerPools;
+		Vector<PoolInfo> mOcclusionPools;
+
+		Mutex mMutex;
+	};
+
 	/**	Handles creation of Vulkan queries. */
 	/**	Handles creation of Vulkan queries. */
 	class VulkanQueryManager : public QueryManager
 	class VulkanQueryManager : public QueryManager
 	{
 	{
@@ -36,13 +106,24 @@ namespace BansheeEngine
 	class VulkanQuery : public VulkanResource
 	class VulkanQuery : public VulkanResource
 	{
 	{
 	public:
 	public:
-		VulkanQuery(VulkanResourceManager* owner);
-		~VulkanQuery();
+		VulkanQuery(VulkanResourceManager* owner, VkQueryPool pool, UINT32 queryIdx);
+
+		/** 
+		 * Attempts to retrieve the result from the query. The result is only valid if the query stopped executing on the
+		 * GPU (otherwise previous query results could be accessed, if the reset command hasn't executed yet).
+		 * 
+		 * @param[out]	result	Value of the query, if the method return true. Undefined otherwise.
+		 * @return				True if the result is ready, false otherwise.
+		 */
+		bool getResult(UINT64& result) const;
 
 
 	private:
 	private:
-		VkEvent mEvent;
-	};
+		friend class VulkanQueryPool;
 
 
+		VkQueryPool mPool;
+		UINT32 mQueryIdx;
+		bool mFree;		
+	};
 
 
 	/** @} */
 	/** @} */
 }
 }

+ 7 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanTimerQuery.h

@@ -31,6 +31,13 @@ namespace BansheeEngine
 		float getTimeMs() override;
 		float getTimeMs() override;
 
 
 	private:
 	private:
+		VulkanDevice& mDevice;
+		VulkanQuery* mBeginQuery;
+		VulkanQuery* mEndQuery;
+
+		float mTimeDelta;
+		bool mQueryEndCalled : 1;
+		bool mQueryFinalized : 1;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 3 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanDevice.cpp

@@ -4,6 +4,7 @@
 #include "BsVulkanQueue.h"
 #include "BsVulkanQueue.h"
 #include "BsVulkanCommandBuffer.h"
 #include "BsVulkanCommandBuffer.h"
 #include "BsVulkanDescriptorManager.h"
 #include "BsVulkanDescriptorManager.h"
+#include "BsVulkanQueryManager.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -110,6 +111,7 @@ namespace BansheeEngine
 
 
 		// Create pools/managers
 		// Create pools/managers
 		mCommandBufferPool = bs_new<VulkanCmdBufferPool>(*this);
 		mCommandBufferPool = bs_new<VulkanCmdBufferPool>(*this);
+		mQueryPool = bs_new<VulkanQueryPool>(*this);
 		mDescriptorManager = bs_new<VulkanDescriptorManager>(*this);
 		mDescriptorManager = bs_new<VulkanDescriptorManager>(*this);
 		mResourceManager = bs_new<VulkanResourceManager>(*this);
 		mResourceManager = bs_new<VulkanResourceManager>(*this);
 	}
 	}
@@ -127,6 +129,7 @@ namespace BansheeEngine
 
 
 		bs_delete(mResourceManager);
 		bs_delete(mResourceManager);
 		bs_delete(mDescriptorManager);
 		bs_delete(mDescriptorManager);
+		bs_delete(mQueryPool);
 		bs_delete(mCommandBufferPool);
 		bs_delete(mCommandBufferPool);
 		
 		
 		vkDestroyDevice(mLogicalDevice, gVulkanAllocator);
 		vkDestroyDevice(mLogicalDevice, gVulkanAllocator);

+ 34 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanEventQuery.cpp

@@ -7,6 +7,40 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	VulkanEvent::VulkanEvent(VulkanResourceManager* owner)
+		:VulkanResource(owner, false)
+	{
+		VkDevice vkDevice = owner->getDevice().getLogical();
+
+		VkEventCreateInfo eventCI;
+		eventCI.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
+		eventCI.pNext = nullptr;
+		eventCI.flags = 0;
+
+		VkResult result = vkCreateEvent(vkDevice, &eventCI, gVulkanAllocator, &mEvent);
+		assert(result == VK_SUCCESS);
+	}
+
+	VulkanEvent::~VulkanEvent()
+	{
+		VkDevice vkDevice = mOwner->getDevice().getLogical();
+		vkDestroyEvent(vkDevice, mEvent, gVulkanAllocator);
+	}
+
+	bool VulkanEvent::isSignaled() const
+	{
+		VkDevice vkDevice = mOwner->getDevice().getLogical();
+		return vkGetEventStatus(vkDevice, mEvent) == VK_EVENT_SET;
+	}
+
+	void VulkanEvent::reset()
+	{
+		VkDevice vkDevice = mOwner->getDevice().getLogical();
+
+		VkResult result = vkResetEvent(vkDevice, mEvent);
+		assert(result == VK_SUCCESS);
+	}
+
 	VulkanEventQuery::VulkanEventQuery(VulkanDevice& device)
 	VulkanEventQuery::VulkanEventQuery(VulkanDevice& device)
 		:mDevice(device), mEvent(nullptr)
 		:mDevice(device), mEvent(nullptr)
 	{
 	{

+ 71 - 3
Source/BansheeVulkanRenderAPI/Source/BsVulkanOcclusionQuery.cpp

@@ -1,38 +1,106 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsVulkanOcclusionQuery.h"
 #include "BsVulkanOcclusionQuery.h"
+#include "BsVulkanDevice.h"
+#include "BsVulkanQueryManager.h"
+#include "BsVulkanRenderAPI.h"
+#include "BsVulkanCommandBuffer.h"
 #include "BsRenderStats.h"
 #include "BsRenderStats.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	VulkanOcclusionQuery::VulkanOcclusionQuery(VulkanDevice& device, bool binary)
 	VulkanOcclusionQuery::VulkanOcclusionQuery(VulkanDevice& device, bool binary)
-		:OcclusionQuery(binary)
+		: OcclusionQuery(binary), mDevice(device), mQuery(nullptr), mNumSamples(0), mQueryEndCalled(false)
+		, mQueryFinalized(false)
 	{
 	{
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Query);
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Query);
 	}
 	}
 
 
 	VulkanOcclusionQuery::~VulkanOcclusionQuery()
 	VulkanOcclusionQuery::~VulkanOcclusionQuery()
 	{
 	{
+		if (mQuery != nullptr)
+			mDevice.getQueryPool().releaseQuery(mQuery);
+
 		BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_Query);
 		BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_Query);
 	}
 	}
 
 
 	void VulkanOcclusionQuery::begin(const SPtr<CommandBuffer>& cb)
 	void VulkanOcclusionQuery::begin(const SPtr<CommandBuffer>& cb)
 	{
 	{
+		VulkanQueryPool& queryPool = mDevice.getQueryPool();
+
+		// Clear any existing query
+		if (mQuery != nullptr)
+		{
+			queryPool.releaseQuery(mQuery);
+			mQuery = nullptr;
+		}
+
+		mQueryEndCalled = false;
+		mNumSamples = 0;
+
+		// Retrieve and queue new query
+		VulkanCommandBuffer* vulkanCB;
+		if (cb != nullptr)
+			vulkanCB = static_cast<VulkanCommandBuffer*>(cb.get());
+		else
+			vulkanCB = static_cast<VulkanCommandBuffer*>(gVulkanRenderAPI()._getMainCommandBuffer());
+
+		VulkanCmdBuffer* internalCB = vulkanCB->getInternal();
+		mQuery = queryPool.beginOcclusionQuery(internalCB, !mBinary);
+
 		setActive(true);
 		setActive(true);
 	}
 	}
 
 
 	void VulkanOcclusionQuery::end(const SPtr<CommandBuffer>& cb)
 	void VulkanOcclusionQuery::end(const SPtr<CommandBuffer>& cb)
 	{
 	{
+		if(mQuery == nullptr)
+		{
+			LOGERR("end() called but query was never started.");
+			return;
+		}
 
 
+		mQueryEndCalled = true;
+		mQueryFinalized = false;
+
+		VulkanCommandBuffer* vulkanCB;
+		if (cb != nullptr)
+			vulkanCB = static_cast<VulkanCommandBuffer*>(cb.get());
+		else
+			vulkanCB = static_cast<VulkanCommandBuffer*>(gVulkanRenderAPI()._getMainCommandBuffer());
+
+		VulkanQueryPool& queryPool = mDevice.getQueryPool();
+		VulkanCmdBuffer* internalCB = vulkanCB->getInternal();
+		queryPool.endOcclusionQuery(mQuery, internalCB);
 	}
 	}
 
 
 	bool VulkanOcclusionQuery::isReady() const
 	bool VulkanOcclusionQuery::isReady() const
 	{
 	{
-		return false;
+		if (!mQueryEndCalled)
+			return false;
+
+		if (mQueryFinalized)
+			return true;
+
+		UINT64 numSamples;
+		return !mQuery->isBound() && mQuery->getResult(numSamples);
 	}
 	}
 
 
 	UINT32 VulkanOcclusionQuery::getNumSamples()
 	UINT32 VulkanOcclusionQuery::getNumSamples()
 	{
 	{
-		return 0;
+		if(!mQueryFinalized)
+		{
+			UINT64 numSamples;
+			if(!mQuery->isBound() && mQuery->getResult(numSamples))
+			{
+				mQueryFinalized = true;
+				mNumSamples = numSamples;
+
+				VulkanQueryPool& queryPool = mDevice.getQueryPool();
+				queryPool.releaseQuery(mQuery);
+				mQuery = nullptr;
+			}
+		}
+
+		return (UINT32)mNumSamples;
 	}
 	}
 }
 }

+ 145 - 26
Source/BansheeVulkanRenderAPI/Source/BsVulkanQueryManager.cpp

@@ -6,9 +6,143 @@
 #include "BsVulkanOcclusionQuery.h"
 #include "BsVulkanOcclusionQuery.h"
 #include "BsVulkanRenderAPI.h"
 #include "BsVulkanRenderAPI.h"
 #include "BsVulkanDevice.h"
 #include "BsVulkanDevice.h"
+#include "BsVulkanCommandBuffer.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	VulkanQueryPool::VulkanQueryPool(VulkanDevice& device)
+		:mDevice(device)
+	{
+		Lock(mMutex);
+
+		allocatePool(VK_QUERY_TYPE_TIMESTAMP);
+		allocatePool(VK_QUERY_TYPE_OCCLUSION);
+	}
+
+	VulkanQueryPool::~VulkanQueryPool()
+	{
+		Lock(mMutex);
+
+		for (auto& entry : mTimerQueries)
+			entry->destroy();
+
+		for (auto& entry : mOcclusionQueries)
+			entry->destroy();
+
+		for (auto& entry : mTimerPools)
+			vkDestroyQueryPool(mDevice.getLogical(), entry.pool, gVulkanAllocator);
+
+		for (auto& entry : mOcclusionPools)
+			vkDestroyQueryPool(mDevice.getLogical(), entry.pool, gVulkanAllocator);
+	}
+
+	VulkanQueryPool::PoolInfo& VulkanQueryPool::allocatePool(VkQueryType type)
+	{
+		VkQueryPoolCreateInfo queryPoolCI;
+		queryPoolCI.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
+		queryPoolCI.pNext = nullptr;
+		queryPoolCI.flags = 0;
+		queryPoolCI.pipelineStatistics = 0;
+		queryPoolCI.queryCount = NUM_QUERIES_PER_POOL;
+		queryPoolCI.queryType = type;
+
+		PoolInfo poolInfo;
+		VkResult result = vkCreateQueryPool(mDevice.getLogical(), &queryPoolCI, gVulkanAllocator, &poolInfo.pool);
+		assert(result == VK_SUCCESS);
+
+		Vector<PoolInfo>& poolInfos = type == VK_QUERY_TYPE_TIMESTAMP ? mTimerPools : mOcclusionPools;
+		poolInfo.startIdx = (UINT32)poolInfos.size() * NUM_QUERIES_PER_POOL;
+
+		poolInfos.push_back(poolInfo);
+
+		Vector<VulkanQuery*>& queries = type == VK_QUERY_TYPE_TIMESTAMP ? mTimerQueries : mOcclusionQueries;
+		for (UINT32 i = 0; i < NUM_QUERIES_PER_POOL; i++)
+			queries.push_back(nullptr);
+
+		return poolInfos.back();
+	}
+
+	VulkanQuery* VulkanQueryPool::getQuery(VkQueryType type)
+	{
+		Vector<VulkanQuery*>& queries = type == VK_QUERY_TYPE_TIMESTAMP ? mTimerQueries : mOcclusionQueries;
+		Vector<PoolInfo>& poolInfos = type == VK_QUERY_TYPE_TIMESTAMP ? mTimerPools : mOcclusionPools;
+
+		for (UINT32 i = 0; i < (UINT32)queries.size(); i++)
+		{
+			VulkanQuery* curQuery = queries[i];
+			if (curQuery == nullptr)
+			{
+				div_t divResult = std::div(i, (INT32)NUM_QUERIES_PER_POOL);
+				UINT32 poolIdx = (UINT32)divResult.quot;
+				UINT32 queryIdx = (UINT32)divResult.rem;
+
+				curQuery = mDevice.getResourceManager().create<VulkanQuery>(poolInfos[poolIdx].pool, queryIdx);
+				queries[i] = curQuery;
+
+				return curQuery;
+			}
+			else if (!curQuery->isBound() && curQuery->mFree)
+				return curQuery;
+		}
+
+		PoolInfo& poolInfo = allocatePool(type);
+		UINT32 queryIdx = poolInfo.startIdx % NUM_QUERIES_PER_POOL;
+
+		VulkanQuery* query = mDevice.getResourceManager().create<VulkanQuery>(poolInfo.pool, queryIdx);
+		queries[poolInfo.startIdx] = query;
+
+		return query;
+	}
+
+	VulkanQuery* VulkanQueryPool::beginTimerQuery(VulkanCmdBuffer* cb)
+	{
+		Lock(mMutex);
+
+		VulkanQuery* query = getQuery(VK_QUERY_TYPE_TIMESTAMP);
+		query->mFree = false;
+
+		VkCommandBuffer vkCmdBuf = cb->getHandle();
+		vkCmdResetQueryPool(vkCmdBuf, query->mPool, query->mQueryIdx, 1);
+		vkCmdWriteTimestamp(vkCmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, query->mPool, query->mQueryIdx);
+
+		// Note: Must happen only here because we need to check VulkanResource::isBound under the same mutex
+		cb->registerResource(query, VulkanUseFlag::Write);
+
+		return query;
+	}
+
+	VulkanQuery* VulkanQueryPool::beginOcclusionQuery(VulkanCmdBuffer* cb, bool precise)
+	{
+		Lock(mMutex);
+
+		VulkanQuery* query = getQuery(VK_QUERY_TYPE_TIMESTAMP);
+		query->mFree = false;
+
+		VkCommandBuffer vkCmdBuf = cb->getHandle();
+		vkCmdResetQueryPool(vkCmdBuf, query->mPool, query->mQueryIdx, 1);
+		vkCmdBeginQuery(vkCmdBuf, query->mPool, query->mQueryIdx, precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
+
+		// Note: Must happen only here because we need to check VulkanResource::isBound under the same mutex
+		cb->registerResource(query, VulkanUseFlag::Write);
+
+		return query;
+	}
+
+	void VulkanQueryPool::endOcclusionQuery(VulkanQuery* query, VulkanCmdBuffer* cb)
+	{
+		Lock(mMutex);
+
+		VkCommandBuffer vkCmdBuf = cb->getHandle();
+		vkCmdEndQuery(vkCmdBuf, query->mPool, query->mQueryIdx);
+	}
+
+	void VulkanQueryPool::releaseQuery(VulkanQuery* query)
+	{
+		Lock(mMutex);
+
+		query->mFree = true;
+	}
+
 	VulkanQueryManager::VulkanQueryManager(VulkanRenderAPI& rapi)
 	VulkanQueryManager::VulkanQueryManager(VulkanRenderAPI& rapi)
 		:mRenderAPI(rapi)
 		:mRenderAPI(rapi)
 	{ }
 	{ }
@@ -33,7 +167,7 @@ namespace BansheeEngine
 	{
 	{
 		SPtr<VulkanDevice> device = mRenderAPI._getDevice(deviceIdx);
 		SPtr<VulkanDevice> device = mRenderAPI._getDevice(deviceIdx);
 
 
-		SPtr<TimerQuery> query = SPtr<VulkanTimerQuery>(bs_new<VulkanTimerQuery>(device),
+		SPtr<TimerQuery> query = SPtr<VulkanTimerQuery>(bs_new<VulkanTimerQuery>(*device),
 			&QueryManager::deleteTimerQuery, StdAlloc<VulkanTimerQuery>());
 			&QueryManager::deleteTimerQuery, StdAlloc<VulkanTimerQuery>());
 		mTimerQueries.push_back(query.get());
 		mTimerQueries.push_back(query.get());
 
 
@@ -51,37 +185,22 @@ namespace BansheeEngine
 		return query;
 		return query;
 	}
 	}
 
 
-	VulkanEvent::VulkanEvent(VulkanResourceManager* owner)
-		:VulkanResource(owner, false)
-	{
-		VkDevice vkDevice = owner->getDevice().getLogical();
-
-		VkEventCreateInfo eventCI;
-		eventCI.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
-		eventCI.pNext = nullptr;
-		eventCI.flags = 0;
-
-		VkResult result = vkCreateEvent(vkDevice, &eventCI, gVulkanAllocator, &mEvent);
-		assert(result == VK_SUCCESS);
-	}
-
-	VulkanEvent::~VulkanEvent()
+	VulkanQuery::VulkanQuery(VulkanResourceManager* owner, VkQueryPool pool, UINT32 queryIdx)
+		:VulkanResource(owner, false), mPool(pool), mQueryIdx(queryIdx), mFree(true)
 	{
 	{
-		VkDevice vkDevice = mOwner->getDevice().getLogical();
-		vkDestroyEvent(vkDevice, mEvent, gVulkanAllocator);
 	}
 	}
 
 
-	bool VulkanEvent::isSignaled() const
+	bool VulkanQuery::getResult(UINT64& result) const
 	{
 	{
-		VkDevice vkDevice = mOwner->getDevice().getLogical();
-		return vkGetEventStatus(vkDevice, mEvent) == VK_EVENT_SET;
-	}
+		// Note: A potentially better approach to get results is to make the query pool a VulkanResource, which we attach
+		// to a command buffer upon use. Then when CB finishes executing we perform vkGetQueryPoolResults on all queries
+		// in the pool at once.
 
 
-	void VulkanEvent::reset()
-	{
 		VkDevice vkDevice = mOwner->getDevice().getLogical();
 		VkDevice vkDevice = mOwner->getDevice().getLogical();
+		VkResult vkResult = vkGetQueryPoolResults(vkDevice, mPool, 0, 1, sizeof(result), &result, sizeof(result),
+												  VK_QUERY_RESULT_64_BIT);
+		assert(vkResult == VK_SUCCESS || vkResult == VK_NOT_READY);
 
 
-		VkResult result = vkResetEvent(vkDevice, mEvent);
-		assert(result == VK_SUCCESS);
+		return vkResult == VK_SUCCESS;
 	}
 	}
 }
 }

+ 97 - 2
Source/BansheeVulkanRenderAPI/Source/BsVulkanTimerQuery.cpp

@@ -1,37 +1,132 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsVulkanTimerQuery.h"
 #include "BsVulkanTimerQuery.h"
+#include "BsVulkanDevice.h"
+#include "BsVulkanQueryManager.h"
+#include "BsVulkanRenderAPI.h"
+#include "BsVulkanCommandBuffer.h"
 #include "BsRenderStats.h"
 #include "BsRenderStats.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	VulkanTimerQuery::VulkanTimerQuery(VulkanDevice& device)
 	VulkanTimerQuery::VulkanTimerQuery(VulkanDevice& device)
+		: mDevice(device), mBeginQuery(nullptr), mEndQuery(nullptr), mTimeDelta(0.0f), mQueryEndCalled(false)
+		, mQueryFinalized(false)
 	{
 	{
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Query);
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Query);
 	}
 	}
 
 
 	VulkanTimerQuery::~VulkanTimerQuery()
 	VulkanTimerQuery::~VulkanTimerQuery()
 	{
 	{
+		if (mBeginQuery != nullptr)
+			mDevice.getQueryPool().releaseQuery(mBeginQuery);
+
+		if (mEndQuery != nullptr)
+			mDevice.getQueryPool().releaseQuery(mEndQuery);
+
 		BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_Query);
 		BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_Query);
 	}
 	}
 
 
 	void VulkanTimerQuery::begin(const SPtr<CommandBuffer>& cb)
 	void VulkanTimerQuery::begin(const SPtr<CommandBuffer>& cb)
 	{
 	{
+		VulkanQueryPool& queryPool = mDevice.getQueryPool();
+
+		// Clear any existing queries
+		if (mBeginQuery != nullptr)
+		{
+			queryPool.releaseQuery(mBeginQuery);
+			mBeginQuery = nullptr;
+		}
+
+		if (mEndQuery != nullptr)
+		{
+			queryPool.releaseQuery(mEndQuery);
+			mEndQuery = nullptr;
+		}
+
+		mQueryEndCalled = false;
+		mTimeDelta = 0.0f;
+
+		// Retrieve and queue new query
+		VulkanCommandBuffer* vulkanCB;
+		if (cb != nullptr)
+			vulkanCB = static_cast<VulkanCommandBuffer*>(cb.get());
+		else
+			vulkanCB = static_cast<VulkanCommandBuffer*>(gVulkanRenderAPI()._getMainCommandBuffer());
+
+		VulkanCmdBuffer* internalCB = vulkanCB->getInternal();
+		mBeginQuery = queryPool.beginTimerQuery(internalCB);
+
 		setActive(true);
 		setActive(true);
 	}
 	}
 
 
 	void VulkanTimerQuery::end(const SPtr<CommandBuffer>& cb)
 	void VulkanTimerQuery::end(const SPtr<CommandBuffer>& cb)
 	{
 	{
+		if (mBeginQuery == nullptr)
+		{
+			LOGERR("end() called but query was never started.");
+			return;
+		}
+
+		mQueryEndCalled = true;
+		mQueryFinalized = false;
 
 
+		VulkanCommandBuffer* vulkanCB;
+		if (cb != nullptr)
+			vulkanCB = static_cast<VulkanCommandBuffer*>(cb.get());
+		else
+			vulkanCB = static_cast<VulkanCommandBuffer*>(gVulkanRenderAPI()._getMainCommandBuffer());
+
+		VulkanQueryPool& queryPool = mDevice.getQueryPool();
+		VulkanCmdBuffer* internalCB = vulkanCB->getInternal();
+		mEndQuery = queryPool.beginTimerQuery(internalCB);
 	}
 	}
 
 
 	bool VulkanTimerQuery::isReady() const
 	bool VulkanTimerQuery::isReady() const
 	{
 	{
-		return false;
+		if (!mQueryEndCalled)
+			return false;
+
+		if (mQueryFinalized)
+			return true;
+
+		UINT64 timeBegin;
+		bool beginReady = !mBeginQuery->isBound() && mBeginQuery->getResult(timeBegin);
+
+		UINT64 timeEnd;
+		bool endReady = !mEndQuery->isBound() && mEndQuery->getResult(timeEnd);
+
+		return beginReady && endReady;
 	}
 	}
 
 
 	float VulkanTimerQuery::getTimeMs()
 	float VulkanTimerQuery::getTimeMs()
 	{
 	{
-		return 0.0f;
+		if (!mQueryFinalized)
+		{
+			UINT64 timeBegin;
+			bool beginReady = !mBeginQuery->isBound() && mBeginQuery->getResult(timeBegin);
+
+			UINT64 timeEnd;
+			bool endReady = !mEndQuery->isBound() && mEndQuery->getResult(timeEnd);
+
+			if (beginReady && endReady)
+			{
+				mQueryFinalized = true;
+
+				UINT64 timeDiff = timeEnd - timeBegin;
+				double timestampToMs = (double)mDevice.getDeviceProperties().limits.timestampPeriod / 1e6; // Nano to milli
+
+				mTimeDelta = (float)((double)timeDiff * timestampToMs);
+
+				VulkanQueryPool& queryPool = mDevice.getQueryPool();
+				queryPool.releaseQuery(mBeginQuery);
+				mBeginQuery = nullptr;
+
+				queryPool.releaseQuery(mEndQuery);
+				mEndQuery = nullptr;
+			}
+		}
+
+		return mTimeDelta;
 	}
 	}
 }
 }