2947f5ef7e0441cbd99d156448f3da03146be632
[mesa.git] / src / mesa / drivers / dri / r300 / r300_fragprog_common.c
1 /*
2 * Copyright (C) 2009 Maciej Cencora <m.cencora@gmail.com>
3 *
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 */
27
28 /**
29 * \file
30 *
31 * Fragment program compiler. Perform transformations on the intermediate
32 * representation until the program is in a form where we can translate
33 * it more or less directly into machine-readable form.
34 *
35 * \author Ben Skeggs <darktama@iinet.net.au>
36 * \author Jerome Glisse <j.glisse@gmail.com>
37 */
38
39 #include "r300_fragprog_common.h"
40
41 #include "shader/program.h"
42 #include "shader/prog_parameter.h"
43 #include "shader/prog_print.h"
44
45 #include "compiler/radeon_compiler.h"
46
47 #include "r300_state.h"
48
49
50 static GLuint build_dtm(GLuint depthmode)
51 {
52 switch(depthmode) {
53 default:
54 case GL_LUMINANCE: return 0;
55 case GL_INTENSITY: return 1;
56 case GL_ALPHA: return 2;
57 }
58 }
59
60 static GLuint build_func(GLuint comparefunc)
61 {
62 return comparefunc - GL_NEVER;
63 }
64
65 /**
66 * Collect all external state that is relevant for compiling the given
67 * fragment program.
68 */
69 static void build_state(
70 r300ContextPtr r300,
71 struct gl_fragment_program *fp,
72 struct r300_fragment_program_external_state *state)
73 {
74 int unit;
75
76 _mesa_bzero(state, sizeof(*state));
77
78 for(unit = 0; unit < 16; ++unit) {
79 if (fp->Base.ShadowSamplers & (1 << unit)) {
80 struct gl_texture_object* tex = r300->radeon.glCtx->Texture.Unit[unit]._Current;
81
82 state->unit[unit].depth_texture_mode = build_dtm(tex->DepthMode);
83 state->unit[unit].texture_compare_func = build_func(tex->CompareFunc);
84 }
85 }
86 }
87
88
89 /**
90 * Transform the program to support fragment.position.
91 *
92 * Introduce a small fragment at the start of the program that will be
93 * the only code that directly reads the FRAG_ATTRIB_WPOS input.
94 * All other code pieces that reference that input will be rewritten
95 * to read from a newly allocated temporary.
96 *
97 */
98 static void insert_WPOS_trailer(struct r300_fragment_program_compiler *compiler)
99 {
100 int i;
101
102 if (!(compiler->Base.Program.InputsRead & FRAG_BIT_WPOS)) {
103 compiler->code->wpos_attr = FRAG_ATTRIB_MAX;
104 return;
105 }
106
107 for (i = FRAG_ATTRIB_TEX0; i <= FRAG_ATTRIB_TEX7; ++i)
108 {
109 if (!(compiler->Base.Program.InputsRead & (1 << i))) {
110 compiler->code->wpos_attr = i;
111 break;
112 }
113 }
114
115 rc_transform_fragment_wpos(&compiler->Base, FRAG_ATTRIB_WPOS, compiler->code->wpos_attr);
116 }
117
118 /**
119 * Rewrite fragment.fogcoord to use a texture coordinate slot.
120 * Note that fogcoord is forced into an X001 pattern, and this enforcement
121 * is done here.
122 *
123 * See also the counterpart rewriting for vertex programs.
124 */
125 static void rewriteFog(struct r300_fragment_program_compiler *compiler)
126 {
127 struct rX00_fragment_program_code *code = compiler->code;
128 struct prog_src_register src;
129 int i;
130
131 if (!(compiler->Base.Program.InputsRead & FRAG_BIT_FOGC)) {
132 code->fog_attr = FRAG_ATTRIB_MAX;
133 return;
134 }
135
136 for (i = FRAG_ATTRIB_TEX0; i <= FRAG_ATTRIB_TEX7; ++i)
137 {
138 if (!(compiler->Base.Program.InputsRead & (1 << i))) {
139 code->fog_attr = i;
140 break;
141 }
142 }
143
144 memset(&src, 0, sizeof(src));
145 src.File = PROGRAM_INPUT;
146 src.Index = code->fog_attr;
147 src.Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_ZERO, SWIZZLE_ZERO, SWIZZLE_ONE);
148 rc_move_input(&compiler->Base, FRAG_ATTRIB_FOGC, src);
149 }
150
151
152 /**
153 * Reserve hardware temporary registers for the program inputs.
154 *
155 * @note This allocation is performed explicitly, because the order of inputs
156 * is determined by the RS hardware.
157 */
158 static void allocate_hw_inputs(void * yourdata, void (*allocate)(void * data, unsigned input, unsigned hwreg), void * mydata)
159 {
160 struct r300_fragment_program_compiler * c = yourdata;
161 GLuint InputsRead = c->Base.Program.InputsRead;
162 int i;
163 GLuint hwindex = 0;
164
165 /* Primary colour */
166 if (InputsRead & FRAG_BIT_COL0)
167 allocate(mydata, FRAG_ATTRIB_COL0, hwindex++);
168 InputsRead &= ~FRAG_BIT_COL0;
169
170 /* Secondary color */
171 if (InputsRead & FRAG_BIT_COL1)
172 allocate(mydata, FRAG_ATTRIB_COL1, hwindex++);
173 InputsRead &= ~FRAG_BIT_COL1;
174
175 /* Texcoords */
176 for (i = 0; i < 8; i++) {
177 if (InputsRead & (FRAG_BIT_TEX0 << i))
178 allocate(mydata, FRAG_ATTRIB_TEX0+i, hwindex++);
179 }
180 InputsRead &= ~FRAG_BITS_TEX_ANY;
181
182 /* Fogcoords treated as a texcoord */
183 if (InputsRead & FRAG_BIT_FOGC)
184 allocate(mydata, FRAG_ATTRIB_FOGC, hwindex++);
185 InputsRead &= ~FRAG_BIT_FOGC;
186
187 /* fragment position treated as a texcoord */
188 if (InputsRead & FRAG_BIT_WPOS)
189 allocate(mydata, FRAG_ATTRIB_WPOS, hwindex++);
190 InputsRead &= ~FRAG_BIT_WPOS;
191
192 /* Anything else */
193 if (InputsRead)
194 rc_error(&c->Base, "Don't know how to handle inputs 0x%x\n", InputsRead);
195 }
196
197
198 static void translate_fragment_program(GLcontext *ctx, struct r300_fragment_program_cont *cont, struct r300_fragment_program *fp)
199 {
200 r300ContextPtr r300 = R300_CONTEXT(ctx);
201 struct r300_fragment_program_compiler compiler;
202
203 rc_init(&compiler.Base);
204 compiler.Base.Debug = (RADEON_DEBUG & DEBUG_PIXEL) ? GL_TRUE : GL_FALSE;
205
206 compiler.code = &fp->code;
207 compiler.state = fp->state;
208 compiler.is_r500 = (r300->radeon.radeonScreen->chip_family >= CHIP_FAMILY_RV515) ? GL_TRUE : GL_FALSE;
209 compiler.OutputDepth = FRAG_RESULT_DEPTH;
210 compiler.OutputColor = FRAG_RESULT_COLOR;
211 compiler.AllocateHwInputs = &allocate_hw_inputs;
212 compiler.UserData = &compiler;
213
214 if (compiler.Base.Debug) {
215 fflush(stdout);
216 _mesa_printf("Fragment Program: Initial program:\n");
217 _mesa_print_program(&cont->Base.Base);
218 fflush(stdout);
219 }
220
221 rc_mesa_to_rc_program(&compiler.Base, &cont->Base.Base);
222
223 insert_WPOS_trailer(&compiler);
224
225 rewriteFog(&compiler);
226
227 r3xx_compile_fragment_program(&compiler);
228 fp->error = compiler.Base.Error;
229
230 fp->InputsRead = compiler.Base.Program.InputsRead;
231
232 rc_destroy(&compiler.Base);
233 }
234
235 struct r300_fragment_program *r300SelectAndTranslateFragmentShader(GLcontext *ctx)
236 {
237 r300ContextPtr r300 = R300_CONTEXT(ctx);
238 struct r300_fragment_program_cont *fp_list;
239 struct r300_fragment_program *fp;
240 struct r300_fragment_program_external_state state;
241
242 fp_list = (struct r300_fragment_program_cont *)ctx->FragmentProgram._Current;
243 build_state(r300, ctx->FragmentProgram._Current, &state);
244
245 fp = fp_list->progs;
246 while (fp) {
247 if (_mesa_memcmp(&fp->state, &state, sizeof(state)) == 0) {
248 return r300->selected_fp = fp;
249 }
250 fp = fp->next;
251 }
252
253 fp = _mesa_calloc(sizeof(struct r300_fragment_program));
254
255 fp->state = state;
256
257 fp->next = fp_list->progs;
258 fp_list->progs = fp;
259
260 translate_fragment_program(ctx, fp_list, fp);
261
262 return r300->selected_fp = fp;
263 }