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
21 namespace vk {
22
Query(VkQueryType type)23 Query::Query(VkQueryType type)
24 : finished(marl::Event::Mode::Manual)
25 , state(UNAVAILABLE)
26 , type(type)
27 , value(0)
28 {}
29
reset()30 void Query::reset()
31 {
32 finished.clear();
33 auto prevState = state.exchange(UNAVAILABLE);
34 ASSERT(prevState != ACTIVE);
35 value = 0;
36 }
37
start()38 void Query::start()
39 {
40 auto prevState = state.exchange(ACTIVE);
41 ASSERT(prevState != FINISHED); // Must be reset first
42 wg.add();
43 }
44
finish()45 void Query::finish()
46 {
47 if(wg.done())
48 {
49 auto prevState = state.exchange(FINISHED);
50 ASSERT(prevState == ACTIVE);
51 finished.signal();
52 }
53 }
54
getData() const55 Query::Data Query::getData() const
56 {
57 Data out;
58 out.state = state;
59 out.value = value;
60 return out;
61 }
62
getType() const63 VkQueryType Query::getType() const
64 {
65 return type;
66 }
67
wait()68 void Query::wait()
69 {
70 finished.wait();
71 }
72
set(int64_t v)73 void Query::set(int64_t v)
74 {
75 value = v;
76 }
77
add(int64_t v)78 void Query::add(int64_t v)
79 {
80 value += v;
81 }
82
QueryPool(const VkQueryPoolCreateInfo * pCreateInfo,void * mem)83 QueryPool::QueryPool(const VkQueryPoolCreateInfo *pCreateInfo, void *mem)
84 : pool(reinterpret_cast<Query *>(mem))
85 , type(pCreateInfo->queryType)
86 , count(pCreateInfo->queryCount)
87 {
88 // According to the Vulkan 1.2 spec, section 30. Features:
89 // "pipelineStatisticsQuery specifies whether the pipeline statistics
90 // queries are supported. If this feature is not enabled, queries of
91 // type VK_QUERY_TYPE_PIPELINE_STATISTICS cannot be created, and
92 // none of the VkQueryPipelineStatisticFlagBits bits can be set in the
93 // pipelineStatistics member of the VkQueryPoolCreateInfo structure."
94 if(type == VK_QUERY_TYPE_PIPELINE_STATISTICS)
95 {
96 UNSUPPORTED("VkPhysicalDeviceFeatures::pipelineStatisticsQuery");
97 }
98
99 // Construct all queries
100 for(uint32_t i = 0; i < count; i++)
101 {
102 new(&pool[i]) Query(type);
103 }
104 }
105
destroy(const VkAllocationCallbacks * pAllocator)106 void QueryPool::destroy(const VkAllocationCallbacks *pAllocator)
107 {
108 for(uint32_t i = 0; i < count; i++)
109 {
110 pool[i].~Query();
111 }
112
113 vk::deallocate(pool, pAllocator);
114 }
115
ComputeRequiredAllocationSize(const VkQueryPoolCreateInfo * pCreateInfo)116 size_t QueryPool::ComputeRequiredAllocationSize(const VkQueryPoolCreateInfo *pCreateInfo)
117 {
118 return sizeof(Query) * pCreateInfo->queryCount;
119 }
120
getResults(uint32_t firstQuery,uint32_t queryCount,size_t dataSize,void * pData,VkDeviceSize stride,VkQueryResultFlags flags) const121 VkResult QueryPool::getResults(uint32_t firstQuery, uint32_t queryCount, size_t dataSize,
122 void *pData, VkDeviceSize stride, VkQueryResultFlags flags) const
123 {
124 // dataSize must be large enough to contain the result of each query
125 ASSERT(static_cast<size_t>(stride * queryCount) <= dataSize);
126
127 // The sum of firstQuery and queryCount must be less than or equal to the number of queries
128 ASSERT((firstQuery + queryCount) <= count);
129
130 VkResult result = VK_SUCCESS;
131 uint8_t *data = static_cast<uint8_t *>(pData);
132 for(uint32_t i = firstQuery; i < (firstQuery + queryCount); i++, data += stride)
133 {
134 auto &query = pool[i];
135
136 if(flags & VK_QUERY_RESULT_WAIT_BIT) // Must wait for query to finish
137 {
138 query.wait();
139 }
140
141 const auto current = query.getData();
142
143 // "If VK_QUERY_RESULT_WAIT_BIT and VK_QUERY_RESULT_PARTIAL_BIT are both not set
144 // then no result values are written to pData for queries that are in the
145 // unavailable state at the time of the call, and vkGetQueryPoolResults returns
146 // VK_NOT_READY. However, availability state is still written to pData for those
147 // queries if VK_QUERY_RESULT_WITH_AVAILABILITY_BIT is set."
148 bool writeResult = true;
149 if(current.state == Query::ACTIVE || (current.state == Query::UNAVAILABLE && !(flags & VK_QUERY_RESULT_WAIT_BIT)))
150 {
151 result = VK_NOT_READY;
152 writeResult = (flags & VK_QUERY_RESULT_PARTIAL_BIT); // Allow writing partial results
153 }
154
155 if(flags & VK_QUERY_RESULT_64_BIT)
156 {
157 uint64_t *result64 = reinterpret_cast<uint64_t *>(data);
158 if(writeResult)
159 {
160 result64[0] = current.value;
161 }
162 if(flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) // Output query availablity
163 {
164 result64[1] = current.state;
165 }
166 }
167 else
168 {
169 uint32_t *result32 = reinterpret_cast<uint32_t *>(data);
170 if(writeResult)
171 {
172 result32[0] = static_cast<uint32_t>(current.value);
173 }
174 if(flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) // Output query availablity
175 {
176 result32[1] = current.state;
177 }
178 }
179 }
180
181 return result;
182 }
183
begin(uint32_t query,VkQueryControlFlags flags)184 void QueryPool::begin(uint32_t query, VkQueryControlFlags flags)
185 {
186 ASSERT(query < count);
187
188 // Only accept flags with valid bits set.
189 if(flags & ~(VK_QUERY_CONTROL_PRECISE_BIT))
190 {
191 UNSUPPORTED("vkCmdBeginQuery::flags %d", int(flags));
192 }
193
194 pool[query].start();
195 }
196
end(uint32_t query)197 void QueryPool::end(uint32_t query)
198 {
199 ASSERT(query < count);
200 pool[query].finish();
201 }
202
reset(uint32_t firstQuery,uint32_t queryCount)203 void QueryPool::reset(uint32_t firstQuery, uint32_t queryCount)
204 {
205 // The sum of firstQuery and queryCount must be less than or equal to the number of queries
206 ASSERT((firstQuery + queryCount) <= count);
207
208 for(uint32_t i = firstQuery; i < (firstQuery + queryCount); i++)
209 {
210 pool[i].reset();
211 }
212 }
213
writeTimestamp(uint32_t query)214 void QueryPool::writeTimestamp(uint32_t query)
215 {
216 ASSERT(query < count);
217 ASSERT(type == VK_QUERY_TYPE_TIMESTAMP);
218
219 pool[query].start();
220 pool[query].set(std::chrono::time_point_cast<std::chrono::nanoseconds>(
221 std::chrono::steady_clock::now())
222 .time_since_epoch()
223 .count());
224 pool[query].finish();
225 }
226
227 } // namespace vk
228