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
23Query::Query(VkQueryType type)
24    : finished(marl::Event::Mode::Manual)
25    , state(UNAVAILABLE)
26    , type(type)
27    , value(0)
28{}
29
30void Query::reset()
31{
32	finished.clear();
33	auto prevState = state.exchange(UNAVAILABLE);
34	ASSERT(prevState != ACTIVE);
35	value = 0;
36}
37
38void Query::start()
39{
40	auto prevState = state.exchange(ACTIVE);
41	ASSERT(prevState != FINISHED);  // Must be reset first
42	wg.add();
43}
44
45void Query::finish()
46{
47	if(wg.done())
48	{
49		auto prevState = state.exchange(FINISHED);
50		ASSERT(prevState == ACTIVE);
51		finished.signal();
52	}
53}
54
55Query::Data Query::getData() const
56{
57	Data out;
58	out.state = state;
59	out.value = value;
60	return out;
61}
62
63VkQueryType Query::getType() const
64{
65	return type;
66}
67
68void Query::wait()
69{
70	finished.wait();
71}
72
73void Query::set(int64_t v)
74{
75	value = v;
76}
77
78void Query::add(int64_t v)
79{
80	value += v;
81}
82
83QueryPool::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
106void QueryPool::destroy(const VkAllocationCallbacks *pAllocator)
107{
108	for(uint32_t i = 0; i < count; i++)
109	{
110		pool[i].~Query();
111	}
112
113	vk::freeHostMemory(pool, pAllocator);
114}
115
116size_t QueryPool::ComputeRequiredAllocationSize(const VkQueryPoolCreateInfo *pCreateInfo)
117{
118	return sizeof(Query) * pCreateInfo->queryCount;
119}
120
121VkResult 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
184void 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
197void QueryPool::end(uint32_t query)
198{
199	ASSERT(query < count);
200	pool[query].finish();
201}
202
203void 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
214void 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