summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers/nvfx/nvfx_query.c
blob: 3935ffd7f92938366437e86c34f7724ed00f2da7 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include "pipe/p_context.h"

#include "nvfx_context.h"

struct nvfx_query {
	struct list_head list;
	struct nouveau_resource *object;
	unsigned type;
	boolean ready;
	uint64_t result;
};

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

static struct pipe_query *
nvfx_query_create(struct pipe_context *pipe, unsigned query_type)
{
	struct nvfx_query *q;

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

	assert(q->type == PIPE_QUERY_OCCLUSION_COUNTER);

	return (struct pipe_query *)q;
}

static void
nvfx_query_destroy(struct pipe_context *pipe, struct pipe_query *pq)
{
	struct nvfx_query *q = nvfx_query(pq);

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

static void
nvfx_query_begin(struct pipe_context *pipe, struct pipe_query *pq)
{
	struct nvfx_context *nvfx = nvfx_context(pipe);
	struct nvfx_query *q = nvfx_query(pq);
	struct nvfx_screen *screen = nvfx->screen;
	struct nouveau_channel *chan = screen->base.channel;
	uint64_t tmp;

	assert(!nvfx->query);

	/* 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)
		pipe->get_query_result(pipe, pq, 1, &tmp);

	while (nouveau_resource_alloc(nvfx->screen->query_heap, 1, NULL, &q->object))
	{
		struct nvfx_query* oldestq;
		assert(!LIST_IS_EMPTY(&nvfx->screen->query_list));
		oldestq = LIST_ENTRY(struct nvfx_query, nvfx->screen->query_list.next, list);
		pipe->get_query_result(pipe, (struct pipe_query*)oldestq, 1, &tmp);
	}

	LIST_ADDTAIL(&q->list, &nvfx->screen->query_list);

	nouveau_notifier_reset(nvfx->screen->query, q->object->start);

	WAIT_RING(chan, 4);
	OUT_RING(chan, RING_3D(NV30_3D_QUERY_RESET, 1));
	OUT_RING(chan, 1);
	OUT_RING(chan, RING_3D(NV30_3D_QUERY_ENABLE, 1));
	OUT_RING(chan, 1);

	q->ready = FALSE;

	nvfx->query = pq;
}

static void
nvfx_query_end(struct pipe_context *pipe, struct pipe_query *pq)
{
	struct nvfx_context *nvfx = nvfx_context(pipe);
	struct nouveau_channel *chan = nvfx->screen->base.channel;
	struct nvfx_query *q = nvfx_query(pq);

	assert(nvfx->query == pq);

	WAIT_RING(chan, 4);
	OUT_RING(chan, RING_3D(NV30_3D_QUERY_GET, 1));
	OUT_RING  (chan, (0x01 << NV30_3D_QUERY_GET_UNK24__SHIFT) |
		   ((q->object->start * 32) << NV30_3D_QUERY_GET_OFFSET__SHIFT));
	OUT_RING(chan, RING_3D(NV30_3D_QUERY_ENABLE, 1));
	OUT_RING(chan, 0);
	FIRE_RING(chan);

	nvfx->query = 0;
}

static boolean
nvfx_query_result(struct pipe_context *pipe, struct pipe_query *pq,
		  boolean wait, void *vresult)
{
	uint64_t *result = (uint64_t *)vresult;
	struct nvfx_context *nvfx = nvfx_context(pipe);
	struct nvfx_query *q = nvfx_query(pq);

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

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

			nouveau_notifier_wait_status(nvfx->screen->query,
					q->object->start,
					NV_NOTIFY_STATE_STATUS_COMPLETED, 0);
		}

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

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

void
nvfx_init_query_functions(struct nvfx_context *nvfx)
{
	nvfx->pipe.create_query = nvfx_query_create;
	nvfx->pipe.destroy_query = nvfx_query_destroy;
	nvfx->pipe.begin_query = nvfx_query_begin;
	nvfx->pipe.end_query = nvfx_query_end;
	nvfx->pipe.get_query_result = nvfx_query_result;
}