1/*
2 * Copyright © 2019 Red Hat.
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 "lvp_private.h"
25#include "pipe/p_context.h"
26
27VKAPI_ATTR VkResult VKAPI_CALL lvp_CreateQueryPool(
28    VkDevice                                    _device,
29    const VkQueryPoolCreateInfo*                pCreateInfo,
30    const VkAllocationCallbacks*                pAllocator,
31    VkQueryPool*                                pQueryPool)
32{
33   LVP_FROM_HANDLE(lvp_device, device, _device);
34
35   enum pipe_query_type pipeq;
36   switch (pCreateInfo->queryType) {
37   case VK_QUERY_TYPE_OCCLUSION:
38      pipeq = PIPE_QUERY_OCCLUSION_COUNTER;
39      break;
40   case VK_QUERY_TYPE_TIMESTAMP:
41      pipeq = PIPE_QUERY_TIMESTAMP;
42      break;
43   case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT:
44      pipeq = PIPE_QUERY_SO_STATISTICS;
45      break;
46   case VK_QUERY_TYPE_PIPELINE_STATISTICS:
47      pipeq = PIPE_QUERY_PIPELINE_STATISTICS;
48      break;
49   default:
50      return VK_ERROR_FEATURE_NOT_PRESENT;
51   }
52   struct lvp_query_pool *pool;
53   uint32_t pool_size = sizeof(*pool) + pCreateInfo->queryCount * sizeof(struct pipe_query *);
54
55   pool = vk_zalloc2(&device->vk.alloc, pAllocator,
56                    pool_size, 8,
57                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
58   if (!pool)
59      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
60
61   vk_object_base_init(&device->vk, &pool->base,
62                       VK_OBJECT_TYPE_QUERY_POOL);
63   pool->type = pCreateInfo->queryType;
64   pool->count = pCreateInfo->queryCount;
65   pool->base_type = pipeq;
66   pool->pipeline_stats = pCreateInfo->pipelineStatistics;
67
68   *pQueryPool = lvp_query_pool_to_handle(pool);
69   return VK_SUCCESS;
70}
71
72VKAPI_ATTR void VKAPI_CALL lvp_DestroyQueryPool(
73    VkDevice                                    _device,
74    VkQueryPool                                 _pool,
75    const VkAllocationCallbacks*                pAllocator)
76{
77   LVP_FROM_HANDLE(lvp_device, device, _device);
78   LVP_FROM_HANDLE(lvp_query_pool, pool, _pool);
79
80   if (!pool)
81      return;
82
83   for (unsigned i = 0; i < pool->count; i++)
84      if (pool->queries[i])
85         device->queue.ctx->destroy_query(device->queue.ctx, pool->queries[i]);
86   vk_object_base_finish(&pool->base);
87   vk_free2(&device->vk.alloc, pAllocator, pool);
88}
89
90VKAPI_ATTR VkResult VKAPI_CALL lvp_GetQueryPoolResults(
91   VkDevice                                    _device,
92   VkQueryPool                                 queryPool,
93   uint32_t                                    firstQuery,
94   uint32_t                                    queryCount,
95   size_t                                      dataSize,
96   void*                                       pData,
97   VkDeviceSize                                stride,
98   VkQueryResultFlags                          flags)
99{
100   LVP_FROM_HANDLE(lvp_device, device, _device);
101   LVP_FROM_HANDLE(lvp_query_pool, pool, queryPool);
102   VkResult vk_result = VK_SUCCESS;
103
104   lvp_DeviceWaitIdle(_device);
105
106   for (unsigned i = firstQuery; i < firstQuery + queryCount; i++) {
107      uint8_t *dptr = (uint8_t *)((char *)pData + (stride * (i - firstQuery)));
108      union pipe_query_result result;
109      bool ready = false;
110      if (pool->queries[i]) {
111        ready = device->queue.ctx->get_query_result(device->queue.ctx,
112                                                    pool->queries[i],
113                                                    (flags & VK_QUERY_RESULT_WAIT_BIT),
114                                                    &result);
115      } else {
116        result.u64 = 0;
117      }
118
119      if (!ready && !(flags & VK_QUERY_RESULT_PARTIAL_BIT))
120          vk_result = VK_NOT_READY;
121      if (flags & VK_QUERY_RESULT_64_BIT) {
122         if (ready || (flags & VK_QUERY_RESULT_PARTIAL_BIT)) {
123            if (pool->type == VK_QUERY_TYPE_PIPELINE_STATISTICS) {
124               uint32_t mask = pool->pipeline_stats;
125               uint64_t *pstats = (uint64_t *)&result.pipeline_statistics;
126               while (mask) {
127                  uint32_t i = u_bit_scan(&mask);
128
129                  *(uint64_t *)dptr = pstats[i];
130                  dptr += 8;
131               }
132            } else if (pool->type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) {
133               *(uint64_t *)dptr = result.so_statistics.num_primitives_written;
134               dptr += 8;
135               *(uint64_t *)dptr = result.so_statistics.primitives_storage_needed;
136               dptr += 8;
137            } else {
138               *(uint64_t *)dptr = result.u64;
139               dptr += 8;
140            }
141         } else {
142            if (pool->type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)
143               dptr += 16;
144            else
145               dptr += 8;
146         }
147
148      } else {
149         if (ready || (flags & VK_QUERY_RESULT_PARTIAL_BIT)) {
150            if (pool->type == VK_QUERY_TYPE_PIPELINE_STATISTICS) {
151               uint32_t mask = pool->pipeline_stats;
152               uint64_t *pstats = (uint64_t *)&result.pipeline_statistics;
153               while (mask) {
154                  uint32_t i = u_bit_scan(&mask);
155
156                  if (pstats[i] > UINT32_MAX)
157                     *(uint32_t *)dptr = UINT32_MAX;
158                  else
159                     *(uint32_t *)dptr = pstats[i];
160                  dptr += 4;
161               }
162            } else if (pool->type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) {
163               if (result.so_statistics.num_primitives_written > UINT32_MAX)
164                  *(uint32_t *)dptr = UINT32_MAX;
165               else
166                  *(uint32_t *)dptr = (uint32_t)result.so_statistics.num_primitives_written;
167               dptr += 4;
168               if (result.so_statistics.primitives_storage_needed > UINT32_MAX)
169                  *(uint32_t *)dptr = UINT32_MAX;
170               else
171                  *(uint32_t *)dptr = (uint32_t)result.so_statistics.primitives_storage_needed;
172               dptr += 4;
173            } else {
174               if (result.u64 > UINT32_MAX)
175                  *(uint32_t *)dptr = UINT32_MAX;
176               else
177                  *(uint32_t *)dptr = result.u32;
178               dptr += 4;
179            }
180         } else
181            if (pool->type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)
182               dptr += 8;
183            else
184               dptr += 4;
185      }
186
187      if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) {
188        if (flags & VK_QUERY_RESULT_64_BIT)
189           *(uint64_t *)dptr = ready;
190        else
191           *(uint32_t *)dptr = ready;
192      }
193   }
194   return vk_result;
195}
196
197VKAPI_ATTR void VKAPI_CALL lvp_ResetQueryPool(
198   VkDevice                                    _device,
199   VkQueryPool                                 queryPool,
200   uint32_t                                    firstQuery,
201   uint32_t                                    queryCount)
202{
203   LVP_FROM_HANDLE(lvp_device, device, _device);
204   LVP_FROM_HANDLE(lvp_query_pool, pool, queryPool);
205
206   for (uint32_t i = 0; i < queryCount; i++) {
207      uint32_t idx = i + firstQuery;
208
209      if (pool->queries[idx]) {
210         device->queue.ctx->destroy_query(device->queue.ctx, pool->queries[idx]);
211         pool->queries[idx] = NULL;
212      }
213   }
214}
215