summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers/nouveau/nouveau_stateobj.h
blob: e844f6abb3dcc120747295a9a704ae49cd06390e (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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#ifndef __NOUVEAU_STATEOBJ_H__
#define __NOUVEAU_STATEOBJ_H__

#include "util/u_debug.h"

#ifdef DEBUG
#define DEBUG_NOUVEAU_STATEOBJ
#endif /* DEBUG */

struct nouveau_stateobj_reloc {
	struct nouveau_bo *bo;

	struct nouveau_grobj *gr;
	uint32_t push_offset;
	uint32_t mthd;

	uint32_t data;
	unsigned flags;
	unsigned vor;
	unsigned tor;
};

struct nouveau_stateobj_start {
	struct nouveau_grobj *gr;
	uint32_t mthd;
	uint32_t size;
	unsigned offset;
};

struct nouveau_stateobj {
	struct pipe_reference reference;

	struct nouveau_stateobj_start *start;
	struct nouveau_stateobj_reloc *reloc;

	/* Common memory pool for data. */
	uint32_t *pool;
	unsigned pool_cur;

#ifdef DEBUG_NOUVEAU_STATEOBJ
	unsigned start_alloc;
	unsigned reloc_alloc;
	unsigned pool_alloc;
#endif  /* DEBUG_NOUVEAU_STATEOBJ */

	unsigned total; /* includes begin_ring */
	unsigned cur; /* excludes begin_ring, offset from "cur_start" */
	unsigned cur_start;
	unsigned cur_reloc;
};

static INLINE void
so_dump(struct nouveau_stateobj *so)
{
	unsigned i, nr, total = 0;

	for (i = 0; i < so->cur_start; i++) {
		if (so->start[i].gr->subc > -1)
			debug_printf("+0x%04x: 0x%08x\n", total++,
				(so->start[i].size << 18) | (so->start[i].gr->subc << 13)
				| so->start[i].mthd);
		else
			debug_printf("+0x%04x: 0x%08x\n", total++,
				(so->start[i].size << 18) | so->start[i].mthd);
		for (nr = 0; nr < so->start[i].size; nr++, total++)
			debug_printf("+0x%04x: 0x%08x\n", total,
				so->pool[so->start[i].offset + nr]);
	}
}

static INLINE struct nouveau_stateobj *
so_new(unsigned start, unsigned push, unsigned reloc)
{
	struct nouveau_stateobj *so;

	so = MALLOC(sizeof(struct nouveau_stateobj));
	pipe_reference_init(&so->reference, 1);
	so->total = so->cur = so->cur_start = so->cur_reloc = 0;

#ifdef DEBUG_NOUVEAU_STATEOBJ
	so->start_alloc = start;
	so->reloc_alloc = reloc;
	so->pool_alloc = push;
#endif /* DEBUG_NOUVEAU_STATEOBJ */

	so->start = MALLOC(start * sizeof(struct nouveau_stateobj_start));
	so->reloc = MALLOC(reloc * sizeof(struct nouveau_stateobj_reloc));
	so->pool = MALLOC(push * sizeof(uint32_t));
	so->pool_cur = 0;

	if (!so->start || !so->reloc || !so->pool) {
		debug_printf("malloc failed\n");
		assert(0);
	}

	return so;
}

static INLINE void
so_ref(struct nouveau_stateobj *ref, struct nouveau_stateobj **pso)
{
	struct nouveau_stateobj *so = *pso;
	int i;

	if (pipe_reference(&(*pso)->reference, &ref->reference)) {
		FREE(so->start);
		for (i = 0; i < so->cur_reloc; i++)
			nouveau_bo_ref(NULL, &so->reloc[i].bo);
		FREE(so->reloc);
		FREE(so->pool);
		FREE(so);
	}
	*pso = ref;
}

static INLINE void
so_data(struct nouveau_stateobj *so, uint32_t data)
{
#ifdef DEBUG_NOUVEAU_STATEOBJ
	if (so->cur >= so->start[so->cur_start - 1].size) {
		debug_printf("exceeding specified size\n");
		assert(0);
	}
#endif /* DEBUG_NOUVEAU_STATEOBJ */

	so->pool[so->start[so->cur_start - 1].offset + so->cur++] = data;
}

static INLINE void
so_datap(struct nouveau_stateobj *so, uint32_t *data, unsigned size)
{
#ifdef DEBUG_NOUVEAU_STATEOBJ
	if ((so->cur + size) > so->start[so->cur_start - 1].size) {
		debug_printf("exceeding specified size\n");
		assert(0);
	}
#endif /* DEBUG_NOUVEAU_STATEOBJ */

	while (size--)
		so->pool[so->start[so->cur_start - 1].offset + so->cur++] =
			*data++;
}

static INLINE void
so_method(struct nouveau_stateobj *so, struct nouveau_grobj *gr,
	  unsigned mthd, unsigned size)
{
	struct nouveau_stateobj_start *start;

#ifdef DEBUG_NOUVEAU_STATEOBJ
	if (so->start_alloc <= so->cur_start) {
		debug_printf("exceeding num_start size\n");
		assert(0);
	} else
#endif /* DEBUG_NOUVEAU_STATEOBJ */
		start = so->start;

#ifdef DEBUG_NOUVEAU_STATEOBJ
	if (so->cur_start > 0 && start[so->cur_start - 1].size > so->cur) {
		debug_printf("previous so_method was not filled\n");
		assert(0);
	}
#endif /* DEBUG_NOUVEAU_STATEOBJ */

	so->start = start;
	start[so->cur_start].gr = gr;
	start[so->cur_start].mthd = mthd;
	start[so->cur_start].size = size;

#ifdef DEBUG_NOUVEAU_STATEOBJ
	if (so->pool_alloc < (size + so->pool_cur)) {
		debug_printf("exceeding num_pool size\n");
		assert(0);
	}
#endif /* DEBUG_NOUVEAU_STATEOBJ */

	start[so->cur_start].offset = so->pool_cur;
	so->pool_cur += size;

	so->cur_start++;
	/* The 1 is for *this* begin_ring. */
	so->total += so->cur + 1;
	so->cur = 0;
}

static INLINE void
so_reloc(struct nouveau_stateobj *so, struct nouveau_bo *bo,
	 unsigned data, unsigned flags, unsigned vor, unsigned tor)
{
	struct nouveau_stateobj_reloc *r;

#ifdef DEBUG_NOUVEAU_STATEOBJ
	if (so->reloc_alloc <= so->cur_reloc) {
		debug_printf("exceeding num_reloc size\n");
		assert(0);
	} else
#endif /* DEBUG_NOUVEAU_STATEOBJ */
		r = so->reloc;

	so->reloc = r;
	r[so->cur_reloc].bo = NULL;
	nouveau_bo_ref(bo, &(r[so->cur_reloc].bo));
	r[so->cur_reloc].gr = so->start[so->cur_start-1].gr;
	r[so->cur_reloc].push_offset = so->total + so->cur;
	r[so->cur_reloc].data = data;
	r[so->cur_reloc].flags = flags;
	r[so->cur_reloc].mthd = so->start[so->cur_start-1].mthd +
							(so->cur << 2);
	r[so->cur_reloc].vor = vor;
	r[so->cur_reloc].tor = tor;

	so_data(so, data);
	so->cur_reloc++;
}

/* Determine if this buffer object is referenced by this state object. */
static INLINE boolean
so_bo_is_reloc(struct nouveau_stateobj *so, struct nouveau_bo *bo)
{
	int i;

	for (i = 0; i < so->cur_reloc; i++)
		if (so->reloc[i].bo == bo)
			return true;

	return false;
}

static INLINE void
so_emit(struct nouveau_channel *chan, struct nouveau_stateobj *so)
{
	struct nouveau_pushbuf *pb = chan->pushbuf;
	unsigned nr, i;
	int ret = 0;

#ifdef DEBUG_NOUVEAU_STATEOBJ
	if (so->start[so->cur_start - 1].size > so->cur) {
		debug_printf("emit: previous so_method was not filled\n");
		assert(0);
	}
#endif /* DEBUG_NOUVEAU_STATEOBJ */

	/* We cannot update total in case we so_emit again. */
	nr = so->total + so->cur;

	/* This will flush if we need space.
	 * We don't actually need the marker.
	 */
	if ((ret = nouveau_pushbuf_marker_emit(chan, nr, so->cur_reloc))) {
		debug_printf("so_emit failed marker emit with error %d\n", ret);
		assert(0);
	}

	/* Submit data. This will ensure proper binding of objects. */
	for (i = 0; i < so->cur_start; i++) {
		BEGIN_RING(chan, so->start[i].gr, so->start[i].mthd, so->start[i].size);
		OUT_RINGp(chan, &(so->pool[so->start[i].offset]), so->start[i].size);
	}

	for (i = 0; i < so->cur_reloc; i++) {
		struct nouveau_stateobj_reloc *r = &so->reloc[i];

		if ((ret = nouveau_pushbuf_emit_reloc(chan, pb->cur - nr +
						r->push_offset, r->bo, r->data,
						0, r->flags, r->vor, r->tor))) {
			debug_printf("so_emit failed reloc with error %d\n", ret);
			assert(0);
		}
	}
}

static INLINE void
so_emit_reloc_markers(struct nouveau_channel *chan, struct nouveau_stateobj *so)
{
	struct nouveau_pushbuf *pb = chan->pushbuf;
	struct nouveau_grobj *gr = NULL;
	unsigned i;
	int ret = 0;

	if (!so)
		return;

	/* If we need to flush in flush notify, then we have a problem anyway. */
	for (i = 0; i < so->cur_reloc; i++) {
		struct nouveau_stateobj_reloc *r = &so->reloc[i];

#ifdef DEBUG_NOUVEAU_STATEOBJ
		if (r->mthd & 0x40000000) {
			debug_printf("error: NI mthd 0x%08X\n", r->mthd);
			continue;
		}
#endif /* DEBUG_NOUVEAU_STATEOBJ */

		/* The object needs to be bound and the system must know the
		 * subchannel is being used. Otherwise it will discard it.
		 */
		if (gr != r->gr) {
			BEGIN_RING(chan, r->gr, 0x100, 1);
			OUT_RING(chan, 0);
			gr = r->gr;
		}

		/* Some relocs really don't like to be hammered,
		 * NOUVEAU_BO_DUMMY makes sure it only
		 * happens when needed.
		 */
		ret = OUT_RELOC(chan, r->bo, (r->gr->subc << 13) | (1<< 18) |
			r->mthd, (r->flags & (NOUVEAU_BO_VRAM | NOUVEAU_BO_GART
				| NOUVEAU_BO_RDWR)) | NOUVEAU_BO_DUMMY, 0, 0);
		if (ret) {
			debug_printf("OUT_RELOC failed %d\n", ret);
			assert(0);
		}

		ret = OUT_RELOC(chan, r->bo, r->data, r->flags |
			NOUVEAU_BO_DUMMY, r->vor, r->tor);
		if (ret) {
			debug_printf("OUT_RELOC failed %d\n", ret);
			assert(0);
		}

		pb->remaining -= 2;
	}
}

#endif