summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers/nv40/nv40_query.c
blob: 899a8dc0b2d1a609806ac48e79e6048cc08d1cc4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include "pipe/p_context.h"

#include "nv40_context.h"

struct nv40_query {
	struct nouveau_resource *object;
	unsigned type;
	boolean ready;
	uint64_t result;
};

static INLINE struct nv40_query *
nv40_query(struct pipe_query *pipe)
{
	return (struct nv40_query *)pipe;
}

static struct pipe_query *
nv40_query_create(struct pipe_context *pipe, unsigned query_type)
{
	struct nv40_query *q;

	q = CALLOC(1, sizeof(struct nv40_query));
	q->type = query_type;

	return (struct pipe_query *)q;
}

static void
nv40_query_destroy(struct pipe_context *pipe, struct pipe_query *pq)
{
	struct nv40_query *q = nv40_query(pq);

	if (q->object)
		nouveau_resource_free(&q->object);
	FREE(q);
}

static void
nv40_query_begin(struct pipe_context *pipe, struct pipe_query *pq)
{
	struct nv40_context *nv40 = nv40_context(pipe);
	struct nv40_query *q = nv40_query(pq);
	struct nv40_screen *screen = nv40->screen;
	struct nouveau_channel *chan = screen->base.channel;
	struct nouveau_grobj *eng3d = screen->eng3d;

	assert(q->type == PIPE_QUERY_OCCLUSION_COUNTER);

	/* Happens when end_query() is called, then another begin_query()
	 * without querying the result in-between.  For now we'll wait for
	 * the existing query to notify completion, but it could be better.
	 */
	if (q->object) {
		uint64_t tmp;
		pipe->get_query_result(pipe, pq, 1, &tmp);
	}

	if (nouveau_resource_alloc(nv40->screen->query_heap, 1, NULL, &q->object))
		assert(0);
	nouveau_notifier_reset(nv40->screen->query, q->object->start);

	BEGIN_RING(chan, eng3d, NV34TCL_QUERY_RESET, 1);
	OUT_RING  (chan, 1);
	BEGIN_RING(chan, eng3d, NV34TCL_QUERY_UNK17CC, 1);
	OUT_RING  (chan, 1);

	q->ready = FALSE;
}

static void
nv40_query_end(struct pipe_context *pipe, struct pipe_query *pq)
{
	struct nv40_context *nv40 = nv40_context(pipe);
	struct nv40_query *q = nv40_query(pq);
	struct nv40_screen *screen = nv40->screen;
	struct nouveau_channel *chan = screen->base.channel;
	struct nouveau_grobj *eng3d = screen->eng3d;

	BEGIN_RING(chan, eng3d, NV34TCL_QUERY_GET, 1);
	OUT_RING  (chan, (0x01 << NV34TCL_QUERY_GET_UNK24_SHIFT) |
		   ((q->object->start * 32) << NV34TCL_QUERY_GET_OFFSET_SHIFT));
	FIRE_RING(chan);
}

static boolean
nv40_query_result(struct pipe_context *pipe, struct pipe_query *pq,
		  boolean wait, uint64_t *result)
{
	struct nv40_context *nv40 = nv40_context(pipe);
	struct nv40_query *q = nv40_query(pq);

	assert(q->object && q->type == PIPE_QUERY_OCCLUSION_COUNTER);

	if (!q->ready) {
		unsigned status;

		status = nouveau_notifier_status(nv40->screen->query,
						 q->object->start);
		if (status != NV_NOTIFY_STATE_STATUS_COMPLETED) {
			if (wait == FALSE)
				return FALSE;
			nouveau_notifier_wait_status(nv40->screen->query,
					      q->object->start,
					      NV_NOTIFY_STATE_STATUS_COMPLETED,
					      0);
		}

		q->result = nouveau_notifier_return_val(nv40->screen->query,
							q->object->start);
		q->ready = TRUE;
		nouveau_resource_free(&q->object);
	}

	*result = q->result;
	return TRUE;
}

void
nv40_init_query_functions(struct nv40_context *nv40)
{
	nv40->pipe.create_query = nv40_query_create;
	nv40->pipe.destroy_query = nv40_query_destroy;
	nv40->pipe.begin_query = nv40_query_begin;
	nv40->pipe.end_query = nv40_query_end;
	nv40->pipe.get_query_result = nv40_query_result;
}