1/*
2 * Copyright (C) 2009-2010 Francisco Jerez.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27/*
28 * Vertex submission helper definitions shared among the software and
29 * hardware TnL paths.
30 */
31
32#include "nouveau_gldefs.h"
33
34#include "main/light.h"
35#include "vbo/vbo.h"
36#include "tnl/tnl.h"
37
38#define OUT_INDICES_L(r, i, d, n)		\
39	BATCH_OUT_L(i + d, n);			\
40	(void)r
41#define OUT_INDICES_I16(r, i, d, n)				\
42	BATCH_OUT_I16(r->ib.extract_u(&r->ib, 0, i) + d,	\
43		      r->ib.extract_u(&r->ib, 0, i + 1) + d)
44#define OUT_INDICES_I32(r, i, d, n)			\
45	BATCH_OUT_I32(r->ib.extract_u(&r->ib, 0, i) + d)
46
47/*
48 * Emit <n> vertices using BATCH_OUT_<out>, MAX_OUT_<out> at a time,
49 * grouping them in packets of length MAX_PACKET.
50 *
51 * out:   hardware index data type.
52 * ctx:   GL context.
53 * start: element within the index buffer to begin with.
54 * delta: integer correction that will be added to each index found in
55 *        the index buffer.
56 */
57#define EMIT_VBO(out, ctx, start, delta, n) do {			\
58		struct nouveau_render_state *render = to_render_state(ctx); \
59		int npush = n;						\
60									\
61		while (npush) {						\
62			int npack = MIN2(npush, MAX_PACKET * MAX_OUT_##out); \
63			npush -= npack;					\
64									\
65			BATCH_PACKET_##out((npack + MAX_OUT_##out - 1)	\
66					   / MAX_OUT_##out);		\
67			while (npack) {					\
68				int nout = MIN2(npack, MAX_OUT_##out);	\
69				npack -= nout;				\
70									\
71				OUT_INDICES_##out(render, start, delta, \
72						  nout);		\
73				start += nout;				\
74			}						\
75		}							\
76	} while (0)
77
78/*
79 * Emit the <n>-th element of the array <a>, using IMM_OUT.
80 */
81#define EMIT_IMM(ctx, a, n) do {					\
82		struct nouveau_attr_info *info =			\
83			&TAG(vertex_attrs)[(a)->attr];			\
84		int m;							\
85									\
86		if (!info->emit) {					\
87			IMM_PACKET(info->imm_method, info->imm_fields);	\
88									\
89			for (m = 0; m < (a)->fields; m++)		\
90				IMM_OUT((a)->extract_f(a, n, m));	\
91									\
92			for (m = (a)->fields; m < info->imm_fields; m++) \
93				IMM_OUT(((float []){0, 0, 0, 1})[m]);	\
94									\
95		} else {						\
96			info->emit(ctx, a, (a)->buf + n * (a)->stride);	\
97		}							\
98	} while (0)
99
100static void
101dispatch_l(struct gl_context *ctx, unsigned int start, int delta,
102	   unsigned int n)
103{
104	struct nouveau_pushbuf *push = context_push(ctx);
105	RENDER_LOCALS(ctx);
106
107	EMIT_VBO(L, ctx, start, delta, n);
108}
109
110static void
111dispatch_i32(struct gl_context *ctx, unsigned int start, int delta,
112	     unsigned int n)
113{
114	struct nouveau_pushbuf *push = context_push(ctx);
115	RENDER_LOCALS(ctx);
116
117	EMIT_VBO(I32, ctx, start, delta, n);
118}
119
120static void
121dispatch_i16(struct gl_context *ctx, unsigned int start, int delta,
122	     unsigned int n)
123{
124	struct nouveau_pushbuf *push = context_push(ctx);
125	RENDER_LOCALS(ctx);
126
127	EMIT_VBO(I32, ctx, start, delta, n & 1);
128	EMIT_VBO(I16, ctx, start, delta, n & ~1);
129}
130
131/*
132 * Select an appropriate dispatch function for the given index buffer.
133 */
134static dispatch_t
135get_array_dispatch(struct nouveau_array *a)
136{
137	if (!a->fields)
138		return dispatch_l;
139	else if (a->type == GL_UNSIGNED_INT)
140		return dispatch_i32;
141	else
142		return dispatch_i16;
143}
144
145/*
146 * Returns how many vertices you can draw using <n> pushbuf dwords.
147 */
148static inline unsigned
149get_max_vertices(struct gl_context *ctx, const struct _mesa_index_buffer *ib,
150		 int n)
151{
152	struct nouveau_render_state *render = to_render_state(ctx);
153
154	if (render->mode == IMM) {
155		return MAX2(0, n - 4) / (render->vertex_size / 4 +
156					 render->attr_count);
157	} else {
158		unsigned max_out;
159
160		if (ib) {
161			switch (ib->type) {
162			case GL_UNSIGNED_INT:
163				max_out = MAX_OUT_I32;
164				break;
165
166			case GL_UNSIGNED_SHORT:
167				max_out = MAX_OUT_I16;
168				break;
169
170			case GL_UNSIGNED_BYTE:
171				max_out = MAX_OUT_I16;
172				break;
173
174			default:
175				assert(0);
176				max_out = 0;
177				break;
178			}
179		} else {
180			max_out = MAX_OUT_L;
181		}
182
183		return MAX2(0, n - 7) * max_out * MAX_PACKET / (1 + MAX_PACKET);
184	}
185}
186
187static void
188TAG(emit_material)(struct gl_context *ctx, struct nouveau_array *a,
189		   const void *v)
190{
191	int attr = a->attr - VERT_ATTRIB_GENERIC0;
192	int state = ((int []) {
193			NOUVEAU_STATE_MATERIAL_FRONT_AMBIENT,
194			NOUVEAU_STATE_MATERIAL_BACK_AMBIENT,
195			NOUVEAU_STATE_MATERIAL_FRONT_DIFFUSE,
196			NOUVEAU_STATE_MATERIAL_BACK_DIFFUSE,
197			NOUVEAU_STATE_MATERIAL_FRONT_SPECULAR,
198			NOUVEAU_STATE_MATERIAL_BACK_SPECULAR,
199			NOUVEAU_STATE_MATERIAL_FRONT_AMBIENT,
200			NOUVEAU_STATE_MATERIAL_BACK_AMBIENT,
201			NOUVEAU_STATE_MATERIAL_FRONT_SHININESS,
202			NOUVEAU_STATE_MATERIAL_BACK_SHININESS
203		}) [attr];
204
205	COPY_4V(ctx->Light.Material.Attrib[attr], (float *)v);
206	_mesa_update_material(ctx, 1 << attr);
207
208	context_drv(ctx)->emit[state](ctx, state);
209}
210