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 extern const struct etna_acc_sample_provider occlusion_provider;
40 extern const struct etna_acc_sample_provider perfmon_provider;
41 
42 static const struct etna_acc_sample_provider *acc_sample_provider[] =
43 {
44    &occlusion_provider,
45    &perfmon_provider,
46 };
47 
48 static void
etna_acc_destroy_query(struct etna_context * ctx,struct etna_query * q)49 etna_acc_destroy_query(struct etna_context *ctx, struct etna_query *q)
50 {
51    struct etna_acc_query *aq = etna_acc_query(q);
52 
53    pipe_resource_reference(&aq->prsc, NULL);
54    list_del(&aq->node);
55 
56    FREE(aq);
57 }
58 
59 static void
realloc_query_bo(struct etna_context * ctx,struct etna_acc_query * aq)60 realloc_query_bo(struct etna_context *ctx, struct etna_acc_query *aq)
61 {
62    struct etna_resource *rsc;
63    void *map;
64 
65    pipe_resource_reference(&aq->prsc, NULL);
66 
67    /* allocate resource with space for 64 * 64bit values */
68    aq->prsc = pipe_buffer_create(&ctx->screen->base, PIPE_BIND_QUERY_BUFFER,
69                                  0, 0x1000);
70 
71    /* don't assume the buffer is zero-initialized */
72    rsc = etna_resource(aq->prsc);
73 
74    etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_WRITE);
75 
76    map = etna_bo_map(rsc->bo);
77    memset(map, 0, 0x1000);
78    etna_bo_cpu_fini(rsc->bo);
79 }
80 
81 static void
etna_acc_begin_query(struct etna_context * ctx,struct etna_query * q)82 etna_acc_begin_query(struct etna_context *ctx, struct etna_query *q)
83 {
84    struct etna_acc_query *aq = etna_acc_query(q);
85    const struct etna_acc_sample_provider *p = aq->provider;
86 
87    /* ->begin_query() discards previous results, so realloc bo */
88    realloc_query_bo(ctx, aq);
89 
90    p->resume(aq, ctx);
91    aq->samples++;
92 
93    /* add to active list */
94    assert(list_is_empty(&aq->node));
95    list_addtail(&aq->node, &ctx->active_acc_queries);
96 }
97 
98 static void
etna_acc_end_query(struct etna_context * ctx,struct etna_query * q)99 etna_acc_end_query(struct etna_context *ctx, struct etna_query *q)
100 {
101    struct etna_acc_query *aq = etna_acc_query(q);
102    const struct etna_acc_sample_provider *p = aq->provider;
103 
104    p->suspend(aq, ctx);
105    aq->samples++;
106 
107    /* remove from active list */
108    list_delinit(&aq->node);
109 }
110 
111 static bool
etna_acc_get_query_result(struct etna_context * ctx,struct etna_query * q,bool wait,union pipe_query_result * result)112 etna_acc_get_query_result(struct etna_context *ctx, struct etna_query *q,
113                           bool wait, union pipe_query_result *result)
114 {
115    struct etna_acc_query *aq = etna_acc_query(q);
116    struct etna_resource *rsc = etna_resource(aq->prsc);
117    const struct etna_acc_sample_provider *p = aq->provider;
118 
119    assert(list_is_empty(&aq->node));
120 
121    if (rsc->status & ETNA_PENDING_WRITE) {
122       if (!wait) {
123          /* piglit spec@arb_occlusion_query@occlusion_query_conform
124           * test, and silly apps perhaps, get stuck in a loop trying
125           * to get query result forever with wait==false..  we don't
126           * wait to flush unnecessarily but we also don't want to
127           * spin forever.
128           */
129          if (aq->no_wait_cnt++ > 5) {
130             ctx->base.flush(&ctx->base, NULL, 0);
131             aq->no_wait_cnt = 0;
132          }
133 
134          return false;
135       } else {
136          /* flush that GPU executes all query related actions */
137          ctx->base.flush(&ctx->base, NULL, 0);
138       }
139    }
140 
141    /* get the result */
142    int ret = etna_bo_cpu_prep(rsc->bo, DRM_ETNA_PREP_READ);
143    if (ret)
144       return false;
145 
146    void *ptr = etna_bo_map(rsc->bo);
147    bool success = p->result(aq, ptr, result);
148 
149    if (success)
150       aq->samples = 0;
151 
152    etna_bo_cpu_fini(rsc->bo);
153 
154    return success;
155 }
156 
157 static const struct etna_query_funcs acc_query_funcs = {
158    .destroy_query = etna_acc_destroy_query,
159    .begin_query = etna_acc_begin_query,
160    .end_query = etna_acc_end_query,
161    .get_query_result = etna_acc_get_query_result,
162 };
163 
164 struct etna_query *
etna_acc_create_query(struct etna_context * ctx,unsigned query_type)165 etna_acc_create_query(struct etna_context *ctx, unsigned query_type)
166 {
167    const struct etna_acc_sample_provider *p = NULL;
168    struct etna_acc_query *aq;
169    struct etna_query *q;
170 
171    /* find a sample provide for the requested query type */
172    for (unsigned i = 0; i < ARRAY_SIZE(acc_sample_provider); i++) {
173       p = acc_sample_provider[i];
174 
175       if (p->supports(query_type))
176          break;
177       else
178          p = NULL;
179    }
180 
181    if (!p)
182       return NULL;
183 
184    aq = p->allocate(ctx, query_type);
185    if (!aq)
186       return NULL;
187 
188    aq->provider = p;
189 
190    list_inithead(&aq->node);
191 
192    q = &aq->base;
193    q->funcs = &acc_query_funcs;
194    q->type = query_type;
195 
196    return q;
197 }
198