1/*
2 * Copyright (c) 2017 Etnaviv Project
3 * Copyright (C) 2017 Zodiac Inflight Innovations
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sub license,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial portions
14 * of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 *    Rob Clark <robclark@freedesktop.org>
26 *    Christian Gmeiner <christian.gmeiner@gmail.com>
27 */
28
29#include "util/u_inlines.h"
30#include "util/u_memory.h"
31
32#include "etnaviv_context.h"
33#include "etnaviv_debug.h"
34#include "etnaviv_emit.h"
35#include "etnaviv_query_hw.h"
36#include "etnaviv_screen.h"
37
38/*
39 * Occlusion Query:
40 *
41 * OCCLUSION_COUNTER and OCCLUSION_PREDICATE differ only in how they
42 * interpret results
43 */
44
45static void
46occlusion_start(struct etna_hw_query *hq, struct etna_context *ctx)
47{
48   struct etna_resource *rsc = etna_resource(hq->prsc);
49   struct etna_reloc r = {
50      .bo = rsc->bo,
51      .flags = ETNA_RELOC_WRITE
52   };
53
54   if (hq->samples > 63) {
55      hq->samples = 63;
56      BUG("samples overflow");
57   }
58
59   r.offset = hq->samples * 8; /* 64bit value */
60
61   etna_set_state_reloc(ctx->stream, VIVS_GL_OCCLUSION_QUERY_ADDR, &r);
62}
63
64static void
65occlusion_stop(struct etna_hw_query *hq, struct etna_context *ctx)
66{
67   /* 0x1DF5E76 is the value used by blob - but any random value will work */
68   etna_set_state(ctx->stream, VIVS_GL_OCCLUSION_QUERY_CONTROL, 0x1DF5E76);
69}
70
71static void
72occlusion_suspend(struct etna_hw_query *hq, struct etna_context *ctx)
73{
74   occlusion_stop(hq, ctx);
75}
76
77static void
78occlusion_resume(struct etna_hw_query *hq, struct etna_context *ctx)
79{
80   hq->samples++;
81   occlusion_start(hq, ctx);
82}
83
84static void
85occlusion_result(struct etna_hw_query *hq, void *buf,
86                         union pipe_query_result *result)
87{
88   uint64_t sum = 0;
89   uint64_t *ptr = (uint64_t *)buf;
90
91   for (unsigned i = 0; i <= hq->samples; i++)
92      sum += *(ptr + i);
93
94   if (hq->base.type == PIPE_QUERY_OCCLUSION_COUNTER)
95      result->u64 = sum;
96   else
97      result->b = !!sum;
98}
99
100static void
101etna_hw_destroy_query(struct etna_context *ctx, struct etna_query *q)
102{
103   struct etna_hw_query *hq = etna_hw_query(q);
104
105   pipe_resource_reference(&hq->prsc, NULL);
106   list_del(&hq->node);
107
108   FREE(hq);
109}
110
111static const struct etna_hw_sample_provider occlusion_provider = {
112   .start = occlusion_start,
113   .stop = occlusion_stop,
114   .suspend = occlusion_suspend,
115   .resume = occlusion_resume,
116   .result = occlusion_result,
117};
118
119static void
120realloc_query_bo(struct etna_context *ctx, struct etna_hw_query *hq)
121{
122   struct etna_resource *rsc;
123   void *map;
124
125   pipe_resource_reference(&hq->prsc, NULL);
126
127   /* allocate resource with space for 64 * 64bit values */
128   hq->prsc = pipe_buffer_create(&ctx->screen->base, PIPE_BIND_QUERY_BUFFER,
129                                 0, 0x1000);
130
131   /* don't assume the buffer is zero-initialized */
132   rsc = etna_resource(hq->prsc);
133
134   etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_WRITE);
135
136   map = etna_bo_map(rsc->bo);
137   memset(map, 0, 0x1000);
138   etna_bo_cpu_fini(rsc->bo);
139}
140
141static boolean
142etna_hw_begin_query(struct etna_context *ctx, struct etna_query *q)
143{
144   struct etna_hw_query *hq = etna_hw_query(q);
145   const struct etna_hw_sample_provider *p = hq->provider;
146
147   /* ->begin_query() discards previous results, so realloc bo */
148   realloc_query_bo(ctx, hq);
149
150   p->start(hq, ctx);
151
152   /* add to active list */
153   assert(list_empty(&hq->node));
154   list_addtail(&hq->node, &ctx->active_hw_queries);
155
156   return true;
157}
158
159static void
160etna_hw_end_query(struct etna_context *ctx, struct etna_query *q)
161{
162   struct etna_hw_query *hq = etna_hw_query(q);
163   const struct etna_hw_sample_provider *p = hq->provider;
164
165   p->stop(hq, ctx);
166
167   /* remove from active list */
168   list_delinit(&hq->node);
169}
170
171static boolean
172etna_hw_get_query_result(struct etna_context *ctx, struct etna_query *q,
173                         boolean wait, union pipe_query_result *result)
174{
175   struct etna_hw_query *hq = etna_hw_query(q);
176   struct etna_resource *rsc = etna_resource(hq->prsc);
177   const struct etna_hw_sample_provider *p = hq->provider;
178
179   assert(LIST_IS_EMPTY(&hq->node));
180
181   if (!wait) {
182      int ret;
183
184      if (rsc->status & ETNA_PENDING_WRITE) {
185         /* piglit spec@arb_occlusion_query@occlusion_query_conform
186          * test, and silly apps perhaps, get stuck in a loop trying
187          * to get query result forever with wait==false..  we don't
188          * wait to flush unnecessarily but we also don't want to
189          * spin forever.
190          */
191         if (hq->no_wait_cnt++ > 5)
192            ctx->base.flush(&ctx->base, NULL, 0);
193         return false;
194      }
195
196      ret = etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_READ | DRM_ETNA_PREP_NOSYNC);
197      if (ret)
198         return false;
199
200      etna_bo_cpu_fini(rsc->bo);
201   }
202
203   /* flush that GPU executes all query related actions */
204   ctx->base.flush(&ctx->base, NULL, 0);
205
206   /* get the result */
207   etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_READ);
208
209   void *ptr = etna_bo_map(rsc->bo);
210   p->result(hq, ptr, result);
211
212   etna_bo_cpu_fini(rsc->bo);
213
214   return true;
215}
216
217static const struct etna_query_funcs hw_query_funcs = {
218   .destroy_query = etna_hw_destroy_query,
219   .begin_query = etna_hw_begin_query,
220   .end_query = etna_hw_end_query,
221   .get_query_result = etna_hw_get_query_result,
222};
223
224static inline const struct etna_hw_sample_provider *
225query_sample_provider(unsigned query_type)
226{
227   switch (query_type) {
228   case PIPE_QUERY_OCCLUSION_COUNTER:
229      /* fallthrough */
230   case PIPE_QUERY_OCCLUSION_PREDICATE:
231      /* fallthrough */
232   case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
233      return &occlusion_provider;
234   default:
235      return NULL;
236   }
237}
238
239struct etna_query *
240etna_hw_create_query(struct etna_context *ctx, unsigned query_type)
241{
242   struct etna_hw_query *hq;
243   struct etna_query *q;
244   const struct etna_hw_sample_provider *p;
245
246   p = query_sample_provider(query_type);
247   if (!p)
248      return NULL;
249
250   hq = CALLOC_STRUCT(etna_hw_query);
251   if (!hq)
252      return NULL;
253
254   hq->provider = p;
255
256   list_inithead(&hq->node);
257
258   q = &hq->base;
259   q->funcs = &hw_query_funcs;
260   q->type = query_type;
261
262   return q;
263}
264