Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 1 | /* |
2 | * Copyright 2011 Nouveau Project |
||
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 shall be included in |
||
12 | * all copies or substantial portions of the Software. |
||
13 | * |
||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
||
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
||
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||
20 | * OTHER DEALINGS IN THE SOFTWARE. |
||
21 | * |
||
22 | * Authors: Christoph Bumiller |
||
23 | */ |
||
24 | |||
25 | #define NV50_PUSH_EXPLICIT_SPACE_CHECKING |
||
26 | |||
27 | #include "nv50_context.h" |
||
28 | #include "nouveau/nv_object.xml.h" |
||
29 | |||
30 | /* XXX: Nested queries, and simultaneous queries on multiple gallium contexts |
||
31 | * (since we use only a single GPU channel per screen) will not work properly. |
||
32 | * |
||
33 | * The first is not that big of an issue because OpenGL does not allow nested |
||
34 | * queries anyway. |
||
35 | */ |
||
36 | |||
37 | struct nv50_query { |
||
38 | uint32_t *data; |
||
39 | uint16_t type; |
||
40 | uint16_t index; |
||
41 | uint32_t sequence; |
||
42 | struct nouveau_bo *bo; |
||
43 | uint32_t base; |
||
44 | uint32_t offset; /* base + i * 16 */ |
||
45 | boolean ready; |
||
46 | boolean flushed; |
||
47 | boolean is64bit; |
||
48 | struct nouveau_mm_allocation *mm; |
||
49 | }; |
||
50 | |||
51 | #define NV50_QUERY_ALLOC_SPACE 128 |
||
52 | |||
53 | static INLINE struct nv50_query * |
||
54 | nv50_query(struct pipe_query *pipe) |
||
55 | { |
||
56 | return (struct nv50_query *)pipe; |
||
57 | } |
||
58 | |||
59 | static boolean |
||
60 | nv50_query_allocate(struct nv50_context *nv50, struct nv50_query *q, int size) |
||
61 | { |
||
62 | struct nv50_screen *screen = nv50->screen; |
||
63 | int ret; |
||
64 | |||
65 | if (q->bo) { |
||
66 | nouveau_bo_ref(NULL, &q->bo); |
||
67 | if (q->mm) { |
||
68 | if (q->ready) |
||
69 | nouveau_mm_free(q->mm); |
||
70 | else |
||
71 | nouveau_fence_work(screen->base.fence.current, nouveau_mm_free_work, |
||
72 | q->mm); |
||
73 | } |
||
74 | } |
||
75 | if (size) { |
||
76 | q->mm = nouveau_mm_allocate(screen->base.mm_GART, size, &q->bo, &q->base); |
||
77 | if (!q->bo) |
||
78 | return FALSE; |
||
79 | q->offset = q->base; |
||
80 | |||
81 | ret = nouveau_bo_map(q->bo, 0, screen->base.client); |
||
82 | if (ret) { |
||
83 | nv50_query_allocate(nv50, q, 0); |
||
84 | return FALSE; |
||
85 | } |
||
86 | q->data = (uint32_t *)((uint8_t *)q->bo->map + q->base); |
||
87 | } |
||
88 | return TRUE; |
||
89 | } |
||
90 | |||
91 | static void |
||
92 | nv50_query_destroy(struct pipe_context *pipe, struct pipe_query *pq) |
||
93 | { |
||
94 | nv50_query_allocate(nv50_context(pipe), nv50_query(pq), 0); |
||
95 | FREE(nv50_query(pq)); |
||
96 | } |
||
97 | |||
98 | static struct pipe_query * |
||
99 | nv50_query_create(struct pipe_context *pipe, unsigned type) |
||
100 | { |
||
101 | struct nv50_context *nv50 = nv50_context(pipe); |
||
102 | struct nv50_query *q; |
||
103 | |||
104 | q = CALLOC_STRUCT(nv50_query); |
||
105 | if (!q) |
||
106 | return NULL; |
||
107 | |||
108 | if (!nv50_query_allocate(nv50, q, NV50_QUERY_ALLOC_SPACE)) { |
||
109 | FREE(q); |
||
110 | return NULL; |
||
111 | } |
||
112 | |||
113 | q->is64bit = (type == PIPE_QUERY_PRIMITIVES_GENERATED || |
||
114 | type == PIPE_QUERY_PRIMITIVES_EMITTED || |
||
115 | type == PIPE_QUERY_SO_STATISTICS); |
||
116 | q->type = type; |
||
117 | |||
118 | if (q->type == PIPE_QUERY_OCCLUSION_COUNTER) { |
||
119 | q->offset -= 16; |
||
120 | q->data -= 16 / sizeof(*q->data); /* we advance before query_begin ! */ |
||
121 | } |
||
122 | |||
123 | return (struct pipe_query *)q; |
||
124 | } |
||
125 | |||
126 | static void |
||
127 | nv50_query_get(struct nouveau_pushbuf *push, struct nv50_query *q, |
||
128 | unsigned offset, uint32_t get) |
||
129 | { |
||
130 | offset += q->offset; |
||
131 | |||
132 | PUSH_SPACE(push, 5); |
||
133 | PUSH_REFN (push, q->bo, NOUVEAU_BO_GART | NOUVEAU_BO_WR); |
||
134 | BEGIN_NV04(push, NV50_3D(QUERY_ADDRESS_HIGH), 4); |
||
135 | PUSH_DATAh(push, q->bo->offset + offset); |
||
136 | PUSH_DATA (push, q->bo->offset + offset); |
||
137 | PUSH_DATA (push, q->sequence); |
||
138 | PUSH_DATA (push, get); |
||
139 | } |
||
140 | |||
141 | static void |
||
142 | nv50_query_begin(struct pipe_context *pipe, struct pipe_query *pq) |
||
143 | { |
||
144 | struct nv50_context *nv50 = nv50_context(pipe); |
||
145 | struct nouveau_pushbuf *push = nv50->base.pushbuf; |
||
146 | struct nv50_query *q = nv50_query(pq); |
||
147 | |||
148 | /* For occlusion queries we have to change the storage, because a previous |
||
149 | * query might set the initial render conition to FALSE even *after* we re- |
||
150 | * initialized it to TRUE. |
||
151 | */ |
||
152 | if (q->type == PIPE_QUERY_OCCLUSION_COUNTER) { |
||
153 | q->offset += 16; |
||
154 | q->data += 16 / sizeof(*q->data); |
||
155 | if (q->offset - q->base == NV50_QUERY_ALLOC_SPACE) |
||
156 | nv50_query_allocate(nv50, q, NV50_QUERY_ALLOC_SPACE); |
||
157 | |||
158 | /* XXX: can we do this with the GPU, and sync with respect to a previous |
||
159 | * query ? |
||
160 | */ |
||
161 | q->data[1] = 1; /* initial render condition = TRUE */ |
||
162 | } |
||
163 | if (!q->is64bit) |
||
164 | q->data[0] = q->sequence++; /* the previously used one */ |
||
165 | |||
166 | switch (q->type) { |
||
167 | case PIPE_QUERY_OCCLUSION_COUNTER: |
||
168 | PUSH_SPACE(push, 4); |
||
169 | BEGIN_NV04(push, NV50_3D(COUNTER_RESET), 1); |
||
170 | PUSH_DATA (push, NV50_3D_COUNTER_RESET_SAMPLECNT); |
||
171 | BEGIN_NV04(push, NV50_3D(SAMPLECNT_ENABLE), 1); |
||
172 | PUSH_DATA (push, 1); |
||
173 | break; |
||
174 | case PIPE_QUERY_PRIMITIVES_GENERATED: |
||
175 | nv50_query_get(push, q, 0x10, 0x06805002); |
||
176 | break; |
||
177 | case PIPE_QUERY_PRIMITIVES_EMITTED: |
||
178 | nv50_query_get(push, q, 0x10, 0x05805002); |
||
179 | break; |
||
180 | case PIPE_QUERY_SO_STATISTICS: |
||
181 | nv50_query_get(push, q, 0x20, 0x05805002); |
||
182 | nv50_query_get(push, q, 0x30, 0x06805002); |
||
183 | break; |
||
184 | case PIPE_QUERY_TIME_ELAPSED: |
||
185 | nv50_query_get(push, q, 0x10, 0x00005002); |
||
186 | break; |
||
187 | default: |
||
188 | break; |
||
189 | } |
||
190 | q->ready = FALSE; |
||
191 | } |
||
192 | |||
193 | static void |
||
194 | nv50_query_end(struct pipe_context *pipe, struct pipe_query *pq) |
||
195 | { |
||
196 | struct nv50_context *nv50 = nv50_context(pipe); |
||
197 | struct nouveau_pushbuf *push = nv50->base.pushbuf; |
||
198 | struct nv50_query *q = nv50_query(pq); |
||
199 | |||
200 | switch (q->type) { |
||
201 | case PIPE_QUERY_OCCLUSION_COUNTER: |
||
202 | nv50_query_get(push, q, 0, 0x0100f002); |
||
203 | PUSH_SPACE(push, 2); |
||
204 | BEGIN_NV04(push, NV50_3D(SAMPLECNT_ENABLE), 1); |
||
205 | PUSH_DATA (push, 0); |
||
206 | break; |
||
207 | case PIPE_QUERY_PRIMITIVES_GENERATED: |
||
208 | nv50_query_get(push, q, 0, 0x06805002); |
||
209 | break; |
||
210 | case PIPE_QUERY_PRIMITIVES_EMITTED: |
||
211 | nv50_query_get(push, q, 0, 0x05805002); |
||
212 | break; |
||
213 | case PIPE_QUERY_SO_STATISTICS: |
||
214 | nv50_query_get(push, q, 0x00, 0x05805002); |
||
215 | nv50_query_get(push, q, 0x10, 0x06805002); |
||
216 | break; |
||
217 | case PIPE_QUERY_TIMESTAMP: |
||
218 | q->sequence++; |
||
219 | /* fall through */ |
||
220 | case PIPE_QUERY_TIME_ELAPSED: |
||
221 | nv50_query_get(push, q, 0, 0x00005002); |
||
222 | break; |
||
223 | case PIPE_QUERY_GPU_FINISHED: |
||
224 | q->sequence++; |
||
225 | nv50_query_get(push, q, 0, 0x1000f010); |
||
226 | break; |
||
227 | case NVA0_QUERY_STREAM_OUTPUT_BUFFER_OFFSET: |
||
228 | nv50_query_get(push, q, 0, 0x0d005002 | (q->index << 5)); |
||
229 | break; |
||
230 | case PIPE_QUERY_TIMESTAMP_DISJOINT: |
||
231 | break; |
||
232 | default: |
||
233 | assert(0); |
||
234 | break; |
||
235 | } |
||
236 | q->ready = q->flushed = FALSE; |
||
237 | } |
||
238 | |||
239 | static INLINE boolean |
||
240 | nv50_query_ready(struct nv50_query *q) |
||
241 | { |
||
242 | return q->ready || (!q->is64bit && (q->data[0] == q->sequence)); |
||
243 | } |
||
244 | |||
245 | static boolean |
||
246 | nv50_query_result(struct pipe_context *pipe, struct pipe_query *pq, |
||
247 | boolean wait, union pipe_query_result *result) |
||
248 | { |
||
249 | struct nv50_context *nv50 = nv50_context(pipe); |
||
250 | struct nv50_query *q = nv50_query(pq); |
||
251 | uint64_t *res64 = (uint64_t *)result; |
||
252 | uint32_t *res32 = (uint32_t *)result; |
||
253 | boolean *res8 = (boolean *)result; |
||
254 | uint64_t *data64 = (uint64_t *)q->data; |
||
255 | |||
256 | if (!q->ready) /* update ? */ |
||
257 | q->ready = nv50_query_ready(q); |
||
258 | if (!q->ready) { |
||
259 | if (!wait) { |
||
260 | /* for broken apps that spin on GL_QUERY_RESULT_AVAILABLE */ |
||
261 | if (!q->flushed) { |
||
262 | q->flushed = TRUE; |
||
263 | PUSH_KICK(nv50->base.pushbuf); |
||
264 | } |
||
265 | return FALSE; |
||
266 | } |
||
267 | if (nouveau_bo_wait(q->bo, NOUVEAU_BO_RD, nv50->screen->base.client)) |
||
268 | return FALSE; |
||
269 | } |
||
270 | q->ready = TRUE; |
||
271 | |||
272 | switch (q->type) { |
||
273 | case PIPE_QUERY_GPU_FINISHED: |
||
274 | res8[0] = TRUE; |
||
275 | break; |
||
276 | case PIPE_QUERY_OCCLUSION_COUNTER: /* u32 sequence, u32 count, u64 time */ |
||
277 | res64[0] = q->data[1]; |
||
278 | break; |
||
279 | case PIPE_QUERY_PRIMITIVES_GENERATED: /* u64 count, u64 time */ |
||
280 | case PIPE_QUERY_PRIMITIVES_EMITTED: /* u64 count, u64 time */ |
||
281 | res64[0] = data64[0] - data64[2]; |
||
282 | break; |
||
283 | case PIPE_QUERY_SO_STATISTICS: |
||
284 | res64[0] = data64[0] - data64[4]; |
||
285 | res64[1] = data64[2] - data64[6]; |
||
286 | break; |
||
287 | case PIPE_QUERY_TIMESTAMP: |
||
288 | res64[0] = data64[1]; |
||
289 | break; |
||
290 | case PIPE_QUERY_TIMESTAMP_DISJOINT: |
||
291 | res64[0] = 1000000000; |
||
292 | res8[8] = FALSE; |
||
293 | break; |
||
294 | case PIPE_QUERY_TIME_ELAPSED: |
||
295 | res64[0] = data64[1] - data64[3]; |
||
296 | break; |
||
297 | case NVA0_QUERY_STREAM_OUTPUT_BUFFER_OFFSET: |
||
298 | res32[0] = q->data[1]; |
||
299 | break; |
||
300 | default: |
||
301 | return FALSE; |
||
302 | } |
||
303 | |||
304 | return TRUE; |
||
305 | } |
||
306 | |||
307 | void |
||
308 | nv84_query_fifo_wait(struct nouveau_pushbuf *push, struct pipe_query *pq) |
||
309 | { |
||
310 | struct nv50_query *q = nv50_query(pq); |
||
311 | unsigned offset = q->offset; |
||
312 | |||
313 | PUSH_SPACE(push, 5); |
||
314 | PUSH_REFN (push, q->bo, NOUVEAU_BO_GART | NOUVEAU_BO_RD); |
||
315 | BEGIN_NV04(push, SUBC_3D(NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH), 4); |
||
316 | PUSH_DATAh(push, q->bo->offset + offset); |
||
317 | PUSH_DATA (push, q->bo->offset + offset); |
||
318 | PUSH_DATA (push, q->sequence); |
||
319 | PUSH_DATA (push, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL); |
||
320 | } |
||
321 | |||
322 | static void |
||
323 | nv50_render_condition(struct pipe_context *pipe, |
||
324 | struct pipe_query *pq, |
||
325 | boolean condition, uint mode) |
||
326 | { |
||
327 | struct nv50_context *nv50 = nv50_context(pipe); |
||
328 | struct nouveau_pushbuf *push = nv50->base.pushbuf; |
||
329 | struct nv50_query *q; |
||
330 | |||
331 | nv50->cond_query = pq; |
||
332 | nv50->cond_cond = condition; |
||
333 | nv50->cond_mode = mode; |
||
334 | |||
335 | PUSH_SPACE(push, 6); |
||
336 | |||
337 | if (!pq) { |
||
338 | BEGIN_NV04(push, NV50_3D(COND_MODE), 1); |
||
339 | PUSH_DATA (push, NV50_3D_COND_MODE_ALWAYS); |
||
340 | return; |
||
341 | } |
||
342 | q = nv50_query(pq); |
||
343 | |||
344 | if (mode == PIPE_RENDER_COND_WAIT || |
||
345 | mode == PIPE_RENDER_COND_BY_REGION_WAIT) { |
||
346 | BEGIN_NV04(push, SUBC_3D(NV50_GRAPH_SERIALIZE), 1); |
||
347 | PUSH_DATA (push, 0); |
||
348 | } |
||
349 | |||
350 | BEGIN_NV04(push, NV50_3D(COND_ADDRESS_HIGH), 3); |
||
351 | PUSH_DATAh(push, q->bo->offset + q->offset); |
||
352 | PUSH_DATA (push, q->bo->offset + q->offset); |
||
353 | PUSH_DATA (push, NV50_3D_COND_MODE_RES_NON_ZERO); |
||
354 | } |
||
355 | |||
356 | void |
||
357 | nv50_query_pushbuf_submit(struct nouveau_pushbuf *push, |
||
358 | struct pipe_query *pq, unsigned result_offset) |
||
359 | { |
||
360 | struct nv50_query *q = nv50_query(pq); |
||
361 | |||
362 | /* XXX: does this exist ? */ |
||
363 | #define NV50_IB_ENTRY_1_NO_PREFETCH (0 << (31 - 8)) |
||
364 | |||
365 | nouveau_pushbuf_space(push, 0, 0, 1); |
||
366 | nouveau_pushbuf_data(push, q->bo, q->offset + result_offset, 4 | |
||
367 | NV50_IB_ENTRY_1_NO_PREFETCH); |
||
368 | } |
||
369 | |||
370 | void |
||
371 | nva0_so_target_save_offset(struct pipe_context *pipe, |
||
372 | struct pipe_stream_output_target *ptarg, |
||
373 | unsigned index, boolean serialize) |
||
374 | { |
||
375 | struct nv50_so_target *targ = nv50_so_target(ptarg); |
||
376 | |||
377 | if (serialize) { |
||
378 | struct nouveau_pushbuf *push = nv50_context(pipe)->base.pushbuf; |
||
379 | PUSH_SPACE(push, 2); |
||
380 | BEGIN_NV04(push, SUBC_3D(NV50_GRAPH_SERIALIZE), 1); |
||
381 | PUSH_DATA (push, 0); |
||
382 | } |
||
383 | |||
384 | nv50_query(targ->pq)->index = index; |
||
385 | nv50_query_end(pipe, targ->pq); |
||
386 | } |
||
387 | |||
388 | void |
||
389 | nv50_init_query_functions(struct nv50_context *nv50) |
||
390 | { |
||
391 | struct pipe_context *pipe = &nv50->base.pipe; |
||
392 | |||
393 | pipe->create_query = nv50_query_create; |
||
394 | pipe->destroy_query = nv50_query_destroy; |
||
395 | pipe->begin_query = nv50_query_begin; |
||
396 | pipe->end_query = nv50_query_end; |
||
397 | pipe->get_query_result = nv50_query_result; |
||
398 | pipe->render_condition = nv50_render_condition; |
||
399 | }><>><> |