1// Copyright 2018 The SwiftShader Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "VkQueryPool.hpp"
16
17#include <chrono>
18#include <cstring>
19#include <new>
20
21namespace vk
22{
23 Query::Query() : finished(marl::Event::Mode::Manual), state(UNAVAILABLE), type(INVALID_TYPE), value(0) {}
24
25 void Query::reset()
26 {
27 finished.clear();
28 auto prevState = state.exchange(UNAVAILABLE);
29 ASSERT(prevState != ACTIVE);
30 type = INVALID_TYPE;
31 value = 0;
32 }
33
34 void Query::prepare(VkQueryType ty)
35 {
36 auto prevState = state.exchange(ACTIVE);
37 ASSERT(prevState == UNAVAILABLE);
38 type = ty;
39 }
40
41 void Query::start()
42 {
43 ASSERT(state == ACTIVE);
44 wg.add();
45 }
46
47 void Query::finish()
48 {
49 if (wg.done())
50 {
51 auto prevState = state.exchange(FINISHED);
52 ASSERT(prevState == ACTIVE);
53 finished.signal();
54 }
55 }
56
57 Query::Data Query::getData() const
58 {
59 Data out;
60 out.state = state;
61 out.value = value;
62 return out;
63 }
64
65 VkQueryType Query::getType() const
66 {
67 return type;
68 }
69
70 void Query::wait()
71 {
72 finished.wait();
73 }
74
75 void Query::set(int64_t v)
76 {
77 value = v;
78 }
79
80 void Query::add(int64_t v)
81 {
82 value += v;
83 }
84
85 QueryPool::QueryPool(const VkQueryPoolCreateInfo* pCreateInfo, void* mem) :
86 pool(reinterpret_cast<Query*>(mem)), type(pCreateInfo->queryType),
87 count(pCreateInfo->queryCount)
88 {
89 // According to the Vulkan spec, section 34.1. Features:
90 // "pipelineStatisticsQuery specifies whether the pipeline statistics
91 // queries are supported. If this feature is not enabled, queries of
92 // type VK_QUERY_TYPE_PIPELINE_STATISTICS cannot be created, and
93 // none of the VkQueryPipelineStatisticFlagBits bits can be set in the
94 // pipelineStatistics member of the VkQueryPoolCreateInfo structure."
95 if(type == VK_QUERY_TYPE_PIPELINE_STATISTICS)
96 {
97 UNIMPLEMENTED("pCreateInfo->queryType");
98 }
99
100 // Construct all queries
101 for(uint32_t i = 0; i < count; i++)
102 {
103 new (&pool[i]) Query();
104 }
105 }
106
107 void QueryPool::destroy(const VkAllocationCallbacks* pAllocator)
108 {
109 vk::deallocate(pool, pAllocator);
110 }
111
112 size_t QueryPool::ComputeRequiredAllocationSize(const VkQueryPoolCreateInfo* pCreateInfo)
113 {
114 return sizeof(Query) * pCreateInfo->queryCount;
115 }
116
117 VkResult QueryPool::getResults(uint32_t firstQuery, uint32_t queryCount, size_t dataSize,
118 void* pData, VkDeviceSize stride, VkQueryResultFlags flags) const
119 {
120 // dataSize must be large enough to contain the result of each query
121 ASSERT(static_cast<size_t>(stride * queryCount) <= dataSize);
122
123 // The sum of firstQuery and queryCount must be less than or equal to the number of queries
124 ASSERT((firstQuery + queryCount) <= count);
125
126 VkResult result = VK_SUCCESS;
127 uint8_t* data = static_cast<uint8_t*>(pData);
128 for(uint32_t i = firstQuery; i < (firstQuery + queryCount); i++, data += stride)
129 {
130 // If VK_QUERY_RESULT_WAIT_BIT and VK_QUERY_RESULT_PARTIAL_BIT are both not set
131 // then no result values are written to pData for queries that are in the
132 // unavailable state at the time of the call, and vkGetQueryPoolResults returns
133 // VK_NOT_READY. However, availability state is still written to pData for those
134 // queries if VK_QUERY_RESULT_WITH_AVAILABILITY_BIT is set.
135 auto &query = pool[i];
136
137 if(flags & VK_QUERY_RESULT_WAIT_BIT) // Must wait for query to finish
138 {
139 query.wait();
140 }
141
142 const auto current = query.getData();
143
144 bool writeResult = true;
145 if(current.state == Query::ACTIVE)
146 {
147 result = VK_NOT_READY;
148 writeResult = (flags & VK_QUERY_RESULT_PARTIAL_BIT); // Allow writing partial results
149 }
150
151 if(flags & VK_QUERY_RESULT_64_BIT)
152 {
153 uint64_t* result64 = reinterpret_cast<uint64_t*>(data);
154 if(writeResult)
155 {
156 result64[0] = current.value;
157 }
158 if(flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) // Output query availablity
159 {
160 result64[1] = current.state;
161 }
162 }
163 else
164 {
165 uint32_t* result32 = reinterpret_cast<uint32_t*>(data);
166 if(writeResult)
167 {
168 result32[0] = static_cast<uint32_t>(current.value);
169 }
170 if(flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) // Output query availablity
171 {
172 result32[1] = current.state;
173 }
174 }
175 }
176
177 return result;
178 }
179
180 void QueryPool::begin(uint32_t query, VkQueryControlFlags flags)
181 {
182 ASSERT(query < count);
183
184 if(flags != 0)
185 {
186 UNIMPLEMENTED("flags");
187 }
188
189 pool[query].prepare(type);
190 pool[query].start();
191 }
192
193 void QueryPool::end(uint32_t query)
194 {
195 ASSERT(query < count);
196 pool[query].finish();
197 }
198
199 void QueryPool::reset(uint32_t firstQuery, uint32_t queryCount)
200 {
201 // The sum of firstQuery and queryCount must be less than or equal to the number of queries
202 ASSERT((firstQuery + queryCount) <= count);
203
204 for(uint32_t i = firstQuery; i < (firstQuery + queryCount); i++)
205 {
206 pool[i].reset();
207 }
208 }
209
210 void QueryPool::writeTimestamp(uint32_t query)
211 {
212 ASSERT(query < count);
213 ASSERT(type == VK_QUERY_TYPE_TIMESTAMP);
214
215 pool[query].set(std::chrono::time_point_cast<std::chrono::nanoseconds>(
216 std::chrono::system_clock::now()).time_since_epoch().count());
217 }
218} // namespace vk
219