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