17ec681f3Smrg/*
27ec681f3Smrg * Copyright © 2020 Raspberry Pi
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
217ec681f3Smrg * IN THE SOFTWARE.
227ec681f3Smrg */
237ec681f3Smrg
247ec681f3Smrg#include "v3dv_private.h"
257ec681f3Smrg
267ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
277ec681f3Smrgv3dv_CreateQueryPool(VkDevice _device,
287ec681f3Smrg                     const VkQueryPoolCreateInfo *pCreateInfo,
297ec681f3Smrg                     const VkAllocationCallbacks *pAllocator,
307ec681f3Smrg                     VkQueryPool *pQueryPool)
317ec681f3Smrg{
327ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_device, device, _device);
337ec681f3Smrg
347ec681f3Smrg   assert(pCreateInfo->queryType == VK_QUERY_TYPE_OCCLUSION ||
357ec681f3Smrg          pCreateInfo->queryType == VK_QUERY_TYPE_TIMESTAMP);
367ec681f3Smrg   assert(pCreateInfo->queryCount > 0);
377ec681f3Smrg
387ec681f3Smrg   struct v3dv_query_pool *pool =
397ec681f3Smrg      vk_object_zalloc(&device->vk, pAllocator, sizeof(*pool),
407ec681f3Smrg                       VK_OBJECT_TYPE_QUERY_POOL);
417ec681f3Smrg   if (pool == NULL)
427ec681f3Smrg      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
437ec681f3Smrg
447ec681f3Smrg   pool->query_type = pCreateInfo->queryType;
457ec681f3Smrg   pool->query_count = pCreateInfo->queryCount;
467ec681f3Smrg
477ec681f3Smrg   VkResult result;
487ec681f3Smrg
497ec681f3Smrg   const uint32_t pool_bytes = sizeof(struct v3dv_query) * pool->query_count;
507ec681f3Smrg   pool->queries = vk_alloc2(&device->vk.alloc, pAllocator, pool_bytes, 8,
517ec681f3Smrg                             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
527ec681f3Smrg   if (pool->queries == NULL) {
537ec681f3Smrg      result = vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
547ec681f3Smrg      goto fail;
557ec681f3Smrg   }
567ec681f3Smrg
577ec681f3Smrg   if (pool->query_type == VK_QUERY_TYPE_OCCLUSION) {
587ec681f3Smrg      /* The hardware allows us to setup groups of 16 queries in consecutive
597ec681f3Smrg       * 4-byte addresses, requiring only that each group of 16 queries is
607ec681f3Smrg       * aligned to a 1024 byte boundary.
617ec681f3Smrg       */
627ec681f3Smrg      const uint32_t query_groups = DIV_ROUND_UP(pool->query_count, 16);
637ec681f3Smrg      const uint32_t bo_size = query_groups * 1024;
647ec681f3Smrg      pool->bo = v3dv_bo_alloc(device, bo_size, "query", true);
657ec681f3Smrg      if (!pool->bo) {
667ec681f3Smrg         result = vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
677ec681f3Smrg         goto fail;
687ec681f3Smrg      }
697ec681f3Smrg      if (!v3dv_bo_map(device, pool->bo, bo_size)) {
707ec681f3Smrg         result = vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
717ec681f3Smrg         goto fail;
727ec681f3Smrg      }
737ec681f3Smrg   }
747ec681f3Smrg
757ec681f3Smrg   uint32_t i;
767ec681f3Smrg   for (i = 0; i < pool->query_count; i++) {
777ec681f3Smrg      pool->queries[i].maybe_available = false;
787ec681f3Smrg      switch (pool->query_type) {
797ec681f3Smrg      case VK_QUERY_TYPE_OCCLUSION: {
807ec681f3Smrg         const uint32_t query_group = i / 16;
817ec681f3Smrg         const uint32_t query_offset = query_group * 1024 + (i % 16) * 4;
827ec681f3Smrg         pool->queries[i].bo = pool->bo;
837ec681f3Smrg         pool->queries[i].offset = query_offset;
847ec681f3Smrg         break;
857ec681f3Smrg         }
867ec681f3Smrg      case VK_QUERY_TYPE_TIMESTAMP:
877ec681f3Smrg         pool->queries[i].value = 0;
887ec681f3Smrg         break;
897ec681f3Smrg      default:
907ec681f3Smrg         unreachable("Unsupported query type");
917ec681f3Smrg      }
927ec681f3Smrg   }
937ec681f3Smrg
947ec681f3Smrg   *pQueryPool = v3dv_query_pool_to_handle(pool);
957ec681f3Smrg
967ec681f3Smrg   return VK_SUCCESS;
977ec681f3Smrg
987ec681f3Smrgfail:
997ec681f3Smrg   if (pool->bo)
1007ec681f3Smrg      v3dv_bo_free(device, pool->bo);
1017ec681f3Smrg   if (pool->queries)
1027ec681f3Smrg      vk_free2(&device->vk.alloc, pAllocator, pool->queries);
1037ec681f3Smrg   vk_object_free(&device->vk, pAllocator, pool);
1047ec681f3Smrg
1057ec681f3Smrg   return result;
1067ec681f3Smrg}
1077ec681f3Smrg
1087ec681f3SmrgVKAPI_ATTR void VKAPI_CALL
1097ec681f3Smrgv3dv_DestroyQueryPool(VkDevice _device,
1107ec681f3Smrg                      VkQueryPool queryPool,
1117ec681f3Smrg                      const VkAllocationCallbacks *pAllocator)
1127ec681f3Smrg{
1137ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_device, device, _device);
1147ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
1157ec681f3Smrg
1167ec681f3Smrg   if (!pool)
1177ec681f3Smrg      return;
1187ec681f3Smrg
1197ec681f3Smrg   if (pool->bo)
1207ec681f3Smrg      v3dv_bo_free(device, pool->bo);
1217ec681f3Smrg
1227ec681f3Smrg   if (pool->queries)
1237ec681f3Smrg      vk_free2(&device->vk.alloc, pAllocator, pool->queries);
1247ec681f3Smrg
1257ec681f3Smrg   vk_object_free(&device->vk, pAllocator, pool);
1267ec681f3Smrg}
1277ec681f3Smrg
1287ec681f3Smrgstatic void
1297ec681f3Smrgwrite_query_result(void *dst, uint32_t idx, bool do_64bit, uint64_t value)
1307ec681f3Smrg{
1317ec681f3Smrg   if (do_64bit) {
1327ec681f3Smrg      uint64_t *dst64 = (uint64_t *) dst;
1337ec681f3Smrg      dst64[idx] = value;
1347ec681f3Smrg   } else {
1357ec681f3Smrg      uint32_t *dst32 = (uint32_t *) dst;
1367ec681f3Smrg      dst32[idx] = (uint32_t) value;
1377ec681f3Smrg   }
1387ec681f3Smrg}
1397ec681f3Smrg
1407ec681f3Smrgstatic VkResult
1417ec681f3Smrgget_occlusion_query_result(struct v3dv_device *device,
1427ec681f3Smrg                           struct v3dv_query_pool *pool,
1437ec681f3Smrg                           uint32_t query,
1447ec681f3Smrg                           bool do_wait,
1457ec681f3Smrg                           bool *available,
1467ec681f3Smrg                           uint64_t *value)
1477ec681f3Smrg{
1487ec681f3Smrg   assert(pool && pool->query_type == VK_QUERY_TYPE_OCCLUSION);
1497ec681f3Smrg
1507ec681f3Smrg   struct v3dv_query *q = &pool->queries[query];
1517ec681f3Smrg   assert(q->bo && q->bo->map);
1527ec681f3Smrg
1537ec681f3Smrg   if (do_wait) {
1547ec681f3Smrg      /* From the Vulkan 1.0 spec:
1557ec681f3Smrg       *
1567ec681f3Smrg       *    "If VK_QUERY_RESULT_WAIT_BIT is set, (...) If the query does not
1577ec681f3Smrg       *     become available in a finite amount of time (e.g. due to not
1587ec681f3Smrg       *     issuing a query since the last reset), a VK_ERROR_DEVICE_LOST
1597ec681f3Smrg       *     error may occur."
1607ec681f3Smrg       */
1617ec681f3Smrg      if (!q->maybe_available)
1627ec681f3Smrg         return vk_error(device, VK_ERROR_DEVICE_LOST);
1637ec681f3Smrg
1647ec681f3Smrg      if (!v3dv_bo_wait(device, q->bo, 0xffffffffffffffffull))
1657ec681f3Smrg         return vk_error(device, VK_ERROR_DEVICE_LOST);
1667ec681f3Smrg
1677ec681f3Smrg      *available = true;
1687ec681f3Smrg   } else {
1697ec681f3Smrg      *available = q->maybe_available && v3dv_bo_wait(device, q->bo, 0);
1707ec681f3Smrg   }
1717ec681f3Smrg
1727ec681f3Smrg   const uint8_t *query_addr = ((uint8_t *) q->bo->map) + q->offset;
1737ec681f3Smrg   *value = (uint64_t) *((uint32_t *)query_addr);
1747ec681f3Smrg   return VK_SUCCESS;
1757ec681f3Smrg}
1767ec681f3Smrg
1777ec681f3Smrgstatic VkResult
1787ec681f3Smrgget_timestamp_query_result(struct v3dv_device *device,
1797ec681f3Smrg                           struct v3dv_query_pool *pool,
1807ec681f3Smrg                           uint32_t query,
1817ec681f3Smrg                           bool do_wait,
1827ec681f3Smrg                           bool *available,
1837ec681f3Smrg                           uint64_t *value)
1847ec681f3Smrg{
1857ec681f3Smrg   assert(pool && pool->query_type == VK_QUERY_TYPE_TIMESTAMP);
1867ec681f3Smrg
1877ec681f3Smrg   struct v3dv_query *q = &pool->queries[query];
1887ec681f3Smrg
1897ec681f3Smrg   if (do_wait) {
1907ec681f3Smrg      /* From the Vulkan 1.0 spec:
1917ec681f3Smrg       *
1927ec681f3Smrg       *    "If VK_QUERY_RESULT_WAIT_BIT is set, (...) If the query does not
1937ec681f3Smrg       *     become available in a finite amount of time (e.g. due to not
1947ec681f3Smrg       *     issuing a query since the last reset), a VK_ERROR_DEVICE_LOST
1957ec681f3Smrg       *     error may occur."
1967ec681f3Smrg       */
1977ec681f3Smrg      if (!q->maybe_available)
1987ec681f3Smrg         return vk_error(device, VK_ERROR_DEVICE_LOST);
1997ec681f3Smrg
2007ec681f3Smrg      *available = true;
2017ec681f3Smrg   } else {
2027ec681f3Smrg      *available = q->maybe_available;
2037ec681f3Smrg   }
2047ec681f3Smrg
2057ec681f3Smrg   *value = q->value;
2067ec681f3Smrg   return VK_SUCCESS;
2077ec681f3Smrg}
2087ec681f3Smrg
2097ec681f3Smrgstatic VkResult
2107ec681f3Smrgget_query_result(struct v3dv_device *device,
2117ec681f3Smrg                 struct v3dv_query_pool *pool,
2127ec681f3Smrg                 uint32_t query,
2137ec681f3Smrg                 bool do_wait,
2147ec681f3Smrg                 bool *available,
2157ec681f3Smrg                 uint64_t *value)
2167ec681f3Smrg{
2177ec681f3Smrg   switch (pool->query_type) {
2187ec681f3Smrg   case VK_QUERY_TYPE_OCCLUSION:
2197ec681f3Smrg      return get_occlusion_query_result(device, pool, query, do_wait,
2207ec681f3Smrg                                        available, value);
2217ec681f3Smrg   case VK_QUERY_TYPE_TIMESTAMP:
2227ec681f3Smrg      return get_timestamp_query_result(device, pool, query, do_wait,
2237ec681f3Smrg                                        available, value);
2247ec681f3Smrg   default:
2257ec681f3Smrg      unreachable("Unsupported query type");
2267ec681f3Smrg   }
2277ec681f3Smrg}
2287ec681f3Smrg
2297ec681f3SmrgVkResult
2307ec681f3Smrgv3dv_get_query_pool_results_cpu(struct v3dv_device *device,
2317ec681f3Smrg                                struct v3dv_query_pool *pool,
2327ec681f3Smrg                                uint32_t first,
2337ec681f3Smrg                                uint32_t count,
2347ec681f3Smrg                                void *data,
2357ec681f3Smrg                                VkDeviceSize stride,
2367ec681f3Smrg                                VkQueryResultFlags flags)
2377ec681f3Smrg{
2387ec681f3Smrg   assert(first < pool->query_count);
2397ec681f3Smrg   assert(first + count <= pool->query_count);
2407ec681f3Smrg   assert(data);
2417ec681f3Smrg
2427ec681f3Smrg   const bool do_64bit = flags & VK_QUERY_RESULT_64_BIT;
2437ec681f3Smrg   const bool do_wait = flags & VK_QUERY_RESULT_WAIT_BIT;
2447ec681f3Smrg   const bool do_partial = flags & VK_QUERY_RESULT_PARTIAL_BIT;
2457ec681f3Smrg
2467ec681f3Smrg   VkResult result = VK_SUCCESS;
2477ec681f3Smrg   for (uint32_t i = first; i < first + count; i++) {
2487ec681f3Smrg      bool available = false;
2497ec681f3Smrg      uint64_t value = 0;
2507ec681f3Smrg      VkResult query_result =
2517ec681f3Smrg         get_query_result(device, pool, i, do_wait, &available, &value);
2527ec681f3Smrg      if (query_result == VK_ERROR_DEVICE_LOST)
2537ec681f3Smrg         result = VK_ERROR_DEVICE_LOST;
2547ec681f3Smrg
2557ec681f3Smrg      /**
2567ec681f3Smrg       * From the Vulkan 1.0 spec:
2577ec681f3Smrg       *
2587ec681f3Smrg       *    "If VK_QUERY_RESULT_WAIT_BIT and VK_QUERY_RESULT_PARTIAL_BIT are
2597ec681f3Smrg       *     both not set then no result values are written to pData for queries
2607ec681f3Smrg       *     that are in the unavailable state at the time of the call, and
2617ec681f3Smrg       *     vkGetQueryPoolResults returns VK_NOT_READY. However, availability
2627ec681f3Smrg       *     state is still written to pData for those queries if
2637ec681f3Smrg       *     VK_QUERY_RESULT_WITH_AVAILABILITY_BIT is set."
2647ec681f3Smrg       */
2657ec681f3Smrg      uint32_t slot = 0;
2667ec681f3Smrg
2677ec681f3Smrg      const bool write_result = available || do_partial;
2687ec681f3Smrg      if (write_result)
2697ec681f3Smrg         write_query_result(data, slot, do_64bit, value);
2707ec681f3Smrg      slot++;
2717ec681f3Smrg
2727ec681f3Smrg      if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT)
2737ec681f3Smrg         write_query_result(data, slot++, do_64bit, available ? 1u : 0u);
2747ec681f3Smrg
2757ec681f3Smrg      if (!write_result && result != VK_ERROR_DEVICE_LOST)
2767ec681f3Smrg         result = VK_NOT_READY;
2777ec681f3Smrg
2787ec681f3Smrg      data += stride;
2797ec681f3Smrg   }
2807ec681f3Smrg
2817ec681f3Smrg   return result;
2827ec681f3Smrg}
2837ec681f3Smrg
2847ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
2857ec681f3Smrgv3dv_GetQueryPoolResults(VkDevice _device,
2867ec681f3Smrg                         VkQueryPool queryPool,
2877ec681f3Smrg                         uint32_t firstQuery,
2887ec681f3Smrg                         uint32_t queryCount,
2897ec681f3Smrg                         size_t dataSize,
2907ec681f3Smrg                         void *pData,
2917ec681f3Smrg                         VkDeviceSize stride,
2927ec681f3Smrg                         VkQueryResultFlags flags)
2937ec681f3Smrg{
2947ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_device, device, _device);
2957ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
2967ec681f3Smrg
2977ec681f3Smrg   return v3dv_get_query_pool_results_cpu(device, pool, firstQuery, queryCount,
2987ec681f3Smrg                                          pData, stride, flags);
2997ec681f3Smrg}
3007ec681f3Smrg
3017ec681f3SmrgVKAPI_ATTR void VKAPI_CALL
3027ec681f3Smrgv3dv_CmdResetQueryPool(VkCommandBuffer commandBuffer,
3037ec681f3Smrg                       VkQueryPool queryPool,
3047ec681f3Smrg                       uint32_t firstQuery,
3057ec681f3Smrg                       uint32_t queryCount)
3067ec681f3Smrg{
3077ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer);
3087ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
3097ec681f3Smrg
3107ec681f3Smrg   v3dv_cmd_buffer_reset_queries(cmd_buffer, pool, firstQuery, queryCount);
3117ec681f3Smrg}
3127ec681f3Smrg
3137ec681f3SmrgVKAPI_ATTR void VKAPI_CALL
3147ec681f3Smrgv3dv_CmdCopyQueryPoolResults(VkCommandBuffer commandBuffer,
3157ec681f3Smrg                             VkQueryPool queryPool,
3167ec681f3Smrg                             uint32_t firstQuery,
3177ec681f3Smrg                             uint32_t queryCount,
3187ec681f3Smrg                             VkBuffer dstBuffer,
3197ec681f3Smrg                             VkDeviceSize dstOffset,
3207ec681f3Smrg                             VkDeviceSize stride,
3217ec681f3Smrg                             VkQueryResultFlags flags)
3227ec681f3Smrg{
3237ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer);
3247ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
3257ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_buffer, dst, dstBuffer);
3267ec681f3Smrg
3277ec681f3Smrg   v3dv_cmd_buffer_copy_query_results(cmd_buffer, pool,
3287ec681f3Smrg                                      firstQuery, queryCount,
3297ec681f3Smrg                                      dst, dstOffset, stride, flags);
3307ec681f3Smrg}
3317ec681f3Smrg
3327ec681f3SmrgVKAPI_ATTR void VKAPI_CALL
3337ec681f3Smrgv3dv_CmdBeginQuery(VkCommandBuffer commandBuffer,
3347ec681f3Smrg                   VkQueryPool queryPool,
3357ec681f3Smrg                   uint32_t query,
3367ec681f3Smrg                   VkQueryControlFlags flags)
3377ec681f3Smrg{
3387ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer);
3397ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
3407ec681f3Smrg
3417ec681f3Smrg   v3dv_cmd_buffer_begin_query(cmd_buffer, pool, query, flags);
3427ec681f3Smrg}
3437ec681f3Smrg
3447ec681f3SmrgVKAPI_ATTR void VKAPI_CALL
3457ec681f3Smrgv3dv_CmdEndQuery(VkCommandBuffer commandBuffer,
3467ec681f3Smrg                 VkQueryPool queryPool,
3477ec681f3Smrg                 uint32_t query)
3487ec681f3Smrg{
3497ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer);
3507ec681f3Smrg   V3DV_FROM_HANDLE(v3dv_query_pool, pool, queryPool);
3517ec681f3Smrg
3527ec681f3Smrg   v3dv_cmd_buffer_end_query(cmd_buffer, pool, query);
3537ec681f3Smrg}
354