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