1 /*
2 * Copyright (C) 2017 Rob Clark <robclark@freedesktop.org>
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 FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 * Rob Clark <robclark@freedesktop.org>
25 */
26
27 #include "util/u_inlines.h"
28 #include "util/u_memory.h"
29
30 #include "freedreno_context.h"
31 #include "freedreno_query_acc.h"
32 #include "freedreno_resource.h"
33 #include "freedreno_util.h"
34
35 static void
fd_acc_destroy_query(struct fd_context * ctx,struct fd_query * q)36 fd_acc_destroy_query(struct fd_context *ctx, struct fd_query *q) assert_dt
37 {
38 struct fd_acc_query *aq = fd_acc_query(q);
39
40 DBG("%p", q);
41
42 pipe_resource_reference(&aq->prsc, NULL);
43 list_del(&aq->node);
44
45 free(aq->query_data);
46 free(aq);
47 }
48
49 static void
realloc_query_bo(struct fd_context * ctx,struct fd_acc_query * aq)50 realloc_query_bo(struct fd_context *ctx, struct fd_acc_query *aq)
51 {
52 struct fd_resource *rsc;
53 void *map;
54
55 pipe_resource_reference(&aq->prsc, NULL);
56
57 aq->prsc =
58 pipe_buffer_create(&ctx->screen->base, PIPE_BIND_QUERY_BUFFER, 0, 0x1000);
59
60 /* don't assume the buffer is zero-initialized: */
61 rsc = fd_resource(aq->prsc);
62
63 fd_bo_cpu_prep(rsc->bo, ctx->pipe, FD_BO_PREP_WRITE);
64
65 map = fd_bo_map(rsc->bo);
66 memset(map, 0, aq->size);
67 }
68
69 static void
fd_acc_query_pause(struct fd_acc_query * aq)70 fd_acc_query_pause(struct fd_acc_query *aq) assert_dt
71 {
72 const struct fd_acc_sample_provider *p = aq->provider;
73
74 if (!aq->batch)
75 return;
76
77 fd_batch_needs_flush(aq->batch);
78 p->pause(aq, aq->batch);
79 aq->batch = NULL;
80 }
81
82 static void
fd_acc_query_resume(struct fd_acc_query * aq,struct fd_batch * batch)83 fd_acc_query_resume(struct fd_acc_query *aq, struct fd_batch *batch) assert_dt
84 {
85 const struct fd_acc_sample_provider *p = aq->provider;
86
87 fd_screen_lock(batch->ctx->screen);
88 fd_batch_resource_write(batch, fd_resource(aq->prsc));
89 fd_screen_unlock(batch->ctx->screen);
90
91 aq->batch = batch;
92 fd_batch_needs_flush(aq->batch);
93 p->resume(aq, aq->batch);
94 }
95
96 static void
fd_acc_begin_query(struct fd_context * ctx,struct fd_query * q)97 fd_acc_begin_query(struct fd_context *ctx, struct fd_query *q) assert_dt
98 {
99 struct fd_acc_query *aq = fd_acc_query(q);
100
101 DBG("%p", q);
102
103 /* ->begin_query() discards previous results, so realloc bo: */
104 realloc_query_bo(ctx, aq);
105
106 /* Signal that we need to update the active queries on the next draw */
107 fd_context_dirty(ctx, FD_DIRTY_QUERY);
108
109 /* add to active list: */
110 assert(list_is_empty(&aq->node));
111 list_addtail(&aq->node, &ctx->acc_active_queries);
112
113 /* TIMESTAMP/GPU_FINISHED and don't do normal bracketing at draw time, we
114 * need to just emit the capture at this moment.
115 */
116 if (skip_begin_query(q->type)) {
117 struct fd_batch *batch = fd_context_batch(ctx);
118 fd_acc_query_resume(aq, batch);
119 fd_batch_reference(&batch, NULL);
120 }
121 }
122
123 static void
fd_acc_end_query(struct fd_context * ctx,struct fd_query * q)124 fd_acc_end_query(struct fd_context *ctx, struct fd_query *q) assert_dt
125 {
126 struct fd_acc_query *aq = fd_acc_query(q);
127
128 DBG("%p", q);
129
130 fd_acc_query_pause(aq);
131
132 /* remove from active list: */
133 list_delinit(&aq->node);
134
135 /* mark the result available: */
136 struct fd_batch *batch = fd_context_batch(ctx);
137 struct fd_ringbuffer *ring = batch->draw;
138 struct fd_resource *rsc = fd_resource(aq->prsc);
139
140 if (ctx->screen->gen < 5) {
141 OUT_PKT3(ring, CP_MEM_WRITE, 3);
142 OUT_RELOC(ring, rsc->bo, 0, 0, 0);
143 OUT_RING(ring, 1); /* low 32b */
144 OUT_RING(ring, 0); /* high 32b */
145 } else {
146 OUT_PKT7(ring, CP_MEM_WRITE, 4);
147 OUT_RELOC(ring, rsc->bo, 0, 0, 0);
148 OUT_RING(ring, 1); /* low 32b */
149 OUT_RING(ring, 0); /* high 32b */
150 }
151
152 fd_batch_reference(&batch, NULL);
153 }
154
155 static bool
fd_acc_get_query_result(struct fd_context * ctx,struct fd_query * q,bool wait,union pipe_query_result * result)156 fd_acc_get_query_result(struct fd_context *ctx, struct fd_query *q, bool wait,
157 union pipe_query_result *result)
158 {
159 struct fd_acc_query *aq = fd_acc_query(q);
160 const struct fd_acc_sample_provider *p = aq->provider;
161 struct fd_resource *rsc = fd_resource(aq->prsc);
162
163 DBG("%p: wait=%d", q, wait);
164
165 assert(list_is_empty(&aq->node));
166
167 /* ARB_occlusion_query says:
168 *
169 * "Querying the state for a given occlusion query forces that
170 * occlusion query to complete within a finite amount of time."
171 *
172 * So, regardless of whether we are supposed to wait or not, we do need to
173 * flush now.
174 */
175 if (fd_get_query_result_in_driver_thread(q)) {
176 tc_assert_driver_thread(ctx->tc);
177 fd_context_access_begin(ctx);
178 fd_bc_flush_writer(ctx, rsc);
179 fd_context_access_end(ctx);
180 }
181
182 if (!wait) {
183 int ret = fd_resource_wait(
184 ctx, rsc, FD_BO_PREP_READ | FD_BO_PREP_NOSYNC | FD_BO_PREP_FLUSH);
185 if (ret)
186 return false;
187 } else {
188 fd_resource_wait(ctx, rsc, FD_BO_PREP_READ);
189 }
190
191 struct fd_acc_query_sample *s = fd_bo_map(rsc->bo);
192 p->result(aq, s, result);
193
194 return true;
195 }
196
197 static void
fd_acc_get_query_result_resource(struct fd_context * ctx,struct fd_query * q,enum pipe_query_flags flags,enum pipe_query_value_type result_type,int index,struct fd_resource * dst,unsigned offset)198 fd_acc_get_query_result_resource(struct fd_context *ctx, struct fd_query *q,
199 enum pipe_query_flags flags,
200 enum pipe_query_value_type result_type,
201 int index, struct fd_resource *dst,
202 unsigned offset)
203 assert_dt
204 {
205 struct fd_acc_query *aq = fd_acc_query(q);
206 const struct fd_acc_sample_provider *p = aq->provider;
207 struct fd_batch *batch = fd_context_batch(ctx);
208
209 assert(ctx->screen->gen >= 5);
210
211 fd_screen_lock(batch->ctx->screen);
212 fd_batch_resource_write(batch, dst);
213 fd_screen_unlock(batch->ctx->screen);
214
215 /* query_buffer_object isn't really the greatest thing for a tiler,
216 * if the app tries to use the result of the query in the same batch.
217 * In general the query result isn't truly ready until the last gmem
218 * bin/tile.
219 *
220 * So, we mark the query result as not being available in the draw
221 * ring (which technically is true), and then in epilogue ring we
222 * update the query dst buffer with the *actual* results and status.
223 */
224 if (index == -1) {
225 /* Mark the query as not-ready in the draw ring: */
226 struct fd_ringbuffer *ring = batch->draw;
227 bool is_64b = result_type >= PIPE_QUERY_TYPE_I64;
228
229 OUT_PKT7(ring, CP_MEM_WRITE, is_64b ? 4 : 3);
230 OUT_RELOC(ring, dst->bo, offset, 0, 0);
231 OUT_RING(ring, 0); /* low 32b */
232 if (is_64b)
233 OUT_RING(ring, 0); /* high 32b */
234 }
235
236 struct fd_ringbuffer *ring = fd_batch_get_epilogue(batch);
237
238 if (index == -1) {
239 copy_result(ring, result_type, dst, offset, fd_resource(aq->prsc), 0);
240 } else {
241 p->result_resource(aq, ring, result_type, index, dst, offset);
242 }
243
244 /* If we are told to wait for results, then we need to flush. For an IMR
245 * this would just be a wait on the GPU, but the expectation is that draws
246 * following this one see the results of the query, which means we need to
247 * use the big flush-hammer :-(
248 */
249 if (flags & PIPE_QUERY_WAIT)
250 fd_batch_flush(batch);
251
252 fd_batch_reference(&batch, NULL);
253 }
254
255 static const struct fd_query_funcs acc_query_funcs = {
256 .destroy_query = fd_acc_destroy_query,
257 .begin_query = fd_acc_begin_query,
258 .end_query = fd_acc_end_query,
259 .get_query_result = fd_acc_get_query_result,
260 .get_query_result_resource = fd_acc_get_query_result_resource,
261 };
262
263 struct fd_query *
fd_acc_create_query2(struct fd_context * ctx,unsigned query_type,unsigned index,const struct fd_acc_sample_provider * provider)264 fd_acc_create_query2(struct fd_context *ctx, unsigned query_type,
265 unsigned index,
266 const struct fd_acc_sample_provider *provider)
267 {
268 struct fd_acc_query *aq;
269 struct fd_query *q;
270
271 aq = CALLOC_STRUCT(fd_acc_query);
272 if (!aq)
273 return NULL;
274
275 DBG("%p: query_type=%u", aq, query_type);
276
277 aq->provider = provider;
278 aq->size = provider->size;
279
280 list_inithead(&aq->node);
281
282 q = &aq->base;
283 q->funcs = &acc_query_funcs;
284 q->type = query_type;
285 q->index = index;
286
287 return q;
288 }
289
290 struct fd_query *
fd_acc_create_query(struct fd_context * ctx,unsigned query_type,unsigned index)291 fd_acc_create_query(struct fd_context *ctx, unsigned query_type, unsigned index)
292 {
293 int idx = pidx(query_type);
294
295 if ((idx < 0) || !ctx->acc_sample_providers[idx])
296 return NULL;
297
298 return fd_acc_create_query2(ctx, query_type, index,
299 ctx->acc_sample_providers[idx]);
300 }
301
302 /* Called at clear/draw/blit time to enable/disable the appropriate queries in
303 * the batch (and transfer active querying between batches in the case of
304 * batch reordering).
305 */
306 void
fd_acc_query_update_batch(struct fd_batch * batch,bool disable_all)307 fd_acc_query_update_batch(struct fd_batch *batch, bool disable_all)
308 {
309 struct fd_context *ctx = batch->ctx;
310
311 if (disable_all || (ctx->dirty & FD_DIRTY_QUERY)) {
312 struct fd_acc_query *aq;
313 LIST_FOR_EACH_ENTRY (aq, &ctx->acc_active_queries, node) {
314 bool batch_change = aq->batch != batch;
315 bool was_active = aq->batch != NULL;
316 bool now_active =
317 !disable_all && (ctx->active_queries || aq->provider->always);
318
319 if (was_active && (!now_active || batch_change))
320 fd_acc_query_pause(aq);
321 if (now_active && (!was_active || batch_change))
322 fd_acc_query_resume(aq, batch);
323 }
324 }
325 }
326
327 void
fd_acc_query_register_provider(struct pipe_context * pctx,const struct fd_acc_sample_provider * provider)328 fd_acc_query_register_provider(struct pipe_context *pctx,
329 const struct fd_acc_sample_provider *provider)
330 {
331 struct fd_context *ctx = fd_context(pctx);
332 int idx = pidx(provider->query_type);
333
334 assert((0 <= idx) && (idx < MAX_HW_SAMPLE_PROVIDERS));
335 assert(!ctx->acc_sample_providers[idx]);
336
337 ctx->acc_sample_providers[idx] = provider;
338 }
339