bbb1f0b8b263d354265c7cfd4eaf888a7c16a745
[mw/milkymist.git] / software / demo / eval.c
1 /*
2  * Milkymist VJ SoC (Software)
3  * Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <hw/pfpu.h>
21
22 #include <hal/pfpu.h>
23
24 #include "ast.h"
25 #include "compiler.h"
26 #include "scheduler.h"
27 #include "eval.h"
28
29 //#define EVAL_DEBUG
30
31 /****************************************************************/
32 /* PER-FRAME VARIABLES                                          */
33 /****************************************************************/
34
35 static const char pfv_names[EVAL_PFV_COUNT][IDENTIFIER_SIZE] = {
36         "cx",
37         "cy",
38         "rot",
39         "dx",
40         "dy",
41         "zoom",
42         "decay",
43         "wave_mode",
44         "wave_scale",
45         "wave_additive",
46         "wave_usedots",
47         "bMaximizeWaveColor",
48         "wave_thick",
49         "wave_x",
50         "wave_y",
51         "wave_r",
52         "wave_g",
53         "wave_b",
54         "wave_a",
55
56         "time",
57         "bass",
58         "mid",
59         "treb",
60         "bass_att",
61         "mid_att",
62         "treb_att"
63 };
64
65 static int pfv_from_name(const char *name)
66 {
67         int i;
68         for(i=0;i<EVAL_PFV_COUNT;i++)
69                 if(strcmp(pfv_names[i], name) == 0) return i;
70         if(strcmp(name, "fDecay") == 0) return pfv_decay;
71         if(strcmp(name, "nWaveMode") == 0) return pfv_wave_mode;
72         if(strcmp(name, "fWaveScale") == 0) return pfv_wave_scale;
73         if(strcmp(name, "bAdditiveWaves") == 0) return pfv_wave_additive;
74         if(strcmp(name, "bWaveDots") == 0) return pfv_wave_usedots;
75         if(strcmp(name, "bWaveThick") == 0) return pfv_wave_thick;
76         if(strcmp(name, "fWaveAlpha") == 0) return pfv_wave_a;
77         return -1;
78 }
79
80 static void load_defaults(struct eval_state *sc)
81 {
82         int i;
83
84         for(i=0;i<EVAL_PFV_COUNT;i++)
85                 sc->pfv_initial[i] = 0.0f;
86         sc->pfv_initial[pfv_zoom] = 1.0f;
87         sc->pfv_initial[pfv_decay] = 1.0f;
88         sc->pfv_initial[pfv_wave_mode] = 1.0f;
89         sc->pfv_initial[pfv_wave_scale] = 1.0f;
90         sc->pfv_initial[pfv_wave_r] = 1.0f;
91         sc->pfv_initial[pfv_wave_g] = 1.0f;
92         sc->pfv_initial[pfv_wave_b] = 1.0f;
93         sc->pfv_initial[pfv_wave_a] = 1.0f;
94 }
95
96 static void generate_initial(struct eval_state *sc, struct preset *ast)
97 {
98         struct preset_line *line;
99
100         load_defaults(sc);
101
102         line = ast->lines;
103         while(line != NULL) {
104                 if(!line->iseq) {
105                         int pfv;
106
107                         pfv = pfv_from_name(line->label);
108                         if(pfv >= 0)
109                                 sc->pfv_initial[pfv] = line->contents.parameter;
110                 }
111                 line = line->next;
112         }
113 }
114
115 void eval_reset_pfv(struct eval_state *sc)
116 {
117         int i;
118         for(i=0;i<PFPU_REG_COUNT;i++)
119                 sc->perframe_regs_current[i] = sc->perframe_regs_init[i];
120 }
121
122 static int generate_perframe(struct eval_state *sc, struct preset *ast)
123 {
124         struct compiler_state compiler;
125         struct compiler_initial initials[PFPU_REG_COUNT];
126         int i;
127         struct scheduler_state scheduler;
128         struct preset_line *line;
129
130         compiler_init(&compiler);
131
132         /* Add equations from MilkDrop */
133         line = ast->lines;
134         while(line != NULL) {
135                 if(line->iseq && (strncmp(line->label, "per_frame_", 10) == 0)) {
136                         struct preset_equation *equation;
137
138                         equation = line->contents.equations;
139                         while(equation != NULL) {
140                                 if(!compiler_compile_equation(&compiler, equation->target, equation->topnode)) return 0;
141                                 equation = equation->next;
142                         }
143                 }
144                 line = line->next;
145         }
146
147         /* Generate initial register content */
148         for(i=0;i<PFPU_REG_COUNT;i++)
149                 initials[i].name[0] = 0;
150         for(i=0;i<EVAL_PFV_COUNT;i++) {
151                 strcpy(initials[i].name, pfv_names[i]);
152                 initials[i].x = sc->pfv_initial[i];
153         }
154         compiler_get_initial_regs(&compiler, initials, sc->perframe_regs_init);
155         eval_reset_pfv(sc);
156
157         /* Find out which variables we must read from the PFPU */
158         for(i=0;i<EVAL_PFV_COUNT;i++) {
159                 int j;
160
161                 for(j=0;j<PFPU_REG_COUNT;j++)
162                         if(
163                                 compiler.terminals[j].valid
164                                 && !compiler.terminals[j].isconst
165                                 && (strcmp(compiler.terminals[j].id.name, pfv_names[i]) == 0)
166                         ) break;
167                 if(j < PFPU_REG_COUNT)
168                         sc->pfv_allocation[i] = j;
169                 else
170                         sc->pfv_allocation[i] = -1;
171         }
172
173         #ifdef EVAL_DEBUG
174         printf("======== Per-frame virtual program ========\n");
175         print_vprogram(&compiler);
176         printf("======== Per-frame static register allocation ========\n");
177         print_terminals(&compiler);
178         printf("======== Per-frame variables from PFPU ========\n");
179         for(i=0;i<EVAL_PFV_COUNT;i++)
180                 if(sc->pfv_allocation[i] != -1)
181                         printf("R%03d - %s\n", sc->pfv_allocation[i], pfv_names[i]);
182         #endif
183
184         /* Schedule instructions */
185         scheduler_init(&scheduler);
186         scheduler_dont_touch(&scheduler, compiler.terminals);
187         scheduler_schedule(&scheduler, compiler.prog, compiler.prog_length);
188         /* patch the program to make a dummy DMA at the end (otherwise PFPU never finishes) */
189         scheduler.prog[scheduler.last_exit].i.opcode = PFPU_OPCODE_COPY;
190         scheduler.last_exit += PFPU_LATENCY_COPY;
191         if(scheduler.last_exit >= PFPU_PROGSIZE) return 0;
192         scheduler.prog[scheduler.last_exit].i.dest = PFPU_REG_OUT;
193
194         #ifdef EVAL_DEBUG
195         printf("======== Per-frame HW program ========\n");
196         print_program(&scheduler);
197         #endif
198
199         sc->perframe_prog_length = scheduler.last_exit+1;
200         for(i=0;i<=scheduler.last_exit;i++)
201                 sc->perframe_prog[i].w = scheduler.prog[i].w;
202         for(;i<PFPU_PROGSIZE;i++)
203                 sc->perframe_prog[i].w = 0;
204
205         return 1;
206 }
207
208 static unsigned int dummy;
209
210 void eval_pfv_fill_td(struct eval_state *sc, struct pfpu_td *td, pfpu_callback callback, void *user)
211 {
212         td->output = &dummy;
213         td->hmeshlast = 0;
214         td->vmeshlast = 0;
215         td->program = sc->perframe_prog;
216         td->progsize = sc->perframe_prog_length;
217         td->registers = sc->perframe_regs_current;
218         td->update = 1;
219         td->invalidate = 0; /* < we don't care if our dummy variable has coherency problems */
220         td->callback = callback;
221         td->user = user;
222 }
223
224 float eval_read_pfv(struct eval_state *sc, int pfv)
225 {
226         if(sc->pfv_allocation[pfv] < 0)
227                 return sc->pfv_initial[pfv];
228         else
229                 return sc->perframe_regs_current[sc->pfv_allocation[pfv]];
230 }
231
232 void eval_write_pfv(struct eval_state *sc, int pfv, float x)
233 {
234         if(sc->pfv_allocation[pfv] >= 0)
235                 sc->perframe_regs_current[sc->pfv_allocation[pfv]] = x;
236 }
237
238 /****************************************************************/
239 /* PER-VERTEX VARIABLES                                         */
240 /****************************************************************/
241
242 static int generate_pervertex(struct eval_state *sc, struct preset *ast)
243 {
244         int i;
245         struct scheduler_state scheduler;
246         vpfpu_instruction vprog[PFPU_PROGSIZE];
247         unsigned int vlen;
248         
249         sc->pvv_allocation[pvv_hmeshsize] = 3;
250         sc->pvv_allocation[pvv_vmeshsize] = 4;
251         sc->pvv_allocation[pvv_hres] = 5;
252         sc->pvv_allocation[pvv_vres] = 6;
253         sc->pvv_allocation[pvv_cx] = 7;
254         sc->pvv_allocation[pvv_cy] = 8;
255         sc->pvv_allocation[pvv_rot] = 9;
256         sc->pvv_allocation[pvv_dx] = 10;
257         sc->pvv_allocation[pvv_dy] = 11;
258         sc->pvv_allocation[pvv_zoom] = 12;
259
260         for(i=0;i<PFPU_REG_COUNT;i++)
261                 sc->pervertex_regs[i] = 0.0f;
262         sc->pervertex_regs[3] = 1.0f/(float)sc->hmeshlast;
263         sc->pervertex_regs[4] = 1.0f/(float)sc->vmeshlast;
264         sc->pervertex_regs[5] = (float)sc->hres;
265         sc->pervertex_regs[6] = (float)sc->vres;
266         sc->pervertex_regs[13] = PFPU_TRIG_CONV;
267
268         vlen = 0;
269         
270 #define ADD_ISN(_opcode, _opa, _opb, _dest) do { \
271                 vprog[vlen].opcode = _opcode; \
272                 vprog[vlen].opa = _opa; \
273                 vprog[vlen].opb = _opb; \
274                 vprog[vlen].dest = _dest; \
275                 vlen++; \
276         } while(0)
277 #define BR(x) (x)                       /* bound register */
278 #define FR(x) (PFPU_REG_COUNT+(x))      /* free register */
279
280         /* Compute current coordinates as floating point in range 0..1 */
281         ADD_ISN(PFPU_OPCODE_I2F,        BR(  0), BR(  0), FR(  0)); /* FR00: current X index in float */
282         ADD_ISN(PFPU_OPCODE_I2F,        BR(  1), BR(  0), FR(  1)); /* FR01: current Y index in float */
283         ADD_ISN(PFPU_OPCODE_FMUL,       FR(  0), BR(  3), FR(  2)); /* FR02: current X coord (0..1) */
284         ADD_ISN(PFPU_OPCODE_FMUL,       FR(  1), BR(  4), FR(  3)); /* FR03: current Y coord (0..1) */
285
286         /* Zoom */
287         ADD_ISN(PFPU_OPCODE_FSUB,       FR(  2), BR(  7), FR(  4)); /* FR04: x-cx */
288         ADD_ISN(PFPU_OPCODE_FSUB,       FR(  3), BR(  8), FR(  5)); /* FR05: y-cy */
289         ADD_ISN(PFPU_OPCODE_FMUL,       FR(  4), BR( 12), FR(  6)); /* FR06: zoom*(x-cx) */
290         ADD_ISN(PFPU_OPCODE_FMUL,       FR(  5), BR( 12), FR(  7)); /* FR07: zoom*(y-cy) */
291         ADD_ISN(PFPU_OPCODE_FADD,       FR(  6), BR(  7), FR(  8)); /* FR08: final zoomed X: zoom*(x-cx)+cx */
292         ADD_ISN(PFPU_OPCODE_FADD,       FR(  7), BR(  8), FR(  9)); /* FR09: final zoomed Y: zoom*(y-cy)+cy */
293         
294         /* Rotation */
295         ADD_ISN(PFPU_OPCODE_FMUL,       BR( 13), BR(  9), FR( 80)); /* FR80: rot*conv */
296         ADD_ISN(PFPU_OPCODE_F2I,        FR( 80), BR(  0), FR( 81)); /* FR81: int(rot*conv) */
297         ADD_ISN(PFPU_OPCODE_COS,        FR( 81), BR(  0), FR( 10)); /* FR10: cos(rot*conv) */
298         ADD_ISN(PFPU_OPCODE_SIN,        FR( 81), BR(  0), FR( 11)); /* FR11: sin(rot*conv) */
299         ADD_ISN(PFPU_OPCODE_FSUB,       FR(  8), BR(  7), FR( 12)); /* FR12: u=x-cx */
300         ADD_ISN(PFPU_OPCODE_FSUB,       FR(  9), BR(  8), FR( 13)); /* FR13: v=y-cy */
301         ADD_ISN(PFPU_OPCODE_FMUL,       FR( 12), FR( 10), FR( 14)); /* FR14: u*cos */
302         ADD_ISN(PFPU_OPCODE_FMUL,       FR( 12), FR( 11), FR( 15)); /* FR15: u*sin */
303         ADD_ISN(PFPU_OPCODE_FMUL,       FR( 13), FR( 10), FR( 16)); /* FR16: v*cos */
304         ADD_ISN(PFPU_OPCODE_FMUL,       FR( 13), FR( 11), FR( 17)); /* FR17: v*sin */
305         ADD_ISN(PFPU_OPCODE_FSUB,       FR( 14), FR( 17), FR( 18)); /* FR18: u*cos - v*sin */
306         ADD_ISN(PFPU_OPCODE_FADD,       FR( 15), FR( 16), FR( 19)); /* FR19: u*sin + v*cos */
307         ADD_ISN(PFPU_OPCODE_FADD,       FR( 18), BR(  7), FR( 30)); /* FR30: final rotated X: ...+cx */
308         ADD_ISN(PFPU_OPCODE_FADD,       FR( 19), BR(  8), FR( 31)); /* FR31: final rotated Y: ...+cy */
309         
310         /* Displacement */
311         ADD_ISN(PFPU_OPCODE_FADD,       FR( 30), BR( 10), FR( 20)); /* FR20: X */
312         ADD_ISN(PFPU_OPCODE_FADD,       FR( 31), BR( 11), FR( 21)); /* FR21: Y */
313
314         /* Convert to screen coordinates and generate vertex */
315         ADD_ISN(PFPU_OPCODE_FMUL,       FR( 20), BR(  5), FR( 22)); /* FR22: X screen float */
316         ADD_ISN(PFPU_OPCODE_FMUL,       FR( 21), BR(  6), FR( 23)); /* FR23: Y screen float */
317         ADD_ISN(PFPU_OPCODE_F2I,        FR( 22), BR(  0), FR( 24)); /* FR26: X screen integer */
318         ADD_ISN(PFPU_OPCODE_F2I,        FR( 23), BR(  0), FR( 25)); /* FR27: Y screen integer */
319         ADD_ISN(PFPU_OPCODE_VECT,       FR( 25), FR( 24), BR(127)); /* make vector */
320
321 #undef BR
322 #undef FR
323 #undef ADD_ISN
324
325         /* Schedule */
326         scheduler_init(&scheduler);
327         for(i=0;i<EVAL_PVV_COUNT;i++)
328                 scheduler.dont_touch[sc->pvv_allocation[i]] = 1;
329         scheduler.dont_touch[13] = 1; /* PFPU_TRIG_CONV */
330
331         scheduler_schedule(&scheduler, vprog, vlen);
332
333         #ifdef EVAL_DEBUG
334         printf("======== Per-vertex HW program ========\n");
335         print_program(&scheduler);
336         #endif
337
338         sc->pervertex_prog_length = scheduler.last_exit+1;
339         for(i=0;i<=scheduler.last_exit;i++)
340                 sc->pervertex_prog[i].w = scheduler.prog[i].w;
341         for(;i<PFPU_PROGSIZE;i++)
342                 sc->pervertex_prog[i].w = 0;
343
344         return 1;
345 }
346
347 void eval_pfv_to_pvv(struct eval_state *sc)
348 {
349         sc->pervertex_regs[sc->pvv_allocation[pvv_cx]] = eval_read_pfv(sc, pfv_cx);
350         sc->pervertex_regs[sc->pvv_allocation[pvv_cy]] = eval_read_pfv(sc, pfv_cy);
351         sc->pervertex_regs[sc->pvv_allocation[pvv_rot]] = -eval_read_pfv(sc, pfv_rot);
352         sc->pervertex_regs[sc->pvv_allocation[pvv_dx]] = -eval_read_pfv(sc, pfv_dx);
353         sc->pervertex_regs[sc->pvv_allocation[pvv_dy]] = -eval_read_pfv(sc, pfv_dy);
354         sc->pervertex_regs[sc->pvv_allocation[pvv_zoom]] = 1.0/eval_read_pfv(sc, pfv_zoom);
355 }
356
357 void eval_pvv_fill_td(struct eval_state *sc, struct pfpu_td *td, struct tmu_vertex *vertices, pfpu_callback callback, void *user)
358 {
359         td->output = (unsigned int *)vertices;
360         td->hmeshlast = sc->hmeshlast;
361         td->vmeshlast = sc->vmeshlast;
362         td->program = sc->pervertex_prog;
363         td->progsize = sc->pervertex_prog_length;
364         td->registers = sc->pervertex_regs;
365         td->update = 0; /* < no transfer of data in per-vertex equations between frames */
366         td->invalidate = 1;
367         td->callback = callback;
368         td->user = user;
369 }
370
371 /****************************************************************/
372 /* GENERAL                                                      */
373 /****************************************************************/
374
375 void eval_init(struct eval_state *sc,
376         unsigned int hmeshlast, unsigned int vmeshlast,
377         unsigned int hres, unsigned int vres)
378 {
379         sc->hmeshlast = hmeshlast;
380         sc->vmeshlast = vmeshlast;
381         sc->hres = hres;
382         sc->vres = vres;
383 }
384
385 int eval_load_preset(struct eval_state *sc, struct preset *ast)
386 {
387         generate_initial(sc, ast);
388         if(!generate_perframe(sc, ast)) return 0;
389         if(!generate_pervertex(sc, ast)) return 0;
390         return 1;
391 }