| // Copyright 2018 The SwiftShader Authors. All Rights Reserved. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //    http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "VkQueryPool.hpp" | 
 |  | 
 | #include <chrono> | 
 | #include <cstring> | 
 | #include <new> | 
 |  | 
 | namespace vk | 
 | { | 
 | 	Query::Query() : finished(sw::Event::ClearMode::Manual), state(UNAVAILABLE), type(INVALID_TYPE), value(0) {} | 
 |  | 
 | 	void Query::reset() | 
 | 	{ | 
 | 		ASSERT(wg.count() == 0); | 
 | 		finished.clear(); | 
 | 		auto prevState = state.exchange(UNAVAILABLE); | 
 | 		ASSERT(prevState != ACTIVE); | 
 | 		type = INVALID_TYPE; | 
 | 		value = 0; | 
 | 	} | 
 |  | 
 | 	void Query::prepare(VkQueryType ty) | 
 | 	{ | 
 | 		auto prevState = state.exchange(ACTIVE); | 
 | 		ASSERT(prevState == UNAVAILABLE); | 
 | 		type = ty; | 
 | 	} | 
 |  | 
 | 	void Query::start() | 
 | 	{ | 
 | 		ASSERT(state == ACTIVE); | 
 | 		wg.add(); | 
 | 	} | 
 |  | 
 | 	void Query::finish() | 
 | 	{ | 
 | 		if (wg.done()) | 
 | 		{ | 
 | 			auto prevState = state.exchange(FINISHED); | 
 | 			ASSERT(prevState == ACTIVE); | 
 | 			finished.signal(); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	Query::Data Query::getData() const | 
 | 	{ | 
 | 		Data out; | 
 | 		out.state = state; | 
 | 		out.value = value; | 
 | 		return out; | 
 | 	} | 
 |  | 
 | 	VkQueryType Query::getType() const | 
 | 	{ | 
 | 		return type; | 
 | 	} | 
 |  | 
 | 	void Query::wait() | 
 | 	{ | 
 | 		finished.wait(); | 
 | 	} | 
 |  | 
 | 	void Query::set(int64_t v) | 
 | 	{ | 
 | 		value = v; | 
 | 	} | 
 |  | 
 | 	void Query::add(int64_t v) | 
 | 	{ | 
 | 		value += v; | 
 | 	} | 
 |  | 
 | 	QueryPool::QueryPool(const VkQueryPoolCreateInfo* pCreateInfo, void* mem) : | 
 | 		pool(reinterpret_cast<Query*>(mem)), type(pCreateInfo->queryType), | 
 | 		count(pCreateInfo->queryCount) | 
 | 	{ | 
 | 		// According to the Vulkan spec, section 34.1. Features: | 
 | 		// "pipelineStatisticsQuery specifies whether the pipeline statistics | 
 | 		//  queries are supported. If this feature is not enabled, queries of | 
 | 		//  type VK_QUERY_TYPE_PIPELINE_STATISTICS cannot be created, and | 
 | 		//  none of the VkQueryPipelineStatisticFlagBits bits can be set in the | 
 | 		//  pipelineStatistics member of the VkQueryPoolCreateInfo structure." | 
 | 		if(type == VK_QUERY_TYPE_PIPELINE_STATISTICS) | 
 | 		{ | 
 | 			UNIMPLEMENTED("pCreateInfo->queryType"); | 
 | 		} | 
 |  | 
 | 		// Construct all queries | 
 | 		for(uint32_t i = 0; i < count; i++) | 
 | 		{ | 
 | 			new (&pool[i]) Query(); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	void QueryPool::destroy(const VkAllocationCallbacks* pAllocator) | 
 | 	{ | 
 | 		vk::deallocate(pool, pAllocator); | 
 | 	} | 
 |  | 
 | 	size_t QueryPool::ComputeRequiredAllocationSize(const VkQueryPoolCreateInfo* pCreateInfo) | 
 | 	{ | 
 | 		return sizeof(Query) * pCreateInfo->queryCount; | 
 | 	} | 
 |  | 
 | 	VkResult QueryPool::getResults(uint32_t firstQuery, uint32_t queryCount, size_t dataSize, | 
 | 	                               void* pData, VkDeviceSize stride, VkQueryResultFlags flags) const | 
 | 	{ | 
 | 		// dataSize must be large enough to contain the result of each query | 
 | 		ASSERT(static_cast<size_t>(stride * queryCount) <= dataSize); | 
 |  | 
 | 		// The sum of firstQuery and queryCount must be less than or equal to the number of queries | 
 | 		ASSERT((firstQuery + queryCount) <= count); | 
 |  | 
 | 		VkResult result = VK_SUCCESS; | 
 | 		uint8_t* data = static_cast<uint8_t*>(pData); | 
 | 		for(uint32_t i = firstQuery; i < (firstQuery + queryCount); i++, data += stride) | 
 | 		{ | 
 | 			// If VK_QUERY_RESULT_WAIT_BIT and VK_QUERY_RESULT_PARTIAL_BIT are both not set | 
 | 			// then no result values are written to pData for queries that are in the | 
 | 			// unavailable state at the time of the call, and vkGetQueryPoolResults returns | 
 | 			// VK_NOT_READY. However, availability state is still written to pData for those | 
 | 			// queries if VK_QUERY_RESULT_WITH_AVAILABILITY_BIT is set. | 
 | 			auto &query = pool[i]; | 
 |  | 
 | 			if(flags & VK_QUERY_RESULT_WAIT_BIT) // Must wait for query to finish | 
 | 			{ | 
 | 				query.wait(); | 
 | 			} | 
 |  | 
 | 			const auto current = query.getData(); | 
 |  | 
 | 			bool writeResult = true; | 
 | 			if(current.state == Query::ACTIVE) | 
 | 			{ | 
 | 				result = VK_NOT_READY; | 
 | 				writeResult = (flags & VK_QUERY_RESULT_PARTIAL_BIT); // Allow writing partial results | 
 | 			} | 
 |  | 
 | 			if(flags & VK_QUERY_RESULT_64_BIT) | 
 | 			{ | 
 | 				uint64_t* result64 = reinterpret_cast<uint64_t*>(data); | 
 | 				if(writeResult) | 
 | 				{ | 
 | 					result64[0] = current.value; | 
 | 				} | 
 | 				if(flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) // Output query availablity | 
 | 				{ | 
 | 					result64[1] = current.state; | 
 | 				} | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				uint32_t* result32 = reinterpret_cast<uint32_t*>(data); | 
 | 				if(writeResult) | 
 | 				{ | 
 | 					result32[0] = static_cast<uint32_t>(current.value); | 
 | 				} | 
 | 				if(flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) // Output query availablity | 
 | 				{ | 
 | 					result32[1] = current.state; | 
 | 				} | 
 | 			} | 
 | 		} | 
 |  | 
 | 		return result; | 
 | 	} | 
 |  | 
 | 	void QueryPool::begin(uint32_t query, VkQueryControlFlags flags) | 
 | 	{ | 
 | 		ASSERT(query < count); | 
 |  | 
 | 		if(flags != 0) | 
 | 		{ | 
 | 			UNIMPLEMENTED("flags"); | 
 | 		} | 
 |  | 
 | 		pool[query].prepare(type); | 
 | 		pool[query].start(); | 
 | 	} | 
 |  | 
 | 	void QueryPool::end(uint32_t query) | 
 | 	{ | 
 | 		ASSERT(query < count); | 
 | 		pool[query].finish(); | 
 | 	} | 
 |  | 
 | 	void QueryPool::reset(uint32_t firstQuery, uint32_t queryCount) | 
 | 	{ | 
 | 		// The sum of firstQuery and queryCount must be less than or equal to the number of queries | 
 | 		ASSERT((firstQuery + queryCount) <= count); | 
 |  | 
 | 		for(uint32_t i = firstQuery; i < (firstQuery + queryCount); i++) | 
 | 		{ | 
 | 			pool[i].reset(); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	void QueryPool::writeTimestamp(uint32_t query) | 
 | 	{ | 
 | 		ASSERT(query < count); | 
 | 		ASSERT(type == VK_QUERY_TYPE_TIMESTAMP); | 
 |  | 
 | 		pool[query].set(std::chrono::time_point_cast<std::chrono::nanoseconds>( | 
 | 			std::chrono::system_clock::now()).time_since_epoch().count()); | 
 | 	} | 
 | } // namespace vk |