nv10_state_frag.c revision 2e47d01c9e5325906cf3bb979279599991c6328e
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#include "nouveau_driver.h"
28#include "nouveau_context.h"
29#include "nouveau_gldefs.h"
30#include "nv10_3d.xml.h"
31#include "nouveau_util.h"
32#include "nv10_driver.h"
33#include "nv20_driver.h"
34
35#define RC_IN_SHIFT_A	24
36#define RC_IN_SHIFT_B	16
37#define RC_IN_SHIFT_C	8
38#define RC_IN_SHIFT_D	0
39#define RC_IN_SHIFT_E	56
40#define RC_IN_SHIFT_F	48
41#define RC_IN_SHIFT_G	40
42
43#define RC_IN_SOURCE(source)				\
44	((uint64_t)NV10_3D_RC_IN_RGB_D_INPUT_##source)
45#define RC_IN_USAGE(usage)					\
46	((uint64_t)NV10_3D_RC_IN_RGB_D_COMPONENT_USAGE_##usage)
47#define RC_IN_MAPPING(mapping)					\
48	((uint64_t)NV10_3D_RC_IN_RGB_D_MAPPING_##mapping)
49
50#define RC_OUT_BIAS	NV10_3D_RC_OUT_RGB_BIAS_BIAS_BY_NEGATIVE_ONE_HALF
51#define RC_OUT_SCALE_1	NV10_3D_RC_OUT_RGB_SCALE_NONE
52#define RC_OUT_SCALE_2	NV10_3D_RC_OUT_RGB_SCALE_SCALE_BY_TWO
53#define RC_OUT_SCALE_4	NV10_3D_RC_OUT_RGB_SCALE_SCALE_BY_FOUR
54
55/* Make the combiner do: spare0_i = A_i * B_i */
56#define RC_OUT_AB	NV10_3D_RC_OUT_RGB_AB_OUTPUT_SPARE0
57/* spare0_i = dot3(A, B) */
58#define RC_OUT_DOT_AB	(NV10_3D_RC_OUT_RGB_AB_OUTPUT_SPARE0 |	\
59			 NV10_3D_RC_OUT_RGB_AB_DOT_PRODUCT)
60/* spare0_i = A_i * B_i + C_i * D_i */
61#define RC_OUT_SUM	NV10_3D_RC_OUT_RGB_SUM_OUTPUT_SPARE0
62
63struct combiner_state {
64	struct gl_context *ctx;
65	int unit;
66	GLboolean premodulate;
67
68	/* GL state */
69	GLenum mode;
70	GLenum *source;
71	GLenum *operand;
72	GLuint logscale;
73
74	/* Derived HW state */
75	uint64_t in;
76	uint32_t out;
77};
78
79/* Initialize a combiner_state struct from the texture unit
80 * context. */
81#define INIT_COMBINER(chan, ctx, rc, i) do {			\
82		struct gl_tex_env_combine_state *c =		\
83			ctx->Texture.Unit[i]._CurrentCombine;	\
84		(rc)->ctx = ctx;				\
85		(rc)->unit = i;					\
86		(rc)->premodulate = c->_NumArgs##chan == 4;	\
87		(rc)->mode = c->Mode##chan;			\
88		(rc)->source = c->Source##chan;			\
89		(rc)->operand = c->Operand##chan;		\
90		(rc)->logscale = c->ScaleShift##chan;		\
91		(rc)->in = (rc)->out = 0;			\
92	} while (0)
93
94/* Get the RC input source for the specified EXT_texture_env_combine
95 * source. */
96static uint32_t
97get_input_source(struct combiner_state *rc, int source)
98{
99	switch (source) {
100	case GL_ZERO:
101		return RC_IN_SOURCE(ZERO);
102
103	case GL_TEXTURE:
104		return RC_IN_SOURCE(TEXTURE0) + rc->unit;
105
106	case GL_TEXTURE0:
107		return RC_IN_SOURCE(TEXTURE0);
108
109	case GL_TEXTURE1:
110		return RC_IN_SOURCE(TEXTURE1);
111
112	case GL_TEXTURE2:
113		return RC_IN_SOURCE(TEXTURE2);
114
115	case GL_TEXTURE3:
116		return RC_IN_SOURCE(TEXTURE3);
117
118	case GL_CONSTANT:
119		return context_chipset(rc->ctx) >= 0x20 ?
120			RC_IN_SOURCE(CONSTANT_COLOR0) :
121			RC_IN_SOURCE(CONSTANT_COLOR0) + rc->unit;
122
123	case GL_PRIMARY_COLOR:
124		return RC_IN_SOURCE(PRIMARY_COLOR);
125
126	case GL_PREVIOUS:
127		return rc->unit ? RC_IN_SOURCE(SPARE0)
128			: RC_IN_SOURCE(PRIMARY_COLOR);
129
130	default:
131		assert(0);
132	}
133}
134
135/* Get the RC input mapping for the specified texture_env_combine
136 * operand, possibly inverted or biased. */
137#define INVERT 0x1
138#define HALF_BIAS 0x2
139
140static uint32_t
141get_input_mapping(struct combiner_state *rc, int operand, int flags)
142{
143	int map = 0;
144
145	if (is_color_operand(operand))
146		map |= RC_IN_USAGE(RGB);
147	else
148		map |= RC_IN_USAGE(ALPHA);
149
150	if (is_negative_operand(operand) == !(flags & INVERT))
151		map |= flags & HALF_BIAS ?
152			RC_IN_MAPPING(HALF_BIAS_NEGATE) :
153			RC_IN_MAPPING(UNSIGNED_INVERT);
154	else
155		map |= flags & HALF_BIAS ?
156			RC_IN_MAPPING(HALF_BIAS_NORMAL) :
157			RC_IN_MAPPING(UNSIGNED_IDENTITY);
158
159	return map;
160}
161
162static uint32_t
163get_input_arg(struct combiner_state *rc, int arg, int flags)
164{
165	int source = rc->source[arg];
166	int operand = rc->operand[arg];
167
168	/* Fake several unsupported texture formats. */
169	if (is_texture_source(source)) {
170		int i = (source == GL_TEXTURE ?
171			 rc->unit : source - GL_TEXTURE0);
172		struct gl_texture_object *t = rc->ctx->Texture.Unit[i]._Current;
173		gl_format format = t->Image[0][t->BaseLevel]->TexFormat;
174
175		if (format == MESA_FORMAT_A8) {
176			/* Emulated using I8. */
177			if (is_color_operand(operand))
178				return RC_IN_SOURCE(ZERO) |
179					get_input_mapping(rc, operand, flags);
180
181		} else if (format == MESA_FORMAT_L8) {
182			/* Sometimes emulated using I8. */
183			if (!is_color_operand(operand))
184				return RC_IN_SOURCE(ZERO) |
185					get_input_mapping(rc, operand,
186							  flags ^ INVERT);
187
188		} else if (format == MESA_FORMAT_XRGB8888) {
189			/* Sometimes emulated using ARGB8888. */
190			if (!is_color_operand(operand))
191				return RC_IN_SOURCE(ZERO) |
192					get_input_mapping(rc, operand,
193							  flags ^ INVERT);
194		}
195	}
196
197	return get_input_source(rc, source) |
198		get_input_mapping(rc, operand, flags);
199}
200
201/* Bind the RC input variable <var> to the EXT_texture_env_combine
202 * argument <arg>, possibly inverted or biased. */
203#define INPUT_ARG(rc, var, arg, flags)					\
204	(rc)->in |= get_input_arg(rc, arg, flags) << RC_IN_SHIFT_##var
205
206/* Bind the RC input variable <var> to the RC source <src>. */
207#define INPUT_SRC(rc, var, src, chan)					\
208	(rc)->in |= (RC_IN_SOURCE(src) |				\
209		     RC_IN_USAGE(chan)) << RC_IN_SHIFT_##var
210
211/* Bind the RC input variable <var> to a constant +/-1 */
212#define INPUT_ONE(rc, var, flags)					\
213	(rc)->in |= (RC_IN_SOURCE(ZERO) |				\
214		     (flags & INVERT ? RC_IN_MAPPING(EXPAND_NORMAL) :	\
215		      RC_IN_MAPPING(UNSIGNED_INVERT))) << RC_IN_SHIFT_##var
216
217static void
218setup_combiner(struct combiner_state *rc)
219{
220	switch (rc->mode) {
221	case GL_REPLACE:
222		INPUT_ARG(rc, A, 0, 0);
223		INPUT_ONE(rc, B, 0);
224
225		rc->out = RC_OUT_AB;
226		break;
227
228	case GL_MODULATE:
229		INPUT_ARG(rc, A, 0, 0);
230		INPUT_ARG(rc, B, 1, 0);
231
232		rc->out = RC_OUT_AB;
233		break;
234
235	case GL_ADD:
236	case GL_ADD_SIGNED:
237		if (rc->premodulate) {
238			INPUT_ARG(rc, A, 0, 0);
239			INPUT_ARG(rc, B, 1, 0);
240			INPUT_ARG(rc, C, 2, 0);
241			INPUT_ARG(rc, D, 3, 0);
242		} else {
243			INPUT_ARG(rc, A, 0, 0);
244			INPUT_ONE(rc, B, 0);
245			INPUT_ARG(rc, C, 1, 0);
246			INPUT_ONE(rc, D, 0);
247		}
248
249		rc->out = RC_OUT_SUM |
250			(rc->mode == GL_ADD_SIGNED ? RC_OUT_BIAS : 0);
251		break;
252
253	case GL_INTERPOLATE:
254		INPUT_ARG(rc, A, 0, 0);
255		INPUT_ARG(rc, B, 2, 0);
256		INPUT_ARG(rc, C, 1, 0);
257		INPUT_ARG(rc, D, 2, INVERT);
258
259		rc->out = RC_OUT_SUM;
260		break;
261
262	case GL_SUBTRACT:
263		INPUT_ARG(rc, A, 0, 0);
264		INPUT_ONE(rc, B, 0);
265		INPUT_ARG(rc, C, 1, 0);
266		INPUT_ONE(rc, D, INVERT);
267
268		rc->out = RC_OUT_SUM;
269		break;
270
271	case GL_DOT3_RGB:
272	case GL_DOT3_RGBA:
273		INPUT_ARG(rc, A, 0, HALF_BIAS);
274		INPUT_ARG(rc, B, 1, HALF_BIAS);
275
276		rc->out = RC_OUT_DOT_AB | RC_OUT_SCALE_4;
277
278		assert(!rc->logscale);
279		break;
280
281	default:
282		assert(0);
283	}
284
285	switch (rc->logscale) {
286	case 0:
287		rc->out |= RC_OUT_SCALE_1;
288		break;
289	case 1:
290		rc->out |= RC_OUT_SCALE_2;
291		break;
292	case 2:
293		rc->out |= RC_OUT_SCALE_4;
294		break;
295	default:
296		assert(0);
297	}
298}
299
300void
301nv10_get_general_combiner(struct gl_context *ctx, int i,
302			  uint32_t *a_in, uint32_t *a_out,
303			  uint32_t *c_in, uint32_t *c_out, uint32_t *k)
304{
305	struct combiner_state rc_a, rc_c;
306
307	if (ctx->Texture.Unit[i]._ReallyEnabled) {
308		INIT_COMBINER(RGB, ctx, &rc_c, i);
309
310		if (rc_c.mode == GL_DOT3_RGBA)
311			rc_a = rc_c;
312		else
313			INIT_COMBINER(A, ctx, &rc_a, i);
314
315		setup_combiner(&rc_c);
316		setup_combiner(&rc_a);
317
318	} else {
319		rc_a.in = rc_a.out = rc_c.in = rc_c.out = 0;
320	}
321
322	*k = pack_rgba_f(MESA_FORMAT_ARGB8888,
323			 ctx->Texture.Unit[i].EnvColor);
324	*a_in = rc_a.in;
325	*a_out = rc_a.out;
326	*c_in = rc_c.in;
327	*c_out = rc_c.out;
328}
329
330void
331nv10_get_final_combiner(struct gl_context *ctx, uint64_t *in, int *n)
332{
333	struct combiner_state rc = {};
334
335	/*
336	 * The final fragment value equation is something like:
337	 *	x_i = A_i * B_i + (1 - A_i) * C_i + D_i
338	 *	x_alpha = G_alpha
339	 * where D_i = E_i * F_i, i one of {red, green, blue}.
340	 */
341	if (ctx->Fog.ColorSumEnabled || ctx->Light.Enabled) {
342		INPUT_SRC(&rc, D, E_TIMES_F, RGB);
343		INPUT_SRC(&rc, F, SECONDARY_COLOR, RGB);
344	}
345
346	if (ctx->Fog.Enabled) {
347		INPUT_SRC(&rc, A, FOG, ALPHA);
348		INPUT_SRC(&rc, C, FOG, RGB);
349		INPUT_SRC(&rc, E, FOG, ALPHA);
350	} else {
351		INPUT_ONE(&rc, A, 0);
352		INPUT_ONE(&rc, C, 0);
353		INPUT_ONE(&rc, E, 0);
354	}
355
356	if (ctx->Texture._EnabledUnits) {
357		INPUT_SRC(&rc, B, SPARE0, RGB);
358		INPUT_SRC(&rc, G, SPARE0, ALPHA);
359	} else {
360		INPUT_SRC(&rc, B, PRIMARY_COLOR, RGB);
361		INPUT_SRC(&rc, G, PRIMARY_COLOR, ALPHA);
362	}
363
364	*in = rc.in;
365	*n = log2i(ctx->Texture._EnabledUnits) + 1;
366}
367
368void
369nv10_emit_tex_env(struct gl_context *ctx, int emit)
370{
371	const int i = emit - NOUVEAU_STATE_TEX_ENV0;
372	struct nouveau_pushbuf *push = context_push(ctx);
373	uint32_t a_in, a_out, c_in, c_out, k;
374
375	nv10_get_general_combiner(ctx, i, &a_in, &a_out, &c_in, &c_out, &k);
376
377	/* Enable the combiners we're going to need. */
378	if (i == 1) {
379		if (c_out || a_out)
380			c_out |= 0x5 << 27;
381		else
382			c_out |= 0x3 << 27;
383	}
384
385	BEGIN_NV04(push, NV10_3D(RC_IN_ALPHA(i)), 1);
386	PUSH_DATA (push, a_in);
387	BEGIN_NV04(push, NV10_3D(RC_IN_RGB(i)), 1);
388	PUSH_DATA (push, c_in);
389	BEGIN_NV04(push, NV10_3D(RC_COLOR(i)), 1);
390	PUSH_DATA (push, k);
391	BEGIN_NV04(push, NV10_3D(RC_OUT_ALPHA(i)), 1);
392	PUSH_DATA (push, a_out);
393	BEGIN_NV04(push, NV10_3D(RC_OUT_RGB(i)), 1);
394	PUSH_DATA (push, c_out);
395
396	context_dirty(ctx, FRAG);
397}
398
399void
400nv10_emit_frag(struct gl_context *ctx, int emit)
401{
402	struct nouveau_pushbuf *push = context_push(ctx);
403	uint64_t in;
404	int n;
405
406	nv10_get_final_combiner(ctx, &in, &n);
407
408	BEGIN_NV04(push, NV10_3D(RC_FINAL0), 2);
409	PUSH_DATA (push, in);
410	PUSH_DATA (push, in >> 32);
411}
412