| // 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(marl::Event::Mode::Manual) |
| , state(UNAVAILABLE) |
| , type(INVALID_TYPE) |
| , value(0) |
| {} |
| |
| void Query::reset() |
| { |
| 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 |