etnaviv: reset no_wait_cnt after triggered flush
[mesa.git] / src / gallium / drivers / etnaviv / etnaviv_query_acc.c
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_acc.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
45 static void
46 occlusion_resume(struct etna_acc_query *aq, struct etna_context *ctx)
47 {
48 struct etna_resource *rsc = etna_resource(aq->prsc);
49 struct etna_reloc r = {
50 .bo = rsc->bo,
51 .flags = ETNA_RELOC_WRITE
52 };
53
54 if (aq->samples > 63) {
55 aq->samples = 63;
56 BUG("samples overflow");
57 }
58
59 r.offset = aq->samples * 8; /* 64bit value */
60
61 etna_set_state_reloc(ctx->stream, VIVS_GL_OCCLUSION_QUERY_ADDR, &r);
62 resource_written(ctx, aq->prsc);
63 }
64
65 static void
66 occlusion_suspend(struct etna_acc_query *aq, struct etna_context *ctx)
67 {
68 /* 0x1DF5E76 is the value used by blob - but any random value will work */
69 etna_set_state(ctx->stream, VIVS_GL_OCCLUSION_QUERY_CONTROL, 0x1DF5E76);
70 resource_written(ctx, aq->prsc);
71 }
72
73 static void
74 occlusion_result(struct etna_acc_query *aq, void *buf,
75 union pipe_query_result *result)
76 {
77 uint64_t sum = 0;
78 uint64_t *ptr = (uint64_t *)buf;
79
80 for (unsigned i = 0; i < aq->samples; i++)
81 sum += *(ptr + i);
82
83 if (aq->base.type == PIPE_QUERY_OCCLUSION_COUNTER)
84 result->u64 = sum;
85 else
86 result->b = !!sum;
87 }
88
89 static void
90 etna_acc_destroy_query(struct etna_context *ctx, struct etna_query *q)
91 {
92 struct etna_acc_query *aq = etna_acc_query(q);
93
94 pipe_resource_reference(&aq->prsc, NULL);
95 list_del(&aq->node);
96
97 FREE(aq);
98 }
99
100 static const struct etna_acc_sample_provider occlusion_provider = {
101 .suspend = occlusion_suspend,
102 .resume = occlusion_resume,
103 .result = occlusion_result,
104 };
105
106 static void
107 realloc_query_bo(struct etna_context *ctx, struct etna_acc_query *aq)
108 {
109 struct etna_resource *rsc;
110 void *map;
111
112 pipe_resource_reference(&aq->prsc, NULL);
113
114 /* allocate resource with space for 64 * 64bit values */
115 aq->prsc = pipe_buffer_create(&ctx->screen->base, PIPE_BIND_QUERY_BUFFER,
116 0, 0x1000);
117
118 /* don't assume the buffer is zero-initialized */
119 rsc = etna_resource(aq->prsc);
120
121 etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_WRITE);
122
123 map = etna_bo_map(rsc->bo);
124 memset(map, 0, 0x1000);
125 etna_bo_cpu_fini(rsc->bo);
126 }
127
128 static bool
129 etna_acc_begin_query(struct etna_context *ctx, struct etna_query *q)
130 {
131 struct etna_acc_query *aq = etna_acc_query(q);
132 const struct etna_acc_sample_provider *p = aq->provider;
133
134 /* ->begin_query() discards previous results, so realloc bo */
135 realloc_query_bo(ctx, aq);
136
137 p->resume(aq, ctx);
138 aq->samples++;
139
140 /* add to active list */
141 assert(list_is_empty(&aq->node));
142 list_addtail(&aq->node, &ctx->active_acc_queries);
143
144 return true;
145 }
146
147 static void
148 etna_acc_end_query(struct etna_context *ctx, struct etna_query *q)
149 {
150 struct etna_acc_query *aq = etna_acc_query(q);
151 const struct etna_acc_sample_provider *p = aq->provider;
152
153 p->suspend(aq, ctx);
154 aq->samples++;
155
156 /* remove from active list */
157 list_delinit(&aq->node);
158 }
159
160 static bool
161 etna_acc_get_query_result(struct etna_context *ctx, struct etna_query *q,
162 bool wait, union pipe_query_result *result)
163 {
164 struct etna_acc_query *aq = etna_acc_query(q);
165 struct etna_resource *rsc = etna_resource(aq->prsc);
166 const struct etna_acc_sample_provider *p = aq->provider;
167
168 assert(list_is_empty(&aq->node));
169
170 if (!wait) {
171 int ret;
172
173 if (rsc->status & ETNA_PENDING_WRITE) {
174 /* piglit spec@arb_occlusion_query@occlusion_query_conform
175 * test, and silly apps perhaps, get stuck in a loop trying
176 * to get query result forever with wait==false.. we don't
177 * wait to flush unnecessarily but we also don't want to
178 * spin forever.
179 */
180 if (aq->no_wait_cnt++ > 5) {
181 ctx->base.flush(&ctx->base, NULL, 0);
182 aq->no_wait_cnt = 0;
183 }
184
185 return false;
186 }
187
188 ret = etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_READ | DRM_ETNA_PREP_NOSYNC);
189 if (ret)
190 return false;
191
192 etna_bo_cpu_fini(rsc->bo);
193 }
194
195 /* flush that GPU executes all query related actions */
196 ctx->base.flush(&ctx->base, NULL, 0);
197
198 /* get the result */
199 etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_READ);
200
201 void *ptr = etna_bo_map(rsc->bo);
202 p->result(aq, ptr, result);
203 aq->samples = 0;
204
205 etna_bo_cpu_fini(rsc->bo);
206
207 return true;
208 }
209
210 static const struct etna_query_funcs acc_query_funcs = {
211 .destroy_query = etna_acc_destroy_query,
212 .begin_query = etna_acc_begin_query,
213 .end_query = etna_acc_end_query,
214 .get_query_result = etna_acc_get_query_result,
215 };
216
217 static inline const struct etna_acc_sample_provider *
218 query_sample_provider(unsigned query_type)
219 {
220 switch (query_type) {
221 case PIPE_QUERY_OCCLUSION_COUNTER:
222 /* fallthrough */
223 case PIPE_QUERY_OCCLUSION_PREDICATE:
224 /* fallthrough */
225 case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
226 return &occlusion_provider;
227 default:
228 return NULL;
229 }
230 }
231
232 struct etna_query *
233 etna_acc_create_query(struct etna_context *ctx, unsigned query_type)
234 {
235 struct etna_acc_query *aq;
236 struct etna_query *q;
237 const struct etna_acc_sample_provider *p;
238
239 p = query_sample_provider(query_type);
240 if (!p)
241 return NULL;
242
243 aq = CALLOC_STRUCT(etna_acc_query);
244 if (!aq)
245 return NULL;
246
247 aq->provider = p;
248
249 list_inithead(&aq->node);
250
251 q = &aq->base;
252 q->funcs = &acc_query_funcs;
253 q->type = query_type;
254
255 return q;
256 }