1 /*
2 * Copyright © 2020 Raspberry Pi
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "v3dv_private.h"
25
26 VkResult
v3dv_CreateQueryPool(VkDevice _device,const VkQueryPoolCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkQueryPool * pQueryPool)27 v3dv_CreateQueryPool(VkDevice _device,
28 const VkQueryPoolCreateInfo *pCreateInfo,
29 const VkAllocationCallbacks *pAllocator,
30 VkQueryPool *pQueryPool)
31 {
32 V3DV_FROM_HANDLE(v3dv_device, device, _device);
33
34 assert(pCreateInfo->queryType == VK_QUERY_TYPE_OCCLUSION ||
35 pCreateInfo->queryType == VK_QUERY_TYPE_TIMESTAMP);
36 assert(pCreateInfo->queryCount > 0);
37
38 /* FIXME: the hw allows us to allocate up to 16 queries in a single block
39 * for occlussion queries so we should try to use that.
40 */
41 struct v3dv_query_pool *pool =
42 vk_alloc2(&device->alloc, pAllocator, sizeof(*pool), 8,
43 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
44 if (pool == NULL)
45 return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
46
47 pool->query_type = pCreateInfo->queryType;
48 pool->query_count = pCreateInfo->queryCount;
49
50 VkResult result;
51
52 const uint32_t pool_bytes = sizeof(struct v3dv_query) * pool->query_count;
53 pool->queries = vk_alloc2(&device->alloc, pAllocator, pool_bytes, 8,
54 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
55 if (pool->queries == NULL) {
56 result = vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
57 goto fail_alloc_bo_list;
58 }
59
60 uint32_t i;
61 for (i = 0; i < pool->query_count; i++) {
62 pool->queries[i].maybe_available = false;
63 switch (pool->query_type) {
64 case VK_QUERY_TYPE_OCCLUSION:
65 pool->queries[i].bo = v3dv_bo_alloc(device, 4096, "query", true);
66 if (!pool->queries[i].bo) {
67 result = vk_error(device->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
68 goto fail_alloc_bo;
69 }
70 /* For occlusion queries we only need a 4-byte counter */
71 if (!v3dv_bo_map(device, pool->queries[i].bo, 4)) {
72 result = vk_error(device->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
73 goto fail_alloc_bo;
74 }
75 break;
76 case VK_QUERY_TYPE_TIMESTAMP:
77 pool->queries[i].value = 0;
78 break;
79 default:
80 unreachable("Unsupported query type");
81 }
82 }
83
84 *pQueryPool = v3dv_query_pool_to_handle(pool);
85
86 return VK_SUCCESS;
87
88 fail_alloc_bo:
89 for (uint32_t j = 0; j < i; j++)
90 v3dv_bo_free(device, pool->queries[j].bo);
91 vk_free2(&device->alloc, pAllocator, pool->queries);
92
93 fail_alloc_bo_list:
94 vk_free2(&device->alloc, pAllocator, pool);
95
96 return result;
97 }
98
99 void
v3dv_DestroyQueryPool(VkDevice _device,VkQueryPool queryPool,const VkAllocationCallbacks * pAllocator)100 v3dv_DestroyQueryPool(VkDevice _device,
101 VkQueryPool queryPool,
102 const VkAllocationCallbacks *pAllocator)
103 {
104 V3DV_FROM_HANDLE(v3dv_device, device, _device);
105 V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
106
107 if (!pool)
108 return;
109
110 if (pool->query_type == VK_QUERY_TYPE_OCCLUSION) {
111 for (uint32_t i = 0; i < pool->query_count; i++)
112 v3dv_bo_free(device, pool->queries[i].bo);
113 }
114
115 vk_free2(&device->alloc, pAllocator, pool->queries);
116 vk_free2(&device->alloc, pAllocator, pool);
117 }
118
119 static void
write_query_result(void * dst,uint32_t idx,bool do_64bit,uint64_t value)120 write_query_result(void *dst, uint32_t idx, bool do_64bit, uint64_t value)
121 {
122 if (do_64bit) {
123 uint64_t *dst64 = (uint64_t *) dst;
124 dst64[idx] = value;
125 } else {
126 uint32_t *dst32 = (uint32_t *) dst;
127 dst32[idx] = (uint32_t) value;
128 }
129 }
130
131 static uint64_t
get_occlusion_query_result(struct v3dv_device * device,struct v3dv_query_pool * pool,uint32_t query,bool do_wait,bool * available)132 get_occlusion_query_result(struct v3dv_device *device,
133 struct v3dv_query_pool *pool,
134 uint32_t query,
135 bool do_wait,
136 bool *available)
137 {
138 assert(pool && pool->query_type == VK_QUERY_TYPE_OCCLUSION);
139
140 struct v3dv_query *q = &pool->queries[query];
141 assert(q->bo && q->bo->map);
142
143 if (do_wait) {
144 /* From the Vulkan 1.0 spec:
145 *
146 * "If VK_QUERY_RESULT_WAIT_BIT is set, (...) If the query does not
147 * become available in a finite amount of time (e.g. due to not
148 * issuing a query since the last reset), a VK_ERROR_DEVICE_LOST
149 * error may occur."
150 */
151 if (!q->maybe_available)
152 return vk_error(device->instance, VK_ERROR_DEVICE_LOST);
153
154 if (!v3dv_bo_wait(device, q->bo, 0xffffffffffffffffull))
155 return vk_error(device->instance, VK_ERROR_DEVICE_LOST);
156
157 *available = true;
158 } else {
159 *available = q->maybe_available && v3dv_bo_wait(device, q->bo, 0);
160 }
161
162 return (uint64_t) *((uint32_t *) q->bo->map);
163 }
164
165 static uint64_t
get_timestamp_query_result(struct v3dv_device * device,struct v3dv_query_pool * pool,uint32_t query,bool do_wait,bool * available)166 get_timestamp_query_result(struct v3dv_device *device,
167 struct v3dv_query_pool *pool,
168 uint32_t query,
169 bool do_wait,
170 bool *available)
171 {
172 assert(pool && pool->query_type == VK_QUERY_TYPE_TIMESTAMP);
173
174 struct v3dv_query *q = &pool->queries[query];
175
176 if (do_wait) {
177 /* From the Vulkan 1.0 spec:
178 *
179 * "If VK_QUERY_RESULT_WAIT_BIT is set, (...) If the query does not
180 * become available in a finite amount of time (e.g. due to not
181 * issuing a query since the last reset), a VK_ERROR_DEVICE_LOST
182 * error may occur."
183 */
184 if (!q->maybe_available)
185 return vk_error(device->instance, VK_ERROR_DEVICE_LOST);
186
187 *available = true;
188 } else {
189 *available = q->maybe_available;
190 }
191
192 return q->value;
193 }
194
195 static uint64_t
get_query_result(struct v3dv_device * device,struct v3dv_query_pool * pool,uint32_t query,bool do_wait,bool * available)196 get_query_result(struct v3dv_device *device,
197 struct v3dv_query_pool *pool,
198 uint32_t query,
199 bool do_wait,
200 bool *available)
201 {
202 switch (pool->query_type) {
203 case VK_QUERY_TYPE_OCCLUSION:
204 return get_occlusion_query_result(device, pool, query, do_wait, available);
205 case VK_QUERY_TYPE_TIMESTAMP:
206 return get_timestamp_query_result(device, pool, query, do_wait, available);
207 default:
208 unreachable("Unsupported query type");
209 }
210 }
211
212 VkResult
v3dv_get_query_pool_results_cpu(struct v3dv_device * device,struct v3dv_query_pool * pool,uint32_t first,uint32_t count,void * data,VkDeviceSize stride,VkQueryResultFlags flags)213 v3dv_get_query_pool_results_cpu(struct v3dv_device *device,
214 struct v3dv_query_pool *pool,
215 uint32_t first,
216 uint32_t count,
217 void *data,
218 VkDeviceSize stride,
219 VkQueryResultFlags flags)
220 {
221 assert(first < pool->query_count);
222 assert(first + count <= pool->query_count);
223 assert(data);
224
225 const bool do_64bit = flags & VK_QUERY_RESULT_64_BIT;
226 const bool do_wait = flags & VK_QUERY_RESULT_WAIT_BIT;
227 const bool do_partial = flags & VK_QUERY_RESULT_PARTIAL_BIT;
228
229 VkResult result = VK_SUCCESS;
230 for (uint32_t i = first; i < first + count; i++) {
231 bool available;
232 uint64_t value = get_query_result(device, pool, i, do_wait, &available);
233
234 /**
235 * From the Vulkan 1.0 spec:
236 *
237 * "If VK_QUERY_RESULT_WAIT_BIT and VK_QUERY_RESULT_PARTIAL_BIT are
238 * both not set then no result values are written to pData for queries
239 * that are in the unavailable state at the time of the call, and
240 * vkGetQueryPoolResults returns VK_NOT_READY. However, availability
241 * state is still written to pData for those queries if
242 * VK_QUERY_RESULT_WITH_AVAILABILITY_BIT is set."
243 */
244 uint32_t slot = 0;
245
246 const bool write_result = available || do_partial;
247 if (write_result)
248 write_query_result(data, slot, do_64bit, value);
249 slot++;
250
251 if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT)
252 write_query_result(data, slot++, do_64bit, available ? 1u : 0u);
253
254 if (!write_result)
255 result = VK_NOT_READY;
256
257 data += stride;
258 }
259
260 return result;
261 }
262
263 VkResult
v3dv_GetQueryPoolResults(VkDevice _device,VkQueryPool queryPool,uint32_t firstQuery,uint32_t queryCount,size_t dataSize,void * pData,VkDeviceSize stride,VkQueryResultFlags flags)264 v3dv_GetQueryPoolResults(VkDevice _device,
265 VkQueryPool queryPool,
266 uint32_t firstQuery,
267 uint32_t queryCount,
268 size_t dataSize,
269 void *pData,
270 VkDeviceSize stride,
271 VkQueryResultFlags flags)
272 {
273 V3DV_FROM_HANDLE(v3dv_device, device, _device);
274 V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
275
276 return v3dv_get_query_pool_results_cpu(device, pool, firstQuery, queryCount,
277 pData, stride, flags);
278 }
279
280 void
v3dv_CmdResetQueryPool(VkCommandBuffer commandBuffer,VkQueryPool queryPool,uint32_t firstQuery,uint32_t queryCount)281 v3dv_CmdResetQueryPool(VkCommandBuffer commandBuffer,
282 VkQueryPool queryPool,
283 uint32_t firstQuery,
284 uint32_t queryCount)
285 {
286 V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer);
287 V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
288
289 v3dv_cmd_buffer_reset_queries(cmd_buffer, pool, firstQuery, queryCount);
290 }
291
292 void
v3dv_CmdCopyQueryPoolResults(VkCommandBuffer commandBuffer,VkQueryPool queryPool,uint32_t firstQuery,uint32_t queryCount,VkBuffer dstBuffer,VkDeviceSize dstOffset,VkDeviceSize stride,VkQueryResultFlags flags)293 v3dv_CmdCopyQueryPoolResults(VkCommandBuffer commandBuffer,
294 VkQueryPool queryPool,
295 uint32_t firstQuery,
296 uint32_t queryCount,
297 VkBuffer dstBuffer,
298 VkDeviceSize dstOffset,
299 VkDeviceSize stride,
300 VkQueryResultFlags flags)
301 {
302 V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer);
303 V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
304 V3DV_FROM_HANDLE(v3dv_buffer, dst, dstBuffer);
305
306 v3dv_cmd_buffer_copy_query_results(cmd_buffer, pool,
307 firstQuery, queryCount,
308 dst, dstOffset, stride, flags);
309 }
310
311 void
v3dv_CmdBeginQuery(VkCommandBuffer commandBuffer,VkQueryPool queryPool,uint32_t query,VkQueryControlFlags flags)312 v3dv_CmdBeginQuery(VkCommandBuffer commandBuffer,
313 VkQueryPool queryPool,
314 uint32_t query,
315 VkQueryControlFlags flags)
316 {
317 V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer);
318 V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
319
320 v3dv_cmd_buffer_begin_query(cmd_buffer, pool, query, flags);
321 }
322
323 void
v3dv_CmdEndQuery(VkCommandBuffer commandBuffer,VkQueryPool queryPool,uint32_t query)324 v3dv_CmdEndQuery(VkCommandBuffer commandBuffer,
325 VkQueryPool queryPool,
326 uint32_t query)
327 {
328 V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer);
329 V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
330
331 v3dv_cmd_buffer_end_query(cmd_buffer, pool, query);
332 }
333