// 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 #include #include namespace vk { Query::Query(VkQueryType type) : finished(marl::Event::Mode::Manual) , state(UNAVAILABLE) , type(type) , value(0) {} void Query::reset() { finished.clear(); auto prevState = state.exchange(UNAVAILABLE); ASSERT(prevState != ACTIVE); value = 0; } void Query::start() { auto prevState = state.exchange(ACTIVE); ASSERT(prevState != FINISHED); // Must be reset first 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(mem)) , type(pCreateInfo->queryType) , count(pCreateInfo->queryCount) { // According to the Vulkan 1.2 spec, section 30. 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) { UNSUPPORTED("VkPhysicalDeviceFeatures::pipelineStatisticsQuery"); } // Construct all queries for(uint32_t i = 0; i < count; i++) { new(&pool[i]) Query(type); } } void QueryPool::destroy(const VkAllocationCallbacks *pAllocator) { for(uint32_t i = 0; i < count; i++) { pool[i].~Query(); } 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(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(pData); for(uint32_t i = firstQuery; i < (firstQuery + queryCount); i++, data += stride) { auto &query = pool[i]; if(flags & VK_QUERY_RESULT_WAIT_BIT) // Must wait for query to finish { query.wait(); } const auto current = query.getData(); // "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." bool writeResult = true; if(current.state == Query::ACTIVE || (current.state == Query::UNAVAILABLE && !(flags & VK_QUERY_RESULT_WAIT_BIT))) { 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(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(data); if(writeResult) { result32[0] = static_cast(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); // Only accept flags with valid bits set. if(flags & ~(VK_QUERY_CONTROL_PRECISE_BIT)) { UNSUPPORTED("vkCmdBeginQuery::flags %d", int(flags)); } 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].start(); pool[query].set(std::chrono::time_point_cast( std::chrono::steady_clock::now()) .time_since_epoch() .count()); pool[query].finish(); } } // namespace vk