aboutsummaryrefslogtreecommitdiff
path: root/backends/platform/wii/gfx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backends/platform/wii/gfx.cpp')
-rw-r--r--backends/platform/wii/gfx.cpp611
1 files changed, 611 insertions, 0 deletions
diff --git a/backends/platform/wii/gfx.cpp b/backends/platform/wii/gfx.cpp
new file mode 100644
index 0000000000..f34e2b1cca
--- /dev/null
+++ b/backends/platform/wii/gfx.cpp
@@ -0,0 +1,611 @@
+/*
+ * Gamecube/Wii VIDEO/GX subsystem wrapper
+ *
+ * Copyright (C) 2008, 2009 Andre Heider "dhewg" <dhewg@wiibrew.org>
+ *
+ * This code is licensed to you under the terms of the GNU GPL, version 2;
+ * see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ *
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "gfx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FIFO_SIZE (256 * 1024)
+#define ORIGIN_Z (-500)
+
+static GXRModeObj *_vm = NULL;
+static bool _dualstrike = false;
+
+static u32 *_fb[2] = { NULL, NULL };
+static u8 _fb_active = 0;
+
+static u8 *_fifo = NULL;
+
+static u16 _underscan_x = 0;
+static u16 _underscan_y = 0;
+static f32 _ar = 4.0 / 3.0;
+static bool _pillarboxing = false;
+static Mtx _view;
+
+static struct {
+ guVector pos;
+ guVector up;
+ guVector view;
+} _camera = {
+ { 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.5f, 0.0f },
+ { 0.0f, 0.0f, -0.5f }
+};
+
+// Standard, StandardAa, Ds, DsAa
+static GXRModeObj *mode_table[5][4] = {
+ { &TVNtsc480Prog, &TVNtsc480ProgAa, &TVNtsc240Ds, &TVNtsc240DsAa },
+ { &TVNtsc480IntDf, &TVNtsc480IntAa, &TVNtsc240Ds, &TVNtsc240DsAa },
+ { &TVPal528IntDf, &TVPal524IntAa, &TVPal264Ds, &TVPal264DsAa },
+ { &TVEurgb60Hz480IntDf, &TVEurgb60Hz480IntAa, &TVEurgb60Hz240Ds, &TVEurgb60Hz240DsAa },
+ { &TVMpal480IntDf, &TVMpal480IntAa, &TVMpal240Ds, &TVMpal240DsAa }
+};
+
+static gfx_video_mode_t _gfx_video_get_mode(void) {
+ gfx_video_mode_t mode;
+
+#ifdef HW_RVL
+ if ((CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable()) {
+ mode = GFX_MODE_PROGRESSIVE;
+ } else {
+ switch (CONF_GetVideo()) {
+ case CONF_VIDEO_PAL:
+ if (CONF_GetEuRGB60() > 0)
+ mode = GFX_MODE_EURGB60;
+ else
+ mode = GFX_MODE_PAL;
+ break;
+
+ case CONF_VIDEO_MPAL:
+ mode = GFX_MODE_MPAL;
+ break;
+
+ default:
+ mode = GFX_MODE_NTSC;
+ break;
+ }
+ }
+#else
+ switch (VIDEO_GetCurrentTvMode()) {
+ case VI_PAL:
+ mode = GFX_MODE_PAL;
+ break;
+ case VI_MPAL:
+ mode = GFX_MODE_MPAL;
+ break;
+ default:
+ mode = GFX_MODE_NTSC;
+ break;
+ }
+#endif
+
+ return mode;
+}
+
+void gfx_video_init(gfx_video_mode_t mode, gfx_video_setup_t setup) {
+ u8 i;
+
+ if (mode == GFX_MODE_AUTO)
+ mode = _gfx_video_get_mode();
+
+ _vm = mode_table[mode][setup];
+
+ _vm->viWidth = 672;
+ _vm->viXOrigin = (VI_MAX_WIDTH_NTSC - _vm->viWidth) / 2;
+
+ if (_vm)
+ VIDEO_WaitVSync();
+
+ VIDEO_Configure(_vm);
+
+ if (_fb[0])
+ free(MEM_K1_TO_K0(_fb[0]));
+ if (_fb[1])
+ free(MEM_K1_TO_K0(_fb[1]));
+
+ _fb[0] = (u32 *) MEM_K0_TO_K1(SYS_AllocateFramebuffer(_vm));
+ _fb[1] = (u32 *) MEM_K0_TO_K1(SYS_AllocateFramebuffer(_vm));
+
+ VIDEO_ClearFrameBuffer(_vm, _fb[0], COLOR_BLACK);
+ VIDEO_ClearFrameBuffer(_vm, _fb[1], COLOR_BLACK);
+
+ VIDEO_SetNextFramebuffer(_fb[_fb_active]);
+ VIDEO_SetBlack(FALSE);
+ VIDEO_Flush();
+
+ for (i = 0; i < 4; ++i)
+ VIDEO_WaitVSync();
+}
+
+void gfx_video_deinit(void) {
+ u8 i;
+
+ VIDEO_WaitVSync();
+ VIDEO_SetBlack(TRUE);
+ VIDEO_SetNextFramebuffer(NULL);
+ VIDEO_Flush();
+
+ for (i = 0; i < 4; ++i)
+ VIDEO_WaitVSync();
+
+ if (_fb[0]) {
+ free(MEM_K1_TO_K0(_fb[0]));
+ _fb[0] = NULL;
+ }
+
+ if (_fb[1]) {
+ free(MEM_K1_TO_K0(_fb[1]));
+ _fb[1] = NULL;
+ }
+}
+
+u16 gfx_video_get_width(void) {
+ return _vm->fbWidth;
+}
+
+u16 gfx_video_get_height(void) {
+ return _vm->efbHeight;
+}
+
+static void _update_viewport(void) {
+ f32 ar;
+ u16 correction;
+
+ u16 x1 = _underscan_x * 2;
+ u16 y1 = _underscan_y * 2;
+ u16 x2 = _vm->fbWidth - _underscan_x * 4;
+ u16 y2 = _vm->efbHeight - _underscan_y * 4;
+
+ if (_pillarboxing)
+ ar = 16.0 / 9.0;
+ else
+ ar = 4.0 / 3.0;
+
+ if (fabs(ar - _ar) > 0.01) {
+ if (ar > _ar) {
+ correction = _vm->viWidth -
+ (u16) round((f32) _vm->viWidth * _ar / ar);
+
+ x1 += correction / 2;
+ x2 -= correction;
+ } else {
+ correction = _vm->efbHeight -
+ (u16) round((f32) _vm->efbHeight * ar / _ar);
+
+ if (_dualstrike)
+ correction /= 2;
+
+ y1 += correction / 2;
+ y2 -= correction;
+ }
+ }
+
+ GX_SetViewport(x1, y1, x2, y2, 0, 1);
+ GX_SetScissor(x1, y1, x2, y2);
+}
+
+void gfx_init(void) {
+ Mtx44 p;
+ GXColor bg = { 0, 0, 0, 0xff };
+ f32 yscale;
+ u32 xfbHeight;
+
+ GX_AbortFrame();
+
+ if (!_fifo)
+ _fifo = (u8 *) memalign(32, FIFO_SIZE);
+
+ memset(_fifo, 0, FIFO_SIZE);
+ GX_Init(_fifo, FIFO_SIZE);
+
+ GX_SetCopyClear(bg, 0x00ffffff);
+
+ _update_viewport();
+
+ _dualstrike = _vm->viHeight == 2 * _vm->xfbHeight;
+ yscale = GX_GetYScaleFactor(_vm->efbHeight, _vm->xfbHeight);
+ xfbHeight = GX_SetDispCopyYScale(yscale);
+ GX_SetDispCopySrc(0, 0, _vm->fbWidth, _vm->efbHeight);
+ GX_SetDispCopyDst(_vm->fbWidth, xfbHeight);
+ GX_SetCopyFilter(_vm->aa, _vm->sample_pattern, GX_TRUE, _vm->vfilter);
+ GX_SetFieldMode(_vm->field_rendering, _dualstrike ? GX_ENABLE : GX_DISABLE);
+
+ if (_vm->aa)
+ GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
+ else
+ GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
+
+ GX_SetCullMode(GX_CULL_NONE);
+ GX_SetDispCopyGamma(GX_GM_1_0);
+
+ GX_ClearVtxDesc();
+ GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
+ GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
+ GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
+
+ GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
+ GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
+ GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
+
+ GX_InvVtxCache();
+ GX_InvalidateTexAll();
+
+ memset(&_view, 0, sizeof(Mtx));
+ guLookAt(_view, &_camera.pos, &_camera.up, &_camera.view);
+
+ guOrtho(p, 0, _vm->efbHeight, 0, _vm->fbWidth, 100, 1000);
+ GX_LoadProjectionMtx (p, GX_ORTHOGRAPHIC);
+
+ GX_Flush();
+}
+
+void gfx_deinit(void) {
+ GX_AbortFrame();
+
+ free(_fifo);
+ _fifo = NULL;
+}
+
+void gfx_set_underscan(u16 underscan_x, u16 underscan_y) {
+ _underscan_x = underscan_x;
+ _underscan_y = underscan_y;
+
+ if (_underscan_x > 32)
+ _underscan_x = 32;
+
+ if (_underscan_y > 32)
+ _underscan_y = 32;
+
+ _update_viewport();
+ GX_Flush();
+}
+
+void gfx_set_ar(f32 ar) {
+ _ar = ar;
+
+ if (ar < 16.0 / 480.0)
+ ar = 16.0 / 480.0;
+
+ if (ar > 640.0 / 16.0)
+ ar = 640.0 / 16.0;
+
+ _update_viewport();
+ GX_Flush();
+}
+
+void gfx_set_pillarboxing(bool enable) {
+ _pillarboxing = enable;
+
+ _update_viewport();
+ GX_Flush();
+}
+
+bool gfx_tex_init(gfx_tex_t *tex, gfx_tex_format_t format, u32 tlut_name,
+ u16 width, u16 height) {
+ u8 bpp;
+ u8 fmt_tex;
+ bool tlut;
+ u8 fmt_tlut;
+ u32 ax, ay;
+ u32 memsize;
+
+ if (!tex)
+ return false;
+
+ switch(format) {
+ case GFX_TF_RGB565:
+ bpp = 2;
+ fmt_tex = GX_TF_RGB565;
+ tlut = false;
+ fmt_tlut = 0;
+ ax = 3;
+ ay = 3;
+ break;
+
+ case GFX_TF_RGB5A3:
+ bpp = 2;
+ fmt_tex = GX_TF_RGB5A3;
+ tlut = false;
+ fmt_tlut = 0;
+ ax = 3;
+ ay = 3;
+ break;
+
+ case GFX_TF_PALETTE_RGB565:
+ bpp = 1;
+ fmt_tex = GX_TF_CI8;
+ tlut = true;
+ fmt_tlut = GX_TL_RGB565;
+ ax = 7;
+ ay = 3;
+ break;
+
+ case GFX_TF_PALETTE_RGB5A3:
+ bpp = 1;
+ fmt_tex = GX_TF_CI8;
+ tlut = true;
+ fmt_tlut = GX_TL_RGB5A3;
+ ax = 7;
+ ay = 3;
+ break;
+
+ default:
+ gfx_tex_deinit(tex);
+ return false;
+ }
+
+ if ((width & ax) || (height & ay)) {
+ gfx_tex_deinit(tex);
+ return false;
+ }
+
+ if (tlut) {
+ if (!tex->palette) {
+ tex->palette = (u16 *) memalign(32, 256 * 2);
+ if (!tex->palette) {
+ gfx_tex_deinit(tex);
+ return false;
+ }
+
+ memset(tex->palette, 0, 256 * 2);
+ DCFlushRange(tex->palette, 256 * 2);
+ }
+
+ tex->tlut_name = tlut_name;
+ GX_InitTlutObj(&tex->tlut, tex->palette, fmt_tlut, 256);
+ } else {
+ tex->tlut_name = 0;
+ free(tex->palette);
+ tex->palette = NULL;
+ }
+
+ if (!tex->pixels || (width != tex->width) || (height != tex->height ||
+ bpp != tex->bpp)) {
+ free(tex->pixels);
+
+ memsize = width * height * bpp;
+ tex->pixels = memalign(32, memsize);
+
+ if (!tex->pixels) {
+ gfx_tex_deinit(tex);
+ return false;
+ }
+
+ memset(tex->pixels, 0, memsize);
+ DCFlushRange(tex->pixels, memsize);
+ }
+
+ tex->format = format;
+ tex->width = width;
+ tex->height = height;
+ tex->bpp = bpp;
+
+ if (tlut) {
+ GX_LoadTlut(&tex->tlut, tlut_name);
+ GX_InitTexObjCI(&tex->obj, tex->pixels, width, height, fmt_tex,
+ GX_CLAMP, GX_CLAMP, GX_FALSE, tlut_name);
+ } else {
+ GX_InitTexObj(&tex->obj, tex->pixels, width, height, fmt_tex,
+ GX_CLAMP, GX_CLAMP, GX_FALSE);
+ }
+
+ return true;
+}
+
+void gfx_tex_deinit(gfx_tex_t *tex) {
+ if (!tex)
+ return;
+
+ free(tex->pixels);
+ free(tex->palette);
+ memset(tex, 0, sizeof(gfx_tex_t));
+}
+
+void gfx_coords(gfx_coords_t *coords, gfx_tex_t *tex, gfx_coord_t type) {
+ if (!coords || !tex)
+ return;
+
+ switch(type) {
+ case GFX_COORD_FULLSCREEN:
+ coords->x = 0.0;
+ coords->y = 0.0;
+ coords->w = _vm->fbWidth;
+ coords->h = _vm->efbHeight;
+ break;
+
+ case GFX_COORD_CENTER:
+ coords->x = (_vm->fbWidth - tex->width) / 2;
+ coords->y = (_vm->efbHeight - tex->height) / 2;
+ coords->w = tex->width;
+ coords->h = tex->height;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool gfx_tex_flush_texture(gfx_tex_t *tex) {
+ if (!tex)
+ return false;
+
+ DCFlushRange(tex->pixels, tex->width * tex->height * tex->bpp);
+ return true;
+}
+
+bool gfx_tex_flush_palette(gfx_tex_t *tex) {
+ if (!tex || !tex->palette)
+ return false;
+
+ DCFlushRange(tex->palette, 256 * 2);
+ GX_LoadTlut(&tex->tlut, tex->tlut_name);
+
+ return true;
+}
+
+bool gfx_tex_clear_palette(gfx_tex_t *tex) {
+ if (!tex || !tex->palette)
+ return false;
+
+ memset(tex->palette, 0, 256 * 2);
+ DCFlushRange(tex->palette, 256 * 2);
+ GX_LoadTlut(&tex->tlut, tex->tlut_name);
+
+ return true;
+}
+
+bool gfx_tex_convert(gfx_tex_t *tex, const void *src) {
+ bool ret;
+ u16 w, h, x, y;
+ u64 *dst, *src1, *src2, *src3, *src4;
+ u16 rowpitch;
+ u16 pitch;
+ const u8 *s = (const u8 *) src;
+
+ if (!tex)
+ return false;
+
+ ret = false;
+ w = tex->width;
+ h = tex->height;
+
+ switch(tex->format) {
+ case GFX_TF_RGB565:
+ case GFX_TF_RGB5A3:
+ pitch = w * 2;
+ dst = (u64 *) tex->pixels;
+ src1 = (u64 *) s;
+ src2 = (u64 *) (s + pitch);
+ src3 = (u64 *) (s + (pitch * 2));
+ src4 = (u64 *) (s + (pitch * 3));
+ rowpitch = (pitch >> 3) * 3 + pitch % 8;
+
+ for (y = 0; y < h; y += 4) {
+ for (x = 0; x < (w >> 2); x++) {
+ *dst++ = *src1++;
+ *dst++ = *src2++;
+ *dst++ = *src3++;
+ *dst++ = *src4++;
+ }
+
+ src1 += rowpitch;
+ src2 += rowpitch;
+ src3 += rowpitch;
+ src4 += rowpitch;
+ }
+
+ ret = true;
+ break;
+
+ case GFX_TF_PALETTE_RGB565:
+ case GFX_TF_PALETTE_RGB5A3:
+ pitch = w;
+ dst = (u64 *) tex->pixels;
+ src1 = (u64 *) s;
+ src2 = (u64 *) (s + pitch);
+ src3 = (u64 *) (s + (pitch * 2));
+ src4 = (u64 *) (s + (pitch * 3));
+ rowpitch = (pitch >> 3) * 3 + pitch % 8;
+
+ for (y = 0; y < h; y += 4) {
+ for (x = 0; x < (w >> 3); x++) {
+ *dst++ = *src1++;
+ *dst++ = *src2++;
+ *dst++ = *src3++;
+ *dst++ = *src4++;
+ }
+
+ src1 += rowpitch;
+ src2 += rowpitch;
+ src3 += rowpitch;
+ src4 += rowpitch;
+ }
+
+ ret = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (ret)
+ DCFlushRange(tex->pixels, w * h * tex->bpp);
+
+ return ret;
+}
+
+void gfx_frame_start(void) {
+ GX_InvVtxCache();
+ GX_InvalidateTexAll();
+
+ GX_SetNumChans(1);
+ GX_SetNumTexGens(1);
+ GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
+
+ GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE);
+ GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
+
+ GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
+}
+
+void gfx_frame_end(void) {
+ GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
+ GX_SetColorUpdate(GX_TRUE);
+
+ GX_CopyDisp(_fb[_fb_active], GX_TRUE);
+ GX_Flush();
+ GX_DrawDone();
+
+ VIDEO_SetNextFramebuffer(_fb[_fb_active]);
+ VIDEO_Flush();
+ VIDEO_WaitVSync();
+
+ _fb_active ^= 1;
+}
+
+static void _tex_vert(f32 x, f32 y, f32 z, f32 s, f32 t, u32 c) {
+ GX_Position3f32(x, y, z);
+ GX_Color1u32(c);
+ GX_TexCoord2f32(s, t);
+}
+
+void gfx_draw_tex(gfx_tex_t *tex, gfx_coords_t *coords) {
+ Mtx m;
+ Mtx mv;
+
+ if (!tex || !coords)
+ return;
+
+ GX_LoadTexObj(&tex->obj, GX_TEXMAP0);
+
+ guMtxIdentity(m);
+ guMtxTrans(m, coords->x, coords->y, ORIGIN_Z);
+ guMtxConcat(_view, m, mv);
+ GX_LoadPosMtxImm(mv, GX_PNMTX0);
+
+ GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
+ _tex_vert(0.0, 0.0, 0.0, 0.0, 0.0, 0xffffffff);
+ _tex_vert(coords->w, 0.0, 0.0, 1.0, 0.0, 0xffffffff);
+ _tex_vert(coords->w, coords->h, 0.0, 1.0, 1.0, 0xffffffff);
+ _tex_vert(0.0, coords->h, 0.0, 0.0, 1.0, 0xffffffff);
+ GX_End();
+}
+
+#ifdef __cplusplus
+}
+#endif
+