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 
45 static void
occlusion_start(struct etna_hw_query * hq,struct etna_context * ctx)46 occlusion_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 
64 static void
occlusion_stop(struct etna_hw_query * hq,struct etna_context * ctx)65 occlusion_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 
71 static void
occlusion_suspend(struct etna_hw_query * hq,struct etna_context * ctx)72 occlusion_suspend(struct etna_hw_query *hq, struct etna_context *ctx)
73 {
74    occlusion_stop(hq, ctx);
75 }
76 
77 static void
occlusion_resume(struct etna_hw_query * hq,struct etna_context * ctx)78 occlusion_resume(struct etna_hw_query *hq, struct etna_context *ctx)
79 {
80    hq->samples++;
81    occlusion_start(hq, ctx);
82 }
83 
84 static void
occlusion_result(struct etna_hw_query * hq,void * buf,union pipe_query_result * result)85 occlusion_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 
100 static void
etna_hw_destroy_query(struct etna_context * ctx,struct etna_query * q)101 etna_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 
111 static 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 
119 static void
realloc_query_bo(struct etna_context * ctx,struct etna_hw_query * hq)120 realloc_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 
141 static boolean
etna_hw_begin_query(struct etna_context * ctx,struct etna_query * q)142 etna_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 
159 static void
etna_hw_end_query(struct etna_context * ctx,struct etna_query * q)160 etna_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 
171 static boolean
etna_hw_get_query_result(struct etna_context * ctx,struct etna_query * q,boolean wait,union pipe_query_result * result)172 etna_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 
217 static 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 
224 static inline const struct etna_hw_sample_provider *
query_sample_provider(unsigned query_type)225 query_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 
239 struct etna_query *
etna_hw_create_query(struct etna_context * ctx,unsigned query_type)240 etna_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