decreased the FIFO size
[libgxflux.git] / src / gfx.c
blob481c7ab83e0f2b4decf4327d0f1b7b9310508a0c
1 /*
2 * Gamecube/Wii VIDEO/GX subsystem wrapper
4 * Copyright (C) 2008, 2009 Andre Heider "dhewg" <dhewg@wiibrew.org>
6 * This code is licensed to you under the terms of the GNU GPL, version 2;
7 * see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
9 */
11 #include <malloc.h>
12 #include <string.h>
13 #include <math.h>
15 #include "gfx.h"
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
21 #define FIFO_SIZE (512 * 1024)
22 #define ORIGIN_Z (-500.0)
24 typedef enum {
25 BUSY_READY = 0,
26 BUSY_WAITDRAWDONE,
27 BUSY_WAITVSYNC
28 } _busy_t;
30 typedef struct {
31 gfx_colorop_t op;
32 GXColor c1;
33 GXColor c2;
34 } _colorop_t;
36 static struct {
37 GXRModeObj *vm;
38 bool doublestrike;
40 u32 *fb[2];
41 u32 fb_size;
42 u8 fb_active;
44 bool viewport_enabled;
45 u16 underscan_x;
46 u16 underscan_y;
47 f32 ar;
48 bool pillarboxing;
50 u8 *fifo;
51 Mtx view;
52 _busy_t busy;
54 GXTexObj *tex_loaded;
55 _colorop_t colorop;
56 } _gfx;
58 static struct {
59 guVector pos;
60 guVector up;
61 guVector view;
62 } _camera = {
63 { 0.0, 0.0, 0.0 },
64 { 0.0, 0.5, 0.0 },
65 { 0.0, 0.0, -0.5 }
68 // Standard, DS
69 static GXRModeObj *mode_table[5][2] = {
70 { &TVNtsc480Prog, &TVNtsc240Ds },
71 { &TVNtsc480IntDf, &TVNtsc240Ds },
72 { &TVPal574IntDfScale, &TVPal264Ds },
73 { &TVEurgb60Hz480IntDf, &TVEurgb60Hz240Ds },
74 { &TVMpal480IntDf, &TVMpal240Ds }
77 static void _retrace_cb(u32 count) {
78 (void) count;
80 if (_gfx.busy == BUSY_WAITVSYNC) {
81 VIDEO_SetNextFramebuffer(_gfx.fb[_gfx.fb_active]);
82 VIDEO_Flush();
84 _gfx.fb_active ^= 1;
85 _gfx.busy = BUSY_READY;
89 static void _drawdone_cb(void) {
90 if (_gfx.busy == BUSY_WAITDRAWDONE)
91 _gfx.busy = BUSY_WAITVSYNC;
94 static void _update_viewport(void) {
95 f32 ar;
96 u16 correction;
97 u16 usy;
98 u16 x1, y1, x2, y2;
100 if (_gfx.viewport_enabled) {
101 usy = _gfx.underscan_y;
103 if (!_gfx.doublestrike)
104 usy *= 2;
106 x1 = _gfx.underscan_x * 2;
107 y1 = usy;
108 x2 = _gfx.vm->fbWidth - _gfx.underscan_x * 4;
109 y2 = _gfx.vm->efbHeight - usy * 2;
111 if (_gfx.pillarboxing)
112 ar = 16.0 / 9.0;
113 else
114 ar = 4.0 / 3.0;
116 if (fabs(ar - _gfx.ar) > 0.05) {
117 if (ar > _gfx.ar) {
118 correction = _gfx.vm->viWidth -
119 (u16) round((f32) _gfx.vm->viWidth * _gfx.ar / ar);
121 x1 += correction / 2;
122 x2 -= correction;
123 } else {
124 correction = _gfx.vm->efbHeight -
125 (u16) round((f32) _gfx.vm->efbHeight * ar / _gfx.ar);
127 if (_gfx.doublestrike)
128 correction /= 2;
130 y1 += correction / 2;
131 y2 -= correction;
134 } else {
135 x1 = 0;
136 y1 = 0;
137 x2 = _gfx.vm->fbWidth;
138 y2 = _gfx.vm->efbHeight;
141 GX_SetViewport(x1, y1, x2, y2, 0, 1);
142 GX_SetScissor(x1, y1, x2, y2);
145 gfx_video_standard_t gfx_video_get_standard(void) {
146 gfx_video_standard_t standard;
148 #ifdef HW_RVL
149 if ((CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable()) {
150 standard = GFX_STANDARD_PROGRESSIVE;
151 } else {
152 switch (CONF_GetVideo()) {
153 case CONF_VIDEO_PAL:
154 if (CONF_GetEuRGB60() > 0)
155 standard = GFX_STANDARD_EURGB60;
156 else
157 standard = GFX_STANDARD_PAL;
158 break;
160 case CONF_VIDEO_MPAL:
161 standard = GFX_STANDARD_MPAL;
162 break;
164 default:
165 standard = GFX_STANDARD_NTSC;
166 break;
169 #else
170 switch (VIDEO_GetCurrentTvMode()) {
171 case VI_PAL:
172 standard = GFX_STANDARD_PAL;
173 break;
174 case VI_MPAL:
175 standard = GFX_STANDARD_MPAL;
176 break;
177 default:
178 standard = GFX_STANDARD_NTSC;
179 break;
181 #endif
183 return standard;
186 void gfx_video_init(gfx_video_standard_t standard, gfx_video_mode_t mode) {
187 static bool inited = false;
188 u32 fb_size;
189 u8 i;
191 if (!inited) {
192 memset(&_gfx, 0, sizeof(_gfx));
194 _gfx.viewport_enabled = true;
195 _gfx.ar = 4.0 / 3.0;
197 inited = true;
200 if (standard == GFX_STANDARD_AUTO)
201 standard = gfx_video_get_standard();
203 _gfx.vm = mode_table[standard][mode];
204 _gfx.doublestrike = _gfx.vm->viHeight == 2 * _gfx.vm->xfbHeight;
206 _gfx.vm->viWidth = 672;
207 _gfx.vm->viXOrigin = (VI_MAX_WIDTH_NTSC - _gfx.vm->viWidth) / 2;
209 VIDEO_SetNextFramebuffer(NULL);
211 if (_gfx.vm)
212 VIDEO_WaitVSync();
214 VIDEO_Configure(_gfx.vm);
215 VIDEO_Flush();
217 fb_size = VIDEO_GetFrameBufferSize(_gfx.vm);
219 if (_gfx.fb_size != fb_size) {
220 if (_gfx.fb[0])
221 free(MEM_K1_TO_K0(_gfx.fb[0]));
222 if (_gfx.fb[1])
223 free(MEM_K1_TO_K0(_gfx.fb[1]));
225 _gfx.fb[0] = (u32 *) MEM_K0_TO_K1(SYS_AllocateFramebuffer(_gfx.vm));
226 _gfx.fb[1] = (u32 *) MEM_K0_TO_K1(SYS_AllocateFramebuffer(_gfx.vm));
228 _gfx.fb_size = fb_size;
231 VIDEO_ClearFrameBuffer(_gfx.vm, _gfx.fb[0], COLOR_BLACK);
232 VIDEO_ClearFrameBuffer(_gfx.vm, _gfx.fb[1], COLOR_BLACK);
234 VIDEO_SetNextFramebuffer(_gfx.fb[_gfx.fb_active]);
235 VIDEO_SetBlack(FALSE);
236 VIDEO_Flush();
238 for (i = 0; i < 4; ++i)
239 VIDEO_WaitVSync();
242 void gfx_video_deinit(void) {
243 u8 i;
245 VIDEO_WaitVSync();
246 VIDEO_SetBlack(TRUE);
247 VIDEO_SetNextFramebuffer(NULL);
248 VIDEO_Flush();
250 for (i = 0; i < 4; ++i)
251 VIDEO_WaitVSync();
253 if (_gfx.fb[0]) {
254 free(MEM_K1_TO_K0(_gfx.fb[0]));
255 _gfx.fb[0] = NULL;
258 if (_gfx.fb[1]) {
259 free(MEM_K1_TO_K0(_gfx.fb[1]));
260 _gfx.fb[1] = NULL;
264 u16 gfx_video_get_width(void) {
265 return _gfx.vm->fbWidth;
268 u16 gfx_video_get_height(void) {
269 return _gfx.vm->efbHeight;
272 void gfx_init(void) {
273 Mtx44 p;
274 f32 yscale;
275 u32 xfbHeight;
277 GX_AbortFrame();
279 if (!_gfx.fifo)
280 _gfx.fifo = (u8 *) memalign(32, FIFO_SIZE);
282 memset(_gfx.fifo, 0, FIFO_SIZE);
283 GX_Init(_gfx.fifo, FIFO_SIZE);
285 GX_SetCopyClear(gfx_color_black, 0x00ffffff);
287 yscale = GX_GetYScaleFactor(_gfx.vm->efbHeight, _gfx.vm->xfbHeight);
288 xfbHeight = GX_SetDispCopyYScale(yscale);
289 GX_SetDispCopySrc(0, 0, _gfx.vm->fbWidth, _gfx.vm->efbHeight);
290 GX_SetDispCopyDst(_gfx.vm->fbWidth, xfbHeight);
291 GX_SetCopyFilter(_gfx.vm->aa, _gfx.vm->sample_pattern, GX_TRUE,
292 _gfx.vm->vfilter);
294 if (_gfx.doublestrike)
295 GX_SetFieldMode(_gfx.vm->field_rendering, GX_ENABLE);
296 else
297 GX_SetFieldMode(_gfx.vm->field_rendering, GX_DISABLE);
299 if (_gfx.vm->aa)
300 GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
301 else
302 GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
304 GX_SetCullMode(GX_CULL_NONE);
305 GX_SetDispCopyGamma(GX_GM_1_0);
307 GX_ClearVtxDesc();
308 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
309 GX_SetVtxDesc(GX_VA_CLR0, GX_NONE);
310 GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
312 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
313 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
314 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
316 GX_InvVtxCache();
317 GX_InvalidateTexAll();
319 memset(&_gfx.view, 0, sizeof(Mtx));
320 guLookAt(_gfx.view, &_camera.pos, &_camera.up, &_camera.view);
322 guOrtho(p, 0, _gfx.vm->efbHeight, 0, _gfx.vm->fbWidth, 100, 1000);
323 GX_LoadProjectionMtx (p, GX_ORTHOGRAPHIC);
324 _update_viewport();
326 GX_Flush();
328 _gfx.busy = BUSY_READY;
329 VIDEO_SetPreRetraceCallback(_retrace_cb);
330 GX_SetDrawDoneCallback(_drawdone_cb);
333 void gfx_deinit(void) {
334 VIDEO_SetPreRetraceCallback(NULL);
335 GX_SetDrawDoneCallback(NULL);
336 GX_AbortFrame();
338 free(_gfx.fifo);
339 _gfx.fifo = NULL;
342 bool gfx_enable_viewport(bool enable) {
343 bool old = _gfx.viewport_enabled;
345 _gfx.viewport_enabled = enable;
346 _update_viewport();
348 return old;
351 void gfx_set_underscan(u16 underscan_x, u16 underscan_y) {
352 _gfx.underscan_x = underscan_x;
353 _gfx.underscan_y = underscan_y;
355 if (_gfx.underscan_x > 32)
356 _gfx.underscan_x = 32;
358 if (_gfx.underscan_y > 32)
359 _gfx.underscan_y = 32;
361 _update_viewport();
364 bool gfx_set_pillarboxing(bool enable) {
365 bool old = _gfx.pillarboxing;
366 _gfx.pillarboxing = enable;
368 _update_viewport();
370 return old;
373 f32 gfx_set_ar(f32 ar) {
374 f32 old = _gfx.ar;
376 _gfx.ar = ar;
378 if (ar < 16.0 / 480.0)
379 ar = 16.0 / 480.0;
381 if (ar > 640.0 / 16.0)
382 ar = 640.0 / 16.0;
384 _update_viewport();
386 return old;
389 bool gfx_frame_start(void) {
390 if (_gfx.busy != BUSY_READY)
391 return false;
393 GX_InvVtxCache();
394 GX_InvalidateTexAll();
395 _gfx.tex_loaded = NULL;
397 gfx_set_colorop(COLOROP_NONE, gfx_color_none, gfx_color_none);
399 GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
401 return true;
404 void gfx_frame_end(void) {
405 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
406 GX_SetColorUpdate(GX_TRUE);
408 GX_CopyDisp(_gfx.fb[_gfx.fb_active], GX_TRUE);
410 _gfx.busy = BUSY_WAITDRAWDONE;
411 GX_SetDrawDone();
414 // TEV formula for OP ADD and SUB: (a * (1.0 - c) + b * c) OP d
415 void gfx_set_colorop(gfx_colorop_t op, const GXColor c1, const GXColor c2) {
416 bool skip = false;
418 if (_gfx.colorop.op == op) {
419 switch (op) {
420 case COLOROP_MODULATE_FGBG:
421 if (!gfx_color_cmp(_gfx.colorop.c1, c1))
422 GX_SetTevKColor(GX_KCOLOR0, c1);
423 if (!gfx_color_cmp(_gfx.colorop.c2, c2))
424 GX_SetTevKColor(GX_KCOLOR1, c2);
425 break;
427 default:
428 break;
431 skip = true;
434 _gfx.colorop.op = op;
435 _gfx.colorop.c1 = c1;
436 _gfx.colorop.c2 = c2;
438 if (skip)
439 return;
441 GX_SetNumChans(0);
442 GX_SetNumTexGens(1);
443 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
445 switch (op) {
446 case COLOROP_SIMPLEFADE:
447 GX_SetNumTevStages(1);
448 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORZERO);
450 GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO,
451 GX_CC_ZERO, GX_CC_TEXC);
452 GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO,
453 GX_CS_DIVIDE_2, GX_TRUE, GX_TEVPREV);
454 GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO,
455 GX_CA_ZERO, GX_CA_TEXA);
456 GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO,
457 GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
458 break;
460 case COLOROP_MODULATE_FGBG:
461 GX_SetNumTevStages(2);
462 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORZERO);
464 GX_SetTevKColor(GX_KCOLOR0, c1);
465 GX_SetTevKColor(GX_KCOLOR1, c2);
466 GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0);
467 GX_SetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0_A);
469 GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO,
470 GX_CC_ZERO, GX_CC_KONST);
471 GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO,
472 GX_CS_SCALE_1, GX_TRUE, GX_TEVREG0);
474 GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO,
475 GX_CA_ZERO, GX_CA_KONST);
476 GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO,
477 GX_CS_SCALE_1, GX_TRUE, GX_TEVREG0);
479 GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORZERO);
481 GX_SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K1);
482 GX_SetTevKAlphaSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K1_A);
484 GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_KONST, GX_CC_C0,
485 GX_CC_TEXC, GX_CC_ZERO);
486 GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO,
487 GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
489 GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_KONST, GX_CA_A0,
490 GX_CA_TEXA, GX_CA_ZERO);
491 GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO,
492 GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
494 break;
496 default:
497 GX_SetNumTevStages(1);
498 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORZERO);
500 GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE);
501 break;
505 static inline void _tex_vert(f32 x, f32 y, f32 s, f32 t) {
506 GX_Position2f32(x, y);
507 GX_TexCoord2f32(s, t);
510 static void _quad(f32 x, f32 y, f32 w, f32 h, f32 s1, f32 t1, f32 s2, f32 t2) {
511 Mtx m;
512 Mtx mv;
514 guMtxIdentity(m);
515 guMtxTrans(m, x, y, ORIGIN_Z);
516 guMtxConcat(_gfx.view, m, mv);
517 GX_LoadPosMtxImm(mv, GX_PNMTX0);
519 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
520 _tex_vert(0.0, 0.0, s1, t1);
521 _tex_vert(w, 0.0, s2, t1);
522 _tex_vert(w, h, s2, t2);
523 _tex_vert(0.0, h, s1, t2);
524 GX_End();
527 void gfx_draw_tex(gfx_tex_t *tex, gfx_screen_coords_t *coords) {
528 if (!tex || !coords)
529 return;
531 if (_gfx.tex_loaded != &tex->obj) {
532 GX_LoadTexObj(&tex->obj, GX_TEXMAP0);
533 _gfx.tex_loaded = &tex->obj;
536 _quad(coords->x, coords->y, coords->w, coords->h, 0.0, 0.0, 1.0, 1.0);
539 void gfx_draw_tile(gfx_tiles_t *tiles, gfx_screen_coords_t *coords,
540 u8 col, u8 row) {
541 gfx_draw_tile_by_index(tiles, coords, row * tiles->cols + col);
544 void gfx_draw_tile_by_index(gfx_tiles_t *tiles, gfx_screen_coords_t *coords,
545 u16 index) {
546 gfx_tex_coord_t *tile;
548 if (!tiles)
549 return;
551 if (_gfx.tex_loaded != &tiles->tex->obj) {
552 GX_LoadTexObj(&tiles->tex->obj, GX_TEXMAP0);
553 _gfx.tex_loaded = &tiles->tex->obj;
556 tile = &tiles->tiles[index];
557 _quad(coords->x, coords->y, coords->w, coords->h, tile->s1, tile->t1,
558 tile->s2, tile->t2);
561 #ifdef __cplusplus
563 #endif