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