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
21 #define FIFO_SIZE (512 * 1024)
22 #define ORIGIN_Z (-500.0)
44 bool viewport_enabled
;
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
) {
80 if (_gfx
.busy
== BUSY_WAITVSYNC
) {
81 VIDEO_SetNextFramebuffer(_gfx
.fb
[_gfx
.fb_active
]);
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) {
100 if (_gfx
.viewport_enabled
) {
101 usy
= _gfx
.underscan_y
;
103 if (!_gfx
.doublestrike
)
106 x1
= _gfx
.underscan_x
* 2;
108 x2
= _gfx
.vm
->fbWidth
- _gfx
.underscan_x
* 4;
109 y2
= _gfx
.vm
->efbHeight
- usy
* 2;
111 if (_gfx
.pillarboxing
)
116 if (fabs(ar
- _gfx
.ar
) > 0.05) {
118 correction
= _gfx
.vm
->viWidth
-
119 (u16
) round((f32
) _gfx
.vm
->viWidth
* _gfx
.ar
/ ar
);
121 x1
+= correction
/ 2;
124 correction
= _gfx
.vm
->efbHeight
-
125 (u16
) round((f32
) _gfx
.vm
->efbHeight
* ar
/ _gfx
.ar
);
127 if (_gfx
.doublestrike
)
130 y1
+= correction
/ 2;
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
;
149 if ((CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable()) {
150 standard
= GFX_STANDARD_PROGRESSIVE
;
152 switch (CONF_GetVideo()) {
154 if (CONF_GetEuRGB60() > 0)
155 standard
= GFX_STANDARD_EURGB60
;
157 standard
= GFX_STANDARD_PAL
;
160 case CONF_VIDEO_MPAL
:
161 standard
= GFX_STANDARD_MPAL
;
165 standard
= GFX_STANDARD_NTSC
;
170 switch (VIDEO_GetCurrentTvMode()) {
172 standard
= GFX_STANDARD_PAL
;
175 standard
= GFX_STANDARD_MPAL
;
178 standard
= GFX_STANDARD_NTSC
;
186 void gfx_video_init(gfx_video_standard_t standard
, gfx_video_mode_t mode
) {
187 static bool inited
= false;
192 memset(&_gfx
, 0, sizeof(_gfx
));
194 _gfx
.viewport_enabled
= 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
);
214 VIDEO_Configure(_gfx
.vm
);
217 fb_size
= VIDEO_GetFrameBufferSize(_gfx
.vm
);
219 if (_gfx
.fb_size
!= fb_size
) {
221 free(MEM_K1_TO_K0(_gfx
.fb
[0]));
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
);
238 for (i
= 0; i
< 4; ++i
)
242 void gfx_video_deinit(void) {
246 VIDEO_SetBlack(TRUE
);
247 VIDEO_SetNextFramebuffer(NULL
);
250 for (i
= 0; i
< 4; ++i
)
254 free(MEM_K1_TO_K0(_gfx
.fb
[0]));
259 free(MEM_K1_TO_K0(_gfx
.fb
[1]));
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) {
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
,
294 if (_gfx
.doublestrike
)
295 GX_SetFieldMode(_gfx
.vm
->field_rendering
, GX_ENABLE
);
297 GX_SetFieldMode(_gfx
.vm
->field_rendering
, GX_DISABLE
);
300 GX_SetPixelFmt(GX_PF_RGB565_Z16
, GX_ZC_LINEAR
);
302 GX_SetPixelFmt(GX_PF_RGB8_Z24
, GX_ZC_LINEAR
);
304 GX_SetCullMode(GX_CULL_NONE
);
305 GX_SetDispCopyGamma(GX_GM_1_0
);
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);
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
);
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
);
342 bool gfx_enable_viewport(bool enable
) {
343 bool old
= _gfx
.viewport_enabled
;
345 _gfx
.viewport_enabled
= enable
;
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;
364 bool gfx_set_pillarboxing(bool enable
) {
365 bool old
= _gfx
.pillarboxing
;
366 _gfx
.pillarboxing
= enable
;
373 f32
gfx_set_ar(f32 ar
) {
378 if (ar
< 16.0 / 480.0)
381 if (ar
> 640.0 / 16.0)
389 bool gfx_frame_start(void) {
390 if (_gfx
.busy
!= BUSY_READY
)
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
);
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
;
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
) {
418 if (_gfx
.colorop
.op
== 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
);
434 _gfx
.colorop
.op
= op
;
435 _gfx
.colorop
.c1
= c1
;
436 _gfx
.colorop
.c2
= c2
;
443 GX_SetTexCoordGen(GX_TEXCOORD0
, GX_TG_MTX2x4
, GX_TG_TEX0
, GX_IDENTITY
);
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
);
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
);
497 GX_SetNumTevStages(1);
498 GX_SetTevOrder(GX_TEVSTAGE0
, GX_TEXCOORD0
, GX_TEXMAP0
, GX_COLORZERO
);
500 GX_SetTevOp(GX_TEVSTAGE0
, GX_REPLACE
);
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
) {
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
);
527 void gfx_draw_tex(gfx_tex_t
*tex
, gfx_screen_coords_t
*coords
) {
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
,
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
,
546 gfx_tex_coord_t
*tile
;
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
,