1 /*
2  * Copyright © 2014 Broadcom
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
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 /**
25  * Gallium query object support.
26  *
27  * The HW has native support for occlusion queries, with the query result
28  * being loaded and stored by the TLB unit. From a SW perspective, we have to
29  * be careful to make sure that the jobs that need to be tracking queries are
30  * bracketed by the start and end of counting, even across FBO transitions.
31  *
32  * For the transform feedback PRIMITIVES_GENERATED/WRITTEN queries, we have to
33  * do the calculations in software at draw time.
34  */
35 
36 #include "v3d_context.h"
37 #include "broadcom/cle/v3d_packet_v33_pack.h"
38 
39 struct v3d_query
40 {
41         enum pipe_query_type type;
42         struct v3d_bo *bo;
43 
44         uint32_t start, end;
45 };
46 
47 static struct pipe_query *
v3d_create_query(struct pipe_context * pctx,unsigned query_type,unsigned index)48 v3d_create_query(struct pipe_context *pctx, unsigned query_type, unsigned index)
49 {
50         struct v3d_query *q = calloc(1, sizeof(*q));
51 
52         q->type = query_type;
53 
54         /* Note that struct pipe_query isn't actually defined anywhere. */
55         return (struct pipe_query *)q;
56 }
57 
58 static void
v3d_destroy_query(struct pipe_context * pctx,struct pipe_query * query)59 v3d_destroy_query(struct pipe_context *pctx, struct pipe_query *query)
60 {
61         struct v3d_query *q = (struct v3d_query *)query;
62 
63         v3d_bo_unreference(&q->bo);
64         free(q);
65 }
66 
67 static bool
v3d_begin_query(struct pipe_context * pctx,struct pipe_query * query)68 v3d_begin_query(struct pipe_context *pctx, struct pipe_query *query)
69 {
70         struct v3d_context *v3d = v3d_context(pctx);
71         struct v3d_query *q = (struct v3d_query *)query;
72 
73         switch (q->type) {
74         case PIPE_QUERY_PRIMITIVES_GENERATED:
75                 /* If we are using PRIMITIVE_COUNTS_FEEDBACK to retrieve
76                  * primitive counts from the GPU (which we need when a GS
77                  * is present), then we need to update our counters now
78                  * to discard any primitives generated before this.
79                  */
80                 if (v3d->prog.gs)
81                         v3d_update_primitive_counters(v3d);
82                 q->start = v3d->prims_generated;
83                 break;
84         case PIPE_QUERY_PRIMITIVES_EMITTED:
85                 /* If we are inside transform feedback we need to update the
86                  * primitive counts to skip primtives recorded before this.
87                  */
88                 if (v3d->streamout.num_targets > 0)
89                         v3d_update_primitive_counters(v3d);
90                 q->start = v3d->tf_prims_generated;
91                 break;
92         case PIPE_QUERY_OCCLUSION_COUNTER:
93         case PIPE_QUERY_OCCLUSION_PREDICATE:
94         case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
95                 q->bo = v3d_bo_alloc(v3d->screen, 4096, "query");
96                 uint32_t *map = v3d_bo_map(q->bo);
97                 *map = 0;
98 
99                 v3d->current_oq = q->bo;
100                 v3d->dirty |= VC5_DIRTY_OQ;
101                 break;
102         default:
103                 unreachable("unsupported query type");
104         }
105 
106         return true;
107 }
108 
109 static bool
v3d_end_query(struct pipe_context * pctx,struct pipe_query * query)110 v3d_end_query(struct pipe_context *pctx, struct pipe_query *query)
111 {
112         struct v3d_context *v3d = v3d_context(pctx);
113         struct v3d_query *q = (struct v3d_query *)query;
114 
115         switch (q->type) {
116         case PIPE_QUERY_PRIMITIVES_GENERATED:
117                 /* If we are using PRIMITIVE_COUNTS_FEEDBACK to retrieve
118                  * primitive counts from the GPU (which we need when a GS
119                  * is present), then we need to update our counters now.
120                  */
121                 if (v3d->prog.gs)
122                         v3d_update_primitive_counters(v3d);
123                 q->end = v3d->prims_generated;
124                 break;
125         case PIPE_QUERY_PRIMITIVES_EMITTED:
126                 /* If transform feedback has ended, then we have already
127                  * updated the primitive counts at glEndTransformFeedback()
128                  * time. Otherwise, we have to do it now.
129                  */
130                 if (v3d->streamout.num_targets > 0)
131                         v3d_update_primitive_counters(v3d);
132                 q->end = v3d->tf_prims_generated;
133                 break;
134         case PIPE_QUERY_OCCLUSION_COUNTER:
135         case PIPE_QUERY_OCCLUSION_PREDICATE:
136         case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
137                 v3d->current_oq = NULL;
138                 v3d->dirty |= VC5_DIRTY_OQ;
139                 break;
140         default:
141                 unreachable("unsupported query type");
142         }
143 
144         return true;
145 }
146 
147 static bool
v3d_get_query_result(struct pipe_context * pctx,struct pipe_query * query,bool wait,union pipe_query_result * vresult)148 v3d_get_query_result(struct pipe_context *pctx, struct pipe_query *query,
149                      bool wait, union pipe_query_result *vresult)
150 {
151         struct v3d_context *v3d = v3d_context(pctx);
152         struct v3d_query *q = (struct v3d_query *)query;
153         uint32_t result = 0;
154 
155         if (q->bo) {
156                 v3d_flush_jobs_using_bo(v3d, q->bo);
157 
158                 if (wait) {
159                         if (!v3d_bo_wait(q->bo, ~0ull, "query"))
160                                 return false;
161                 } else {
162                         if (!v3d_bo_wait(q->bo, 0, "query"))
163                                 return false;
164                 }
165 
166                 /* XXX: Sum up per-core values. */
167                 uint32_t *map = v3d_bo_map(q->bo);
168                 result = *map;
169 
170                 v3d_bo_unreference(&q->bo);
171         }
172 
173         switch (q->type) {
174         case PIPE_QUERY_OCCLUSION_COUNTER:
175                 vresult->u64 = result;
176                 break;
177         case PIPE_QUERY_OCCLUSION_PREDICATE:
178         case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
179                 vresult->b = result != 0;
180                 break;
181         case PIPE_QUERY_PRIMITIVES_GENERATED:
182         case PIPE_QUERY_PRIMITIVES_EMITTED:
183                 vresult->u64 = q->end - q->start;
184                 break;
185         default:
186                 unreachable("unsupported query type");
187         }
188 
189         return true;
190 }
191 
192 static void
v3d_set_active_query_state(struct pipe_context * pctx,bool enable)193 v3d_set_active_query_state(struct pipe_context *pctx, bool enable)
194 {
195         struct v3d_context *v3d = v3d_context(pctx);
196 
197         v3d->active_queries = enable;
198         v3d->dirty |= VC5_DIRTY_OQ;
199         v3d->dirty |= VC5_DIRTY_STREAMOUT;
200 }
201 
202 void
v3d_query_init(struct pipe_context * pctx)203 v3d_query_init(struct pipe_context *pctx)
204 {
205         pctx->create_query = v3d_create_query;
206         pctx->destroy_query = v3d_destroy_query;
207         pctx->begin_query = v3d_begin_query;
208         pctx->end_query = v3d_end_query;
209         pctx->get_query_result = v3d_get_query_result;
210         pctx->set_active_query_state = v3d_set_active_query_state;
211 }
212 
213