r600_query.c revision bd25e23bf3740f59ce8859848c715daeb9e9821f
1/*
2 * Copyright 2010 Jerome Glisse <glisse@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 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the 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 NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 *      Jerome Glisse
25 *      Corbin Simpson
26 */
27#include <errno.h>
28#include <util/u_inlines.h>
29#include <util/u_format.h>
30#include <util/u_memory.h>
31#include "r600_screen.h"
32#include "r600_context.h"
33
34static struct radeon_state *r600_query_begin(struct r600_context *rctx, struct r600_query *rquery)
35{
36	struct r600_screen *rscreen = rctx->screen;
37	struct radeon_state *rstate;
38
39	rstate = radeon_state(rscreen->rw, R600_QUERY_BEGIN);
40	if (rstate == NULL)
41		return NULL;
42	rstate->states[R600_QUERY__OFFSET] = rquery->num_results;
43	rstate->reloc_pm4_id[0] = R600_QUERY__BO_ID;
44	rstate->bo[0] = radeon_bo_incref(rscreen->rw, rquery->buffer);
45	rstate->nbo = 1;
46	rstate->placement[0] = RADEON_GEM_DOMAIN_GTT;
47	if (radeon_state_pm4(rstate)) {
48		radeon_state_decref(rstate);
49		return NULL;
50	}
51	return rstate;
52}
53
54static struct radeon_state *r600_query_end(struct r600_context *rctx, struct r600_query *rquery)
55{
56	struct r600_screen *rscreen = rctx->screen;
57	struct radeon_state *rstate;
58
59	rstate = radeon_state(rscreen->rw, R600_QUERY_END);
60	if (rstate == NULL)
61		return NULL;
62	rstate->states[R600_QUERY__OFFSET] = rquery->num_results + 8;
63	rstate->reloc_pm4_id[0] = R600_QUERY__BO_ID;
64	rstate->bo[0] = radeon_bo_incref(rscreen->rw, rquery->buffer);
65	rstate->nbo = 1;
66	rstate->placement[0] = RADEON_GEM_DOMAIN_GTT;
67	if (radeon_state_pm4(rstate)) {
68		radeon_state_decref(rstate);
69		return NULL;
70	}
71	return rstate;
72}
73
74static struct pipe_query *r600_create_query(struct pipe_context *ctx, unsigned query_type)
75{
76	struct r600_screen *rscreen = r600_screen(ctx->screen);
77	struct r600_context *rctx = r600_context(ctx);
78	struct r600_query *q;
79
80	if (query_type != PIPE_QUERY_OCCLUSION_COUNTER)
81		return NULL;
82
83	q = CALLOC_STRUCT(r600_query);
84	if (!q)
85		return NULL;
86
87	q->type = query_type;
88	LIST_ADDTAIL(&q->list, &rctx->query_list);
89	q->buffer_size = 4096;
90
91	q->buffer = radeon_bo(rscreen->rw, 0, q->buffer_size, 1, NULL);
92	if (!q->buffer) {
93		FREE(q);
94		return NULL;
95	}
96	return (struct pipe_query *)q;
97}
98
99static void r600_destroy_query(struct pipe_context *ctx,
100			       struct pipe_query *query)
101{
102	struct r600_screen *rscreen = r600_screen(ctx->screen);
103	struct r600_query *q = r600_query(query);
104
105	radeon_bo_decref(rscreen->rw, q->buffer);
106	LIST_DEL(&q->list);
107	FREE(query);
108}
109
110static void r600_query_result(struct pipe_context *ctx, struct r600_query *rquery)
111{
112	struct r600_screen *rscreen = r600_screen(ctx->screen);
113	u64 start, end;
114	u32 *results;
115	int i;
116
117	radeon_bo_wait(rscreen->rw, rquery->buffer);
118	radeon_bo_map(rscreen->rw, rquery->buffer);
119	results = rquery->buffer->data;
120	for (i = 0; i < rquery->num_results; i += 4) {
121		start = (u64)results[i] | (u64)results[i + 1] << 32;
122		end = (u64)results[i + 2] | (u64)results[i + 3] << 32;
123		if ((start & 0x8000000000000000UL) && (end & 0x8000000000000000UL)) {
124			rquery->result += end - start;
125		}
126	}
127	radeon_bo_unmap(rscreen->rw, rquery->buffer);
128	rquery->num_results = 0;
129}
130
131static void r600_query_resume(struct pipe_context *ctx, struct r600_query *rquery)
132{
133	struct r600_context *rctx = r600_context(ctx);
134
135	if (rquery->num_results >= ((rquery->buffer_size >> 2) - 2)) {
136		/* running out of space */
137		if (!rquery->flushed) {
138			ctx->flush(ctx, 0, NULL);
139		}
140		r600_query_result(ctx, rquery);
141	}
142	rquery->rstate = radeon_state_decref(rquery->rstate);
143	rquery->rstate = r600_query_begin(rctx, rquery);
144	rquery->flushed = false;
145}
146
147static void r600_query_suspend(struct pipe_context *ctx, struct r600_query *rquery)
148{
149	struct r600_context *rctx = r600_context(ctx);
150
151	rquery->rstate = radeon_state_decref(rquery->rstate);
152	rquery->rstate = r600_query_end(rctx, rquery);
153	rquery->num_results += 16;
154}
155
156static void r600_begin_query(struct pipe_context *ctx, struct pipe_query *query)
157{
158	struct r600_context *rctx = r600_context(ctx);
159	struct r600_query *rquery = r600_query(query);
160	int r;
161
162	rquery->state = R600_QUERY_STATE_STARTED;
163	rquery->num_results = 0;
164	rquery->flushed = false;
165	r600_query_resume(ctx, rquery);
166	r = radeon_ctx_set_query_state(rctx->ctx, rquery->rstate);
167	if (r == -EBUSY) {
168		/* this shouldn't happen */
169		R600_ERR("had to flush while emitting end query\n");
170		ctx->flush(ctx, 0, NULL);
171		r = radeon_ctx_set_query_state(rctx->ctx, rquery->rstate);
172	}
173}
174
175static void r600_end_query(struct pipe_context *ctx, struct pipe_query *query)
176{
177	struct r600_context *rctx = r600_context(ctx);
178	struct r600_query *rquery = r600_query(query);
179	int r;
180
181	rquery->state &= ~R600_QUERY_STATE_STARTED;
182	rquery->state |= R600_QUERY_STATE_ENDED;
183	r600_query_suspend(ctx, rquery);
184	r = radeon_ctx_set_query_state(rctx->ctx, rquery->rstate);
185	if (r == -EBUSY) {
186		/* this shouldn't happen */
187		R600_ERR("had to flush while emitting end query\n");
188		ctx->flush(ctx, 0, NULL);
189		r = radeon_ctx_set_query_state(rctx->ctx, rquery->rstate);
190	}
191}
192
193void r600_queries_suspend(struct pipe_context *ctx)
194{
195	struct r600_context *rctx = r600_context(ctx);
196	struct r600_query *rquery;
197	int r;
198
199	LIST_FOR_EACH_ENTRY(rquery, &rctx->query_list, list) {
200		if (rquery->state & R600_QUERY_STATE_STARTED) {
201			r600_query_suspend(ctx, rquery);
202			r = radeon_ctx_set_query_state(rctx->ctx, rquery->rstate);
203			if (r == -EBUSY) {
204				/* this shouldn't happen */
205				R600_ERR("had to flush while emitting end query\n");
206				ctx->flush(ctx, 0, NULL);
207				r = radeon_ctx_set_query_state(rctx->ctx, rquery->rstate);
208			}
209		}
210		rquery->state |= R600_QUERY_STATE_SUSPENDED;
211	}
212}
213
214void r600_queries_resume(struct pipe_context *ctx)
215{
216	struct r600_context *rctx = r600_context(ctx);
217	struct r600_query *rquery;
218	int r;
219
220	LIST_FOR_EACH_ENTRY(rquery, &rctx->query_list, list) {
221		if (rquery->state & R600_QUERY_STATE_STARTED) {
222			r600_query_resume(ctx, rquery);
223			r = radeon_ctx_set_query_state(rctx->ctx, rquery->rstate);
224			if (r == -EBUSY) {
225				/* this shouldn't happen */
226				R600_ERR("had to flush while emitting end query\n");
227				ctx->flush(ctx, 0, NULL);
228				r = radeon_ctx_set_query_state(rctx->ctx, rquery->rstate);
229			}
230		}
231		rquery->state &= ~R600_QUERY_STATE_SUSPENDED;
232	}
233}
234
235static boolean r600_get_query_result(struct pipe_context *ctx,
236					struct pipe_query *query,
237					boolean wait, void *vresult)
238{
239	struct r600_query *rquery = r600_query(query);
240	uint64_t *result = (uint64_t*)vresult;
241
242	if (!rquery->flushed) {
243		ctx->flush(ctx, 0, NULL);
244		rquery->flushed = true;
245	}
246	r600_query_result(ctx, rquery);
247	*result = rquery->result;
248	rquery->result = 0;
249	return TRUE;
250}
251
252void r600_init_query_functions(struct r600_context* rctx)
253{
254	LIST_INITHEAD(&rctx->query_list);
255
256	rctx->context.create_query = r600_create_query;
257	rctx->context.destroy_query = r600_destroy_query;
258	rctx->context.begin_query = r600_begin_query;
259	rctx->context.end_query = r600_end_query;
260	rctx->context.get_query_result = r600_get_query_result;
261}
262