mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 21:32:45 +00:00
51d238875e
ever implemented for OpenGL2 driver, lots of code debt, best to instead just keep improving the overlay system instead which is already available for most video drivers
1816 lines
47 KiB
C
1816 lines
47 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
* Copyright (C) 2012-2015 - Michael Lelli
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <malloc.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <gccore.h>
|
|
#include <ogcsys.h>
|
|
|
|
#include <libretro.h>
|
|
#include <verbosity.h>
|
|
#include <streams/interface_stream.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../../config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_MENU
|
|
#include "../../menu/menu_driver.h"
|
|
#endif
|
|
|
|
#ifdef HW_RVL
|
|
#include "../../memory/wii/mem2_manager.h"
|
|
#endif
|
|
|
|
#include "../font_driver.h"
|
|
|
|
#include "../drivers_font_renderer/bitmap.h"
|
|
#include <defines/gx_defines.h>
|
|
#include "../../configuration.h"
|
|
#include "../../driver.h"
|
|
|
|
#ifndef _CPU_ISR_Disable
|
|
#define _CPU_ISR_Disable( _isr_cookie ) \
|
|
{ register u32 _disable_mask = 0; \
|
|
_isr_cookie = 0; \
|
|
__asm__ __volatile__ ( \
|
|
"mfmsr %0\n" \
|
|
"rlwinm %1,%0,0,17,15\n" \
|
|
"mtmsr %1\n" \
|
|
"extrwi %0,%0,1,16" \
|
|
: "=&r" ((_isr_cookie)), "=&r" ((_disable_mask)) \
|
|
: "0" ((_isr_cookie)), "1" ((_disable_mask)) \
|
|
); \
|
|
}
|
|
#endif
|
|
|
|
#ifndef _CPU_ISR_Restore
|
|
#define _CPU_ISR_Restore( _isr_cookie ) \
|
|
{ register u32 _enable_mask = 0; \
|
|
__asm__ __volatile__ ( \
|
|
" cmpwi %0,0\n" \
|
|
" beq 1f\n" \
|
|
" mfmsr %1\n" \
|
|
" ori %1,%1,0x8000\n" \
|
|
" mtmsr %1\n" \
|
|
"1:" \
|
|
: "=r"((_isr_cookie)),"=&r" ((_enable_mask)) \
|
|
: "0"((_isr_cookie)),"1" ((_enable_mask)) \
|
|
); \
|
|
}
|
|
#endif
|
|
|
|
extern syssram* __SYS_LockSram(void);
|
|
extern u32 __SYS_UnlockSram(u32 write);
|
|
|
|
struct gx_overlay_data
|
|
{
|
|
GXTexObj tex;
|
|
float tex_coord[8];
|
|
float vertex_coord[8];
|
|
float alpha_mod;
|
|
};
|
|
|
|
typedef struct gx_video
|
|
{
|
|
bool should_resize;
|
|
bool keep_aspect;
|
|
bool double_strike;
|
|
bool rgb32;
|
|
bool menu_texture_enable;
|
|
bool vsync;
|
|
#ifdef HAVE_OVERLAY
|
|
bool overlay_enable;
|
|
bool overlay_full_screen;
|
|
#endif
|
|
|
|
int8_t system_xOrigin;
|
|
int8_t used_system_xOrigin;
|
|
int8_t xOriginNeg;
|
|
int8_t xOriginPos;
|
|
int8_t yOriginNeg;
|
|
int8_t yOriginPos;
|
|
|
|
uint16_t xOrigin;
|
|
uint16_t yOrigin;
|
|
|
|
unsigned scale;
|
|
unsigned overscan_correction_top;
|
|
unsigned overscan_correction_bottom;
|
|
unsigned old_width;
|
|
unsigned old_height;
|
|
unsigned current_framebuf;
|
|
#ifdef HAVE_OVERLAY
|
|
unsigned overlays;
|
|
#endif
|
|
|
|
uint32_t orientation;
|
|
|
|
video_viewport_t vp;
|
|
void *framebuf[2];
|
|
uint32_t *menu_data; /* FIXME: Should be const uint16_t*. */
|
|
#ifdef HAVE_OVERLAY
|
|
struct gx_overlay_data *overlay;
|
|
#endif
|
|
} gx_video_t;
|
|
|
|
static struct
|
|
{
|
|
unsigned width;
|
|
unsigned height;
|
|
GXTexObj obj;
|
|
uint32_t *data; /* needs to be resizable. */
|
|
} g_tex;
|
|
|
|
static struct
|
|
{
|
|
uint32_t data[240 * 212];
|
|
GXTexObj obj;
|
|
} menu_tex ATTRIBUTE_ALIGN(32);
|
|
|
|
static OSCond g_video_cond;
|
|
|
|
static volatile bool g_draw_done = false;
|
|
|
|
static uint8_t gx_fifo[256 * 1024] ATTRIBUTE_ALIGN(32);
|
|
static uint8_t display_list[1024] ATTRIBUTE_ALIGN(32);
|
|
|
|
static uint32_t retraceCount = 0;
|
|
static uint32_t referenceRetraceCount = 0;
|
|
|
|
static unsigned max_height = 0;
|
|
|
|
static size_t display_list_size = 0;
|
|
|
|
GXRModeObj gx_mode;
|
|
|
|
float verts[16] ATTRIBUTE_ALIGN(32) = {
|
|
-1, 1, -0.5,
|
|
1, 1, -0.5,
|
|
-1, -1, -0.5,
|
|
1, -1, -0.5,
|
|
};
|
|
|
|
float vertex_ptr[8] ATTRIBUTE_ALIGN(32) = {
|
|
0, 0,
|
|
1, 0,
|
|
0, 1,
|
|
1, 1,
|
|
};
|
|
|
|
u8 color_ptr[16] ATTRIBUTE_ALIGN(32) = {
|
|
0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF,
|
|
};
|
|
|
|
enum
|
|
{
|
|
GX_RESOLUTIONS_DEFAULT = 0,
|
|
GX_RESOLUTIONS_512_192,
|
|
GX_RESOLUTIONS_598_200,
|
|
GX_RESOLUTIONS_640_200,
|
|
GX_RESOLUTIONS_384_224,
|
|
GX_RESOLUTIONS_448_224,
|
|
GX_RESOLUTIONS_480_224,
|
|
GX_RESOLUTIONS_512_224,
|
|
GX_RESOLUTIONS_576_224,
|
|
GX_RESOLUTIONS_608_224,
|
|
GX_RESOLUTIONS_640_224,
|
|
GX_RESOLUTIONS_340_232,
|
|
GX_RESOLUTIONS_512_232,
|
|
GX_RESOLUTIONS_512_236,
|
|
GX_RESOLUTIONS_336_240,
|
|
GX_RESOLUTIONS_352_240,
|
|
GX_RESOLUTIONS_384_240,
|
|
GX_RESOLUTIONS_512_240,
|
|
GX_RESOLUTIONS_530_240,
|
|
GX_RESOLUTIONS_608_240,
|
|
GX_RESOLUTIONS_640_240,
|
|
GX_RESOLUTIONS_512_384,
|
|
GX_RESOLUTIONS_598_400,
|
|
GX_RESOLUTIONS_640_400,
|
|
GX_RESOLUTIONS_384_448,
|
|
GX_RESOLUTIONS_448_448,
|
|
GX_RESOLUTIONS_480_448,
|
|
GX_RESOLUTIONS_512_448,
|
|
GX_RESOLUTIONS_576_448,
|
|
GX_RESOLUTIONS_608_448,
|
|
GX_RESOLUTIONS_640_448,
|
|
GX_RESOLUTIONS_340_464,
|
|
GX_RESOLUTIONS_512_464,
|
|
GX_RESOLUTIONS_512_472,
|
|
GX_RESOLUTIONS_352_480,
|
|
GX_RESOLUTIONS_384_480,
|
|
GX_RESOLUTIONS_512_480,
|
|
GX_RESOLUTIONS_530_480,
|
|
GX_RESOLUTIONS_608_480,
|
|
GX_RESOLUTIONS_640_480,
|
|
GX_RESOLUTIONS_LAST = GX_RESOLUTIONS_640_480,
|
|
};
|
|
|
|
unsigned menu_gx_resolutions[][2] = {
|
|
{ 0, 0 }, /* Let the system choose its preferred resolution, for NTSC is 640x480 */
|
|
{ 512, 192 },
|
|
{ 598, 200 },
|
|
{ 640, 200 },
|
|
{ 384, 224 },
|
|
{ 448, 224 },
|
|
{ 480, 224 },
|
|
{ 512, 224 },
|
|
{ 576, 224 },
|
|
{ 608, 224 },
|
|
{ 640, 224 },
|
|
{ 340, 232 },
|
|
{ 512, 232 },
|
|
{ 512, 236 },
|
|
{ 336, 240 },
|
|
{ 352, 240 },
|
|
{ 384, 240 },
|
|
{ 512, 240 },
|
|
{ 530, 240 },
|
|
{ 608, 240 },
|
|
{ 640, 240 },
|
|
{ 512, 384 },
|
|
{ 598, 400 },
|
|
{ 640, 400 },
|
|
{ 384, 448 },
|
|
{ 448, 448 },
|
|
{ 480, 448 },
|
|
{ 512, 448 },
|
|
{ 576, 448 },
|
|
{ 608, 448 },
|
|
{ 640, 448 },
|
|
{ 340, 464 },
|
|
{ 512, 464 },
|
|
{ 512, 472 },
|
|
{ 352, 480 },
|
|
{ 384, 480 },
|
|
{ 512, 480 },
|
|
{ 530, 480 },
|
|
{ 608, 480 },
|
|
{ 640, 480 },
|
|
};
|
|
|
|
static void retrace_callback(u32 retrace_count)
|
|
{
|
|
uint32_t level = 0;
|
|
|
|
(void)retrace_count;
|
|
|
|
g_draw_done = true;
|
|
OSSignalCond(g_video_cond);
|
|
_CPU_ISR_Disable(level);
|
|
retraceCount = retrace_count;
|
|
_CPU_ISR_Restore(level);
|
|
}
|
|
|
|
static bool gx_is_valid_xorigin(gx_video_t *gx, int origin)
|
|
{
|
|
if (!gx)
|
|
return false;
|
|
|
|
if (origin < 0 || origin + gx->used_system_xOrigin < 0 ||
|
|
gx_mode.viWidth + origin + gx->used_system_xOrigin > 720)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool gx_is_valid_yorigin(int origin)
|
|
{
|
|
if (origin < 0 || gx_mode.viHeight + origin > max_height)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static void gx_set_video_mode(void *data, unsigned fbWidth, unsigned lines,
|
|
bool fullscreen)
|
|
{
|
|
int tmpOrigin;
|
|
float refresh_rate;
|
|
bool progressive;
|
|
unsigned modetype, tvmode, max_width, i;
|
|
size_t new_fb_pitch = 0;
|
|
unsigned new_fb_width = 0;
|
|
unsigned new_fb_height = 0;
|
|
float y_scale = 0.0f;
|
|
uint16_t xfbWidth = 0;
|
|
uint16_t xfbHeight = 0;
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
if (!gx)
|
|
return;
|
|
settings_t *settings = config_get_ptr();
|
|
if (!settings)
|
|
return;
|
|
unsigned viHeightMultiplier = 1;
|
|
bool vfilter = settings->bools.video_vfilter;
|
|
unsigned viWidth = settings->uints.video_viwidth;
|
|
unsigned rgui_aspect_ratio = settings->uints.menu_rgui_aspect_ratio;
|
|
|
|
/* stop vsync callback */
|
|
VIDEO_SetPostRetraceCallback(NULL);
|
|
g_draw_done = false;
|
|
/* wait for next even field */
|
|
/* this prevents screen artifacts when switching
|
|
* between interlaced & non-interlaced modes.
|
|
*
|
|
* But move on if it takes over 3 frames as sometimes under dolphin
|
|
* this stays at constant value.
|
|
*/
|
|
for (i = 0; i < 3; i++) {
|
|
VIDEO_WaitVSync();
|
|
if (VIDEO_GetNextField())
|
|
break;
|
|
}
|
|
|
|
VIDEO_SetBlack(true);
|
|
VIDEO_Flush();
|
|
|
|
#if defined(HW_RVL)
|
|
progressive = CONF_GetProgressiveScan() > 0 && VIDEO_HaveComponentCable();
|
|
|
|
switch (CONF_GetVideo())
|
|
{
|
|
case CONF_VIDEO_PAL:
|
|
if (CONF_GetEuRGB60() > 0)
|
|
tvmode = VI_EURGB60;
|
|
else
|
|
tvmode = VI_PAL;
|
|
break;
|
|
case CONF_VIDEO_MPAL:
|
|
tvmode = VI_MPAL;
|
|
break;
|
|
default:
|
|
tvmode = VI_NTSC;
|
|
break;
|
|
}
|
|
#else
|
|
progressive = VIDEO_HaveComponentCable();
|
|
tvmode = VIDEO_GetCurrentTvMode();
|
|
#endif
|
|
|
|
switch (tvmode)
|
|
{
|
|
case VI_PAL:
|
|
max_width = VI_MAX_WIDTH_PAL;
|
|
max_height = VI_MAX_HEIGHT_PAL;
|
|
break;
|
|
case VI_MPAL:
|
|
max_width = VI_MAX_WIDTH_MPAL;
|
|
max_height = VI_MAX_HEIGHT_MPAL;
|
|
break;
|
|
case VI_EURGB60:
|
|
max_width = VI_MAX_WIDTH_EURGB60;
|
|
max_height = VI_MAX_HEIGHT_EURGB60;
|
|
break;
|
|
default:
|
|
tvmode = VI_NTSC;
|
|
max_width = VI_MAX_WIDTH_NTSC;
|
|
max_height = VI_MAX_HEIGHT_NTSC;
|
|
break;
|
|
}
|
|
|
|
if (lines == 0 || fbWidth == 0)
|
|
{
|
|
GXRModeObj tmp_mode;
|
|
VIDEO_GetPreferredMode(&tmp_mode);
|
|
fbWidth = tmp_mode.fbWidth;
|
|
lines = tmp_mode.xfbHeight;
|
|
}
|
|
|
|
if (lines <= max_height / 2)
|
|
{
|
|
modetype = VI_NON_INTERLACE;
|
|
viHeightMultiplier = 2;
|
|
}
|
|
else
|
|
modetype = progressive ? VI_PROGRESSIVE : VI_INTERLACE;
|
|
|
|
if (lines > max_height)
|
|
lines = max_height;
|
|
|
|
if (fbWidth > max_width)
|
|
fbWidth = max_width;
|
|
|
|
gx_mode.viTVMode = VI_TVMODE(tvmode, modetype);
|
|
gx_mode.fbWidth = fbWidth;
|
|
gx_mode.efbHeight = MIN(lines, 480);
|
|
|
|
if (modetype == VI_NON_INTERLACE && lines > max_height / 2)
|
|
gx_mode.xfbHeight = max_height / 2;
|
|
else if (modetype != VI_NON_INTERLACE && lines > max_height)
|
|
gx_mode.xfbHeight = max_height;
|
|
else
|
|
gx_mode.xfbHeight = lines;
|
|
|
|
gx_mode.viWidth = viWidth;
|
|
gx_mode.viHeight = gx_mode.xfbHeight * viHeightMultiplier;
|
|
|
|
gx->used_system_xOrigin = gx->system_xOrigin;
|
|
|
|
if (gx->used_system_xOrigin > 0)
|
|
{
|
|
while (viWidth + gx->used_system_xOrigin > 720)
|
|
gx->used_system_xOrigin--;
|
|
}
|
|
else if (gx->used_system_xOrigin < 0)
|
|
{
|
|
while (viWidth + gx->used_system_xOrigin > 720)
|
|
gx->used_system_xOrigin++;
|
|
}
|
|
|
|
tmpOrigin = (max_width - gx_mode.viWidth) / 2;
|
|
|
|
if (gx->system_xOrigin > 0)
|
|
{
|
|
while (!gx_is_valid_xorigin(gx, tmpOrigin))
|
|
tmpOrigin--;
|
|
}
|
|
else if (gx->system_xOrigin < 0)
|
|
{
|
|
while (!gx_is_valid_xorigin(gx, tmpOrigin))
|
|
tmpOrigin++;
|
|
}
|
|
|
|
gx_mode.viXOrigin = gx->xOrigin = tmpOrigin;
|
|
gx_mode.viYOrigin = gx->yOrigin =
|
|
(max_height - gx_mode.viHeight) / (2 * viHeightMultiplier);
|
|
|
|
gx->xOriginNeg = 0;
|
|
gx->xOriginPos = 0;
|
|
|
|
while (gx_is_valid_xorigin(gx, gx_mode.viXOrigin+(gx->xOriginNeg-1)))
|
|
gx->xOriginNeg--;
|
|
while (gx_is_valid_xorigin(gx, gx_mode.viXOrigin+(gx->xOriginPos+1)))
|
|
gx->xOriginPos++;
|
|
gx->yOriginNeg = 0;
|
|
gx->yOriginPos = 0;
|
|
while (gx_is_valid_yorigin(gx_mode.viYOrigin+(gx->yOriginNeg-1)))
|
|
gx->yOriginNeg--;
|
|
while (gx_is_valid_yorigin(gx_mode.viYOrigin+(gx->yOriginPos+1)))
|
|
gx->yOriginPos++;
|
|
|
|
gx_mode.xfbMode = (modetype == VI_INTERLACE)
|
|
? VI_XFBMODE_DF
|
|
: VI_XFBMODE_SF;
|
|
gx_mode.field_rendering = GX_FALSE;
|
|
gx_mode.aa = GX_FALSE;
|
|
|
|
for (i = 0; i < 12; i++)
|
|
gx_mode.sample_pattern[i][0] = gx_mode.sample_pattern[i][1] = 6;
|
|
|
|
if (modetype != VI_NON_INTERLACE && vfilter)
|
|
{
|
|
gx_mode.vfilter[0] = 8;
|
|
gx_mode.vfilter[1] = 8;
|
|
gx_mode.vfilter[2] = 10;
|
|
gx_mode.vfilter[3] = 12;
|
|
gx_mode.vfilter[4] = 10;
|
|
gx_mode.vfilter[5] = 8;
|
|
gx_mode.vfilter[6] = 8;
|
|
}
|
|
else
|
|
{
|
|
gx_mode.vfilter[0] = 0;
|
|
gx_mode.vfilter[1] = 0;
|
|
gx_mode.vfilter[2] = 21;
|
|
gx_mode.vfilter[3] = 22;
|
|
gx_mode.vfilter[4] = 21;
|
|
gx_mode.vfilter[5] = 0;
|
|
gx_mode.vfilter[6] = 0;
|
|
}
|
|
|
|
gx->vp.full_width = gx_mode.fbWidth;
|
|
gx->vp.full_height = gx_mode.xfbHeight;
|
|
gx->double_strike = (modetype == VI_NON_INTERLACE);
|
|
gx->should_resize = true;
|
|
|
|
/* Calculate menu dimensions
|
|
* > Height is set as large as possible, limited to
|
|
* maximum of 240 (standard RGUI framebuffer height) */
|
|
new_fb_height = (gx_mode.efbHeight / (gx->double_strike ? 1 : 2)) & ~3;
|
|
if (new_fb_height > 240)
|
|
new_fb_height = 240;
|
|
/* > Width is dertermined by current RGUI aspect ratio
|
|
* (note that width is in principal limited by hardware
|
|
* constraints to 640, but we impose a lower limit of
|
|
* 424 since this is the nearest to the RGUI 'standard'
|
|
* for 16:9 aspect ratios which is supported by the Wii
|
|
* - i.e. last two bits of value must be zero, so 426->424) */
|
|
switch (rgui_aspect_ratio)
|
|
{
|
|
case RGUI_ASPECT_RATIO_16_9:
|
|
case RGUI_ASPECT_RATIO_16_9_CENTRE:
|
|
if (new_fb_height == 240)
|
|
new_fb_width = 424;
|
|
else
|
|
new_fb_width = (unsigned)((16.0f / 9.0f) * (float)new_fb_height) & ~3;
|
|
break;
|
|
case RGUI_ASPECT_RATIO_16_10:
|
|
case RGUI_ASPECT_RATIO_16_10_CENTRE:
|
|
if (new_fb_height == 240)
|
|
new_fb_width = 384;
|
|
else
|
|
new_fb_width = (unsigned)((16.0f / 10.0f) * (float)new_fb_height) & ~3;
|
|
break;
|
|
default:
|
|
/* 4:3 */
|
|
if (new_fb_height == 240)
|
|
new_fb_width = 320;
|
|
else
|
|
new_fb_width = (unsigned)((4.0f / 3.0f) * (float)new_fb_height) & ~3;
|
|
break;
|
|
}
|
|
if (new_fb_width > 424)
|
|
new_fb_width = 424;
|
|
|
|
new_fb_pitch = new_fb_width * 2;
|
|
|
|
gfx_display_set_width(new_fb_width);
|
|
gfx_display_set_height(new_fb_height);
|
|
gfx_display_set_framebuffer_pitch(new_fb_pitch);
|
|
|
|
GX_SetViewportJitter(0, 0, gx_mode.fbWidth, gx_mode.efbHeight, 0, 1, 1);
|
|
GX_SetDispCopySrc(0, 0, gx_mode.fbWidth, gx_mode.efbHeight);
|
|
|
|
y_scale = GX_GetYScaleFactor(gx_mode.efbHeight, gx_mode.xfbHeight);
|
|
xfbWidth = VIDEO_PadFramebufferWidth(gx_mode.fbWidth);
|
|
xfbHeight = GX_SetDispCopyYScale((f32)y_scale);
|
|
GX_SetDispCopyDst((u16)xfbWidth, (u16)xfbHeight);
|
|
|
|
GX_SetCopyFilter(gx_mode.aa, gx_mode.sample_pattern,
|
|
GX_TRUE, gx_mode.vfilter);
|
|
GXColor color = { 0, 0, 0, 0xff };
|
|
GX_SetCopyClear(color, GX_MAX_Z24);
|
|
GX_SetFieldMode(gx_mode.field_rendering,
|
|
(gx_mode.viHeight == 2 * gx_mode.xfbHeight) ? GX_ENABLE : GX_DISABLE);
|
|
GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
|
|
GX_InvalidateTexAll();
|
|
GX_Flush();
|
|
|
|
/* Now apply all the configuration to the screen */
|
|
VIDEO_Configure(&gx_mode);
|
|
VIDEO_ClearFrameBuffer(&gx_mode, gx->framebuf[0], COLOR_BLACK);
|
|
VIDEO_ClearFrameBuffer(&gx_mode, gx->framebuf[1], COLOR_BLACK);
|
|
VIDEO_SetNextFramebuffer(gx->framebuf[0]);
|
|
gx->current_framebuf = 0;
|
|
/* re-activate the Vsync callback */
|
|
VIDEO_SetPostRetraceCallback(retrace_callback);
|
|
VIDEO_SetBlack(false);
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
|
|
RARCH_LOG("[GX]: Resolution: %dx%d (%s)\n", gx_mode.fbWidth,
|
|
gx_mode.efbHeight, (gx_mode.viTVMode & 3) == VI_INTERLACE
|
|
? "interlaced" : "progressive");
|
|
|
|
if (tvmode == VI_PAL)
|
|
{
|
|
refresh_rate = 50.0f;
|
|
if (modetype == VI_NON_INTERLACE)
|
|
refresh_rate = 50.0801f;
|
|
}
|
|
else
|
|
{
|
|
refresh_rate = 59.94f;
|
|
if (modetype == VI_NON_INTERLACE)
|
|
refresh_rate = 59.8261f;
|
|
}
|
|
|
|
driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, &refresh_rate);
|
|
}
|
|
|
|
static void gx_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
if (!gx)
|
|
return;
|
|
|
|
gx->keep_aspect = true;
|
|
gx->should_resize = true;
|
|
}
|
|
|
|
static void gx_get_video_output_size(void *data,
|
|
unsigned *width, unsigned *height, char *desc, size_t desc_len)
|
|
{
|
|
global_t *global = global_get_ptr();
|
|
if (!global)
|
|
return;
|
|
|
|
(void)data;
|
|
|
|
/* If the current index is out of bound default it to zero */
|
|
if (global->console.screen.resolutions.current.id > GX_RESOLUTIONS_LAST)
|
|
global->console.screen.resolutions.current.id = 0;
|
|
|
|
*width = menu_gx_resolutions[
|
|
global->console.screen.resolutions.current.id][0];
|
|
*height = menu_gx_resolutions[
|
|
global->console.screen.resolutions.current.id][1];
|
|
}
|
|
|
|
static void setup_video_mode(gx_video_t *gx)
|
|
{
|
|
unsigned width = 0;
|
|
unsigned height = 0;
|
|
char desc[64] = {0};
|
|
|
|
if (!gx->framebuf[0])
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < 2; i++)
|
|
gx->framebuf[i] = MEM_K0_TO_K1(
|
|
memalign(32, 640 * 576 * VI_DISPLAY_PIX_SZ));
|
|
}
|
|
|
|
gx->orientation = ORIENTATION_NORMAL;
|
|
OSInitThreadQueue(&g_video_cond);
|
|
|
|
gx_get_video_output_size(gx, &width, &height, desc, sizeof(desc));
|
|
gx_set_video_mode(gx, width, height, true);
|
|
}
|
|
|
|
static void init_texture(gx_video_t *gx, unsigned width, unsigned height,
|
|
unsigned g_filter)
|
|
{
|
|
size_t fb_pitch;
|
|
unsigned fb_width, fb_height;
|
|
GXTexObj *fb_ptr = (GXTexObj*)&g_tex.obj;
|
|
GXTexObj *menu_ptr = (GXTexObj*)&menu_tex.obj;
|
|
|
|
if (!gx)
|
|
return;
|
|
|
|
width &= ~3;
|
|
height &= ~3;
|
|
|
|
gfx_display_get_fb_size(&fb_width, &fb_height,
|
|
&fb_pitch);
|
|
|
|
GX_InitTexObj(fb_ptr, g_tex.data, width, height,
|
|
(gx->rgb32) ? GX_TF_RGBA8 : gx->menu_texture_enable ?
|
|
GX_TF_RGB5A3 : GX_TF_RGB565,
|
|
GX_CLAMP, GX_CLAMP, GX_FALSE);
|
|
GX_InitTexObjFilterMode(fb_ptr, g_filter, g_filter);
|
|
GX_InitTexObj(menu_ptr, menu_tex.data, fb_width, fb_height,
|
|
GX_TF_RGB5A3, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
|
GX_InitTexObjFilterMode(menu_ptr, g_filter, g_filter);
|
|
GX_InvalidateTexAll();
|
|
}
|
|
|
|
static void init_vtx(gx_video_t *gx, const video_info_t *video,
|
|
bool video_smooth)
|
|
{
|
|
if (!gx || !video)
|
|
return;
|
|
|
|
Mtx44 m;
|
|
uint32_t level = 0;
|
|
_CPU_ISR_Disable(level);
|
|
referenceRetraceCount = retraceCount;
|
|
_CPU_ISR_Restore(level);
|
|
|
|
GX_SetCullMode(GX_CULL_NONE);
|
|
GX_SetClipMode(GX_CLIP_DISABLE);
|
|
GX_SetZMode(GX_ENABLE, GX_ALWAYS, GX_ENABLE);
|
|
GX_SetColorUpdate(GX_TRUE);
|
|
GX_SetAlphaUpdate(GX_FALSE);
|
|
|
|
guOrtho(m, 1, -1, -1, 1, 0.4, 0.6);
|
|
GX_LoadProjectionMtx(m, GX_ORTHOGRAPHIC);
|
|
|
|
GX_ClearVtxDesc();
|
|
GX_SetVtxDesc(GX_VA_POS, GX_INDEX8);
|
|
GX_SetVtxDesc(GX_VA_TEX0, GX_INDEX8);
|
|
GX_SetVtxDesc(GX_VA_CLR0, GX_INDEX8);
|
|
|
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
|
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
|
|
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
|
|
GX_SetArray(GX_VA_POS, verts, 3 * sizeof(float));
|
|
GX_SetArray(GX_VA_TEX0, vertex_ptr, 2 * sizeof(float));
|
|
GX_SetArray(GX_VA_CLR0, color_ptr, 4 * sizeof(u8));
|
|
|
|
GX_SetNumTexGens(1);
|
|
GX_SetNumChans(1);
|
|
GX_SetChanCtrl(GX_COLOR0A0, GX_DISABLE, GX_SRC_REG,
|
|
GX_SRC_VTX, GX_LIGHTNULL, GX_DF_NONE, GX_AF_NONE);
|
|
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
|
|
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
|
GX_InvVtxCache();
|
|
|
|
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA,
|
|
GX_BL_INVSRCALPHA, GX_LO_CLEAR);
|
|
|
|
if (gx->scale != video->input_scale || gx->rgb32 != video->rgb32)
|
|
{
|
|
RARCH_LOG("[GX]: Reallocate texture.\n");
|
|
free(g_tex.data);
|
|
g_tex.data = memalign(32,
|
|
RARCH_SCALE_BASE * RARCH_SCALE_BASE * video->input_scale *
|
|
video->input_scale * (video->rgb32 ? 4 : 2));
|
|
g_tex.width = g_tex.height = RARCH_SCALE_BASE * video->input_scale;
|
|
|
|
if (!g_tex.data)
|
|
{
|
|
RARCH_ERR("[GX]: Error allocating video texture\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
DCFlushRange(g_tex.data, (g_tex.width *
|
|
g_tex.height * video->rgb32) ? 4 : 2);
|
|
|
|
gx->rgb32 = video->rgb32;
|
|
gx->scale = video->input_scale;
|
|
gx->should_resize = true;
|
|
|
|
init_texture(gx, g_tex.width, g_tex.height,
|
|
video_smooth ? GX_LINEAR : GX_NEAR);
|
|
GX_Flush();
|
|
}
|
|
|
|
static void build_disp_list(void)
|
|
{
|
|
unsigned i;
|
|
|
|
DCInvalidateRange(display_list, sizeof(display_list));
|
|
GX_BeginDispList(display_list, sizeof(display_list));
|
|
GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 4);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
GX_Position1x8(i);
|
|
GX_Color1x8(i);
|
|
GX_TexCoord1x8(i);
|
|
}
|
|
GX_End();
|
|
display_list_size = GX_EndDispList();
|
|
}
|
|
|
|
#if 0
|
|
#define TAKE_EFB_SCREENSHOT_ON_EXIT
|
|
#endif
|
|
|
|
#ifdef TAKE_EFB_SCREENSHOT_ON_EXIT
|
|
|
|
/* Adapted from code by Crayon for GRRLIB (http://code.google.com/p/grrlib) */
|
|
static void gx_efb_screenshot(void)
|
|
{
|
|
int x, y;
|
|
uint8_t tga_header[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0xE0, 0x01, 0x18, 0x00};
|
|
intfstream_t *out = intfstream_open("/screenshot.tga",
|
|
RETRO_VFS_FILE_ACCESS_WRITE,
|
|
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
|
|
|
if (!out)
|
|
return;
|
|
|
|
intfstream_write(out, tga_header, sizeof(tga_header));
|
|
|
|
for (y = 479; y >= 0; --y)
|
|
{
|
|
uint8_t line[640 * 3];
|
|
unsigned i = 0;
|
|
|
|
for (x = 0; x < 640; x++)
|
|
{
|
|
GXColor color;
|
|
GX_PeekARGB(x, y, &color);
|
|
line[i++] = color.b;
|
|
line[i++] = color.g;
|
|
line[i++] = color.r;
|
|
}
|
|
intfstream_write(out, line, sizeof(line));
|
|
}
|
|
|
|
intfstream_close(out);
|
|
free(out);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void *gx_init(const video_info_t *video,
|
|
input_driver_t **input, void **input_data)
|
|
{
|
|
void *gxinput = NULL;
|
|
settings_t *settings = config_get_ptr();
|
|
gx_video_t *gx = (gx_video_t*)calloc(1, sizeof(gx_video_t));
|
|
bool video_smooth = settings->bools.video_smooth;
|
|
const char *input_joypad_driver = settings->arrays.input_joypad_driver;
|
|
if (!gx)
|
|
return NULL;
|
|
|
|
gxinput = input_driver_init_wrap(&input_gx, input_joypad_driver);
|
|
*input = gxinput ? &input_gx : NULL;
|
|
*input_data = gxinput;
|
|
|
|
VIDEO_Init();
|
|
GX_Init(gx_fifo, sizeof(gx_fifo));
|
|
gx->vsync = video->vsync;
|
|
|
|
setup_video_mode(gx);
|
|
init_vtx(gx, video, video_smooth);
|
|
build_disp_list();
|
|
|
|
gx->vp.full_width = gx_mode.fbWidth;
|
|
gx->vp.full_height = gx_mode.xfbHeight;
|
|
gx->should_resize = true;
|
|
gx->old_width = 0;
|
|
gx->old_height = 0;
|
|
|
|
gx->system_xOrigin = 0;
|
|
|
|
#ifdef HW_RVL
|
|
int8_t offset;
|
|
if (CONF_GetDisplayOffsetH(&offset) == 0)
|
|
gx->system_xOrigin = offset;
|
|
#else
|
|
syssram *sram = __SYS_LockSram();
|
|
gx->system_xOrigin = sram->display_offsetH;
|
|
__SYS_UnlockSram(0);
|
|
#endif
|
|
return gx;
|
|
}
|
|
|
|
static void update_texture_asm(const uint32_t *src, const uint32_t *dst,
|
|
unsigned width, unsigned height, unsigned pitch)
|
|
{
|
|
register uint32_t tmp0, tmp1, tmp2, tmp3, line2, line2b,
|
|
line3, line3b, line4, line4b, line5;
|
|
|
|
__asm__ volatile (
|
|
" srwi %[width], %[width], 2 \n"
|
|
" srwi %[height], %[height], 2 \n"
|
|
" subi %[tmp3], %[dst], 4 \n"
|
|
" mr %[dst], %[tmp3] \n"
|
|
" subi %[dst], %[dst], 4 \n"
|
|
" mr %[line2], %[pitch] \n"
|
|
" addi %[line2b], %[line2], 4 \n"
|
|
" mulli %[line3], %[pitch], 2 \n"
|
|
" addi %[line3b], %[line3], 4 \n"
|
|
" mulli %[line4], %[pitch], 3 \n"
|
|
" addi %[line4b], %[line4], 4 \n"
|
|
" mulli %[line5], %[pitch], 4 \n"
|
|
|
|
"2: mtctr %[width] \n"
|
|
" mr %[tmp0], %[src] \n"
|
|
|
|
"1: lwz %[tmp1], 0(%[src]) \n"
|
|
" stwu %[tmp1], 8(%[dst]) \n"
|
|
" lwz %[tmp2], 4(%[src]) \n"
|
|
" stwu %[tmp2], 8(%[tmp3]) \n"
|
|
|
|
" lwzx %[tmp1], %[line2], %[src] \n"
|
|
" stwu %[tmp1], 8(%[dst]) \n"
|
|
" lwzx %[tmp2], %[line2b], %[src] \n"
|
|
" stwu %[tmp2], 8(%[tmp3]) \n"
|
|
|
|
" lwzx %[tmp1], %[line3], %[src] \n"
|
|
" stwu %[tmp1], 8(%[dst]) \n"
|
|
" lwzx %[tmp2], %[line3b], %[src] \n"
|
|
" stwu %[tmp2], 8(%[tmp3]) \n"
|
|
|
|
" lwzx %[tmp1], %[line4], %[src] \n"
|
|
" stwu %[tmp1], 8(%[dst]) \n"
|
|
" lwzx %[tmp2], %[line4b], %[src] \n"
|
|
" stwu %[tmp2], 8(%[tmp3]) \n"
|
|
|
|
" addi %[src], %[src], 8 \n"
|
|
" bdnz 1b \n"
|
|
|
|
" add %[src], %[tmp0], %[line5] \n"
|
|
" subic. %[height], %[height], 1 \n"
|
|
" bne 2b \n"
|
|
: [tmp0] "=&b" (tmp0),
|
|
[tmp1] "=&b" (tmp1),
|
|
[tmp2] "=&b" (tmp2),
|
|
[tmp3] "=&b" (tmp3),
|
|
[line2] "=&b" (line2),
|
|
[line2b] "=&b" (line2b),
|
|
[line3] "=&b" (line3),
|
|
[line3b] "=&b" (line3b),
|
|
[line4] "=&b" (line4),
|
|
[line4b] "=&b" (line4b),
|
|
[line5] "=&b" (line5),
|
|
[dst] "+&b" (dst)
|
|
: [src] "b" (src),
|
|
[width] "b" (width),
|
|
[height] "b" (height),
|
|
[pitch] "b" (pitch)
|
|
: "cc"
|
|
);
|
|
}
|
|
|
|
static void convert_texture16(const uint32_t *_src, uint32_t *_dst,
|
|
unsigned width, unsigned height, unsigned pitch)
|
|
{
|
|
width &= ~3;
|
|
height &= ~3;
|
|
update_texture_asm(_src, _dst, width, height, pitch);
|
|
}
|
|
|
|
static void convert_texture16_conv(const uint32_t *_src, uint32_t *_dst,
|
|
unsigned width, unsigned height, unsigned pitch)
|
|
{
|
|
unsigned i, tmp_pitch, width2;
|
|
const uint32_t *src = (const uint32_t*)_src;
|
|
uint32_t *dst = (uint32_t*)_dst;
|
|
|
|
width &= ~3;
|
|
height &= ~3;
|
|
tmp_pitch = pitch >> 2;
|
|
width2 = width >> 1;
|
|
|
|
for (i = 0; i < height; i += 4, dst += 4 * width2)
|
|
{
|
|
#define BLIT_LINE_16_CONV(x) (0x80008000 | (((x) & 0xFFC0FFC0) >> 1) | ((x) & 0x001F001F))
|
|
BLIT_LINE_16(0)
|
|
BLIT_LINE_16(2)
|
|
BLIT_LINE_16(4)
|
|
BLIT_LINE_16(6)
|
|
#undef BLIT_LINE_16_CONV
|
|
}
|
|
}
|
|
|
|
static void convert_texture32(const uint32_t *_src, uint32_t *_dst,
|
|
unsigned width, unsigned height, unsigned pitch)
|
|
{
|
|
unsigned i, tmp_pitch, width2;
|
|
const uint16_t *src = (uint16_t *) _src;
|
|
uint16_t *dst = (uint16_t *) _dst;
|
|
|
|
width &= ~3;
|
|
height &= ~3;
|
|
tmp_pitch = pitch >> 1;
|
|
width2 = width << 1;
|
|
|
|
for (i = 0; i < height; i += 4, dst += 4 * width2)
|
|
{
|
|
BLIT_LINE_32(0)
|
|
BLIT_LINE_32(4)
|
|
BLIT_LINE_32(8)
|
|
BLIT_LINE_32(12)
|
|
}
|
|
}
|
|
|
|
static void gx_resize(gx_video_t *gx,
|
|
bool video_smooth,
|
|
unsigned aspect_ratio_idx,
|
|
unsigned overscan_corr_top,
|
|
unsigned overscan_corr_bottom)
|
|
{
|
|
int gamma;
|
|
unsigned degrees;
|
|
Mtx44 m1, m2;
|
|
float top = 1, bottom = -1, left = -1, right = 1;
|
|
int x = 0, y = 0;
|
|
const global_t *global = global_get_ptr();
|
|
unsigned width = gx->vp.full_width;
|
|
unsigned height = gx->vp.full_height;
|
|
|
|
if (!gx)
|
|
return;
|
|
|
|
#ifdef HW_RVL
|
|
VIDEO_SetTrapFilter(global->console.softfilter_enable);
|
|
gamma = global->console.screen.gamma_correction;
|
|
if (gamma == 0)
|
|
gamma = 10; /* default 1.0 gamma value */
|
|
VIDEO_SetGamma(gamma);
|
|
#else
|
|
gamma = global->console.screen.gamma_correction;
|
|
GX_SetDispCopyGamma(MAX(0,MIN(2,gamma)));
|
|
#endif
|
|
|
|
/* Ignore this for custom resolutions */
|
|
if (gx->keep_aspect && gx_mode.efbHeight >= 192)
|
|
{
|
|
float desired_aspect = video_driver_get_aspect_ratio();
|
|
if (desired_aspect == 0.0)
|
|
desired_aspect = 1.0;
|
|
if (gx->orientation == ORIENTATION_VERTICAL ||
|
|
gx->orientation == ORIENTATION_FLIPPED_ROTATED)
|
|
desired_aspect = 1.0 / desired_aspect;
|
|
|
|
if (aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
|
|
{
|
|
struct video_viewport *custom_vp = video_viewport_get_custom();
|
|
|
|
if (!custom_vp->width || !custom_vp->height)
|
|
{
|
|
custom_vp->x = 0;
|
|
custom_vp->y = 0;
|
|
custom_vp->width = gx->vp.full_width;
|
|
custom_vp->height = gx->vp.full_height;
|
|
}
|
|
|
|
x = custom_vp->x;
|
|
y = custom_vp->y;
|
|
width = custom_vp->width;
|
|
height = custom_vp->height;
|
|
}
|
|
else
|
|
{
|
|
float delta;
|
|
#ifdef HW_RVL
|
|
float device_aspect = CONF_GetAspectRatio() == CONF_ASPECT_4_3 ?
|
|
4.0 / 3.0 : 16.0 / 9.0;
|
|
#else
|
|
float device_aspect = 4.0 / 3.0;
|
|
#endif
|
|
if (fabs(device_aspect - desired_aspect) < 0.0001)
|
|
{
|
|
/* If the aspect ratios of screen and desired aspect ratio
|
|
* are sufficiently equal (floating point stuff),
|
|
* assume they are actually equal. */
|
|
}
|
|
else if (device_aspect > desired_aspect)
|
|
{
|
|
delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
|
|
x = (unsigned)(width * (0.5 - delta));
|
|
width = (unsigned)(2.0 * width * delta);
|
|
}
|
|
else
|
|
{
|
|
delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
|
|
y = (unsigned)(height * (0.5 - delta));
|
|
height = (unsigned)(2.0 * height * delta);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Overscan correction */
|
|
if ((overscan_corr_top > 0) ||
|
|
(overscan_corr_bottom > 0))
|
|
{
|
|
float current_aspect = (float)width / (float)height;
|
|
int new_height = height -
|
|
(overscan_corr_top +
|
|
overscan_corr_bottom);
|
|
int new_width = (int)((new_height * current_aspect) + 0.5f);
|
|
|
|
if ((new_height > 0) && (new_width > 0))
|
|
{
|
|
x += (int)((float)(width - new_width) * 0.5f);
|
|
y += (int)overscan_corr_top;
|
|
width = (unsigned)new_width;
|
|
height = (unsigned)new_height;
|
|
}
|
|
}
|
|
|
|
if (gx_is_valid_xorigin(gx, gx->xOrigin + x))
|
|
{
|
|
gx_mode.viXOrigin = gx->xOrigin + x;
|
|
x = 0;
|
|
}
|
|
else if (x < 0)
|
|
{
|
|
gx_mode.viXOrigin = gx->xOrigin + gx->xOriginNeg;
|
|
x -= gx->xOriginNeg;
|
|
}
|
|
else if (x > 0)
|
|
{
|
|
gx_mode.viXOrigin = gx->xOrigin + gx->xOriginPos;
|
|
x -= gx->xOriginPos;
|
|
}
|
|
|
|
if (gx_is_valid_yorigin(gx->yOrigin + y))
|
|
{
|
|
gx_mode.viYOrigin = gx->yOrigin + y;
|
|
y = 0;
|
|
}
|
|
else if (y < 0)
|
|
{
|
|
gx_mode.viYOrigin = gx->yOrigin + gx->yOriginNeg;
|
|
y -= gx->yOriginNeg;
|
|
}
|
|
else if (y > 0)
|
|
{
|
|
gx_mode.viYOrigin = gx->yOrigin + gx->yOriginPos;
|
|
y -= gx->yOriginPos;
|
|
}
|
|
|
|
VIDEO_Configure(&gx_mode);
|
|
|
|
gx->vp.x = x;
|
|
gx->vp.y = y;
|
|
gx->vp.width = width;
|
|
gx->vp.height = height;
|
|
|
|
GX_SetViewportJitter(x, y, width, height, 0, 1, 1);
|
|
|
|
guOrtho(m1, top, bottom, left, right, 0, 1);
|
|
GX_LoadPosMtxImm(m1, GX_PNMTX1);
|
|
|
|
switch(gx->orientation)
|
|
{
|
|
case ORIENTATION_VERTICAL:
|
|
degrees = 90;
|
|
break;
|
|
case ORIENTATION_FLIPPED:
|
|
degrees = 180;
|
|
break;
|
|
case ORIENTATION_FLIPPED_ROTATED:
|
|
degrees = 270;
|
|
break;
|
|
default:
|
|
degrees = 0;
|
|
break;
|
|
}
|
|
guMtxIdentity(m2);
|
|
guMtxRotDeg(m2, 'Z', degrees);
|
|
c_guMtxConcat(m1, m2, m1);
|
|
GX_LoadPosMtxImm(m1, GX_PNMTX0);
|
|
|
|
init_texture(gx, 4, 4,
|
|
video_smooth ? GX_LINEAR : GX_NEAR);
|
|
gx->old_width = 0;
|
|
gx->old_height = 0;
|
|
gx->should_resize = false;
|
|
}
|
|
|
|
static void gx_blit_line(gx_video_t *gx,
|
|
unsigned x, unsigned y, const char *message)
|
|
{
|
|
unsigned width, height, h;
|
|
bool double_width = false;
|
|
|
|
if (!gx)
|
|
return;
|
|
|
|
const GXColor b = {
|
|
.r = 0x00,
|
|
.g = 0x00,
|
|
.b = 0x00,
|
|
.a = 0xff
|
|
};
|
|
const GXColor w = {
|
|
.r = 0xff,
|
|
.g = 0xff,
|
|
.b = 0xff,
|
|
.a = 0xff
|
|
};
|
|
|
|
if (!*message)
|
|
return;
|
|
|
|
double_width = gx_mode.fbWidth > 400;
|
|
width = (double_width ? 2 : 1);
|
|
height = FONT_HEIGHT * (gx->double_strike ? 1 : 2);
|
|
|
|
for (h = 0; h < height; h++)
|
|
{
|
|
GX_PokeARGB(x, y + h, b);
|
|
if (double_width)
|
|
{
|
|
GX_PokeARGB(x + 1, y + h, b);
|
|
}
|
|
}
|
|
|
|
x += (double_width ? 2 : 1);
|
|
|
|
while (*message)
|
|
{
|
|
unsigned i, j;
|
|
for (j = 0; j < FONT_HEIGHT; j++)
|
|
{
|
|
for (i = 0; i < FONT_WIDTH; i++)
|
|
{
|
|
uint8_t rem = 1 << ((i + j * FONT_WIDTH) & 7);
|
|
unsigned offset = (i + j * FONT_WIDTH) >> 3;
|
|
bool col =
|
|
(bitmap_bin[FONT_OFFSET((unsigned char)*message) + offset]
|
|
& rem);
|
|
GXColor c = b;
|
|
|
|
if (col)
|
|
c = w;
|
|
|
|
if (!gx->double_strike)
|
|
{
|
|
GX_PokeARGB(x + (i * width), y + (j * 2), c);
|
|
if (double_width)
|
|
{
|
|
GX_PokeARGB(x + (i * width) + 1, y + (j * 2), c);
|
|
GX_PokeARGB(x + (i * width) + 1, y + (j * 2) + 1, c);
|
|
}
|
|
GX_PokeARGB(x + (i * width), y + (j * 2) + 1, c);
|
|
}
|
|
else
|
|
{
|
|
GX_PokeARGB(x + (i * width), y + j, c);
|
|
if (double_width)
|
|
{
|
|
GX_PokeARGB(x + (i * width) + 1, y + j, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (h = 0; h < height; h++)
|
|
{
|
|
GX_PokeARGB(x + (FONT_WIDTH * width), y + h, b);
|
|
if (double_width)
|
|
{
|
|
GX_PokeARGB(x + (FONT_WIDTH * width) + 1, y + h, b);
|
|
}
|
|
}
|
|
|
|
x += FONT_WIDTH_STRIDE * (double_width ? 2 : 1);
|
|
message++;
|
|
}
|
|
}
|
|
|
|
static void gx_set_nonblock_state(void *data, bool state,
|
|
bool adaptive_vsync_enabled, unsigned swap_interval)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
if (!gx)
|
|
return;
|
|
gx->vsync = !state;
|
|
}
|
|
|
|
static bool gx_alive(void *data)
|
|
{
|
|
(void)data;
|
|
return true;
|
|
}
|
|
|
|
static bool gx_focus(void *data)
|
|
{
|
|
(void)data;
|
|
return true;
|
|
}
|
|
|
|
static bool gx_suppress_screensaver(void *data, bool enable)
|
|
{
|
|
(void)data;
|
|
(void)enable;
|
|
|
|
return false;
|
|
}
|
|
|
|
static void gx_set_rotation(void *data, unsigned orientation)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
if (!gx)
|
|
return;
|
|
|
|
gx->orientation = orientation;
|
|
gx->should_resize = true;
|
|
}
|
|
|
|
static void gx_set_texture_frame(void *data, const void *frame,
|
|
bool rgb32, unsigned width, unsigned height, float alpha)
|
|
{
|
|
(void)rgb32;
|
|
(void)width;
|
|
(void)height;
|
|
(void)alpha;
|
|
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
if (gx)
|
|
gx->menu_data = (uint32_t*)frame;
|
|
}
|
|
|
|
static void gx_set_texture_enable(void *data, bool enable, bool full_screen)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
(void)full_screen;
|
|
|
|
if (!gx)
|
|
return;
|
|
|
|
gx->menu_texture_enable = enable;
|
|
/* need to make sure the game texture is the right pixel
|
|
* format for menu overlay. */
|
|
gx->should_resize = true;
|
|
}
|
|
|
|
static void gx_apply_state_changes(void *data)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
if (gx)
|
|
gx->should_resize = true;
|
|
}
|
|
|
|
static void gx_viewport_info(void *data, struct video_viewport *vp)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
if (gx)
|
|
*vp = gx->vp;
|
|
}
|
|
|
|
static void gx_get_video_output_prev(void *data)
|
|
{
|
|
global_t *global = global_get_ptr();
|
|
|
|
if (global->console.screen.resolutions.current.id == 0)
|
|
{
|
|
global->console.screen.resolutions.current.id = GX_RESOLUTIONS_LAST;
|
|
return;
|
|
}
|
|
|
|
global->console.screen.resolutions.current.id--;
|
|
}
|
|
|
|
static void gx_get_video_output_next(void *data)
|
|
{
|
|
global_t *global = global_get_ptr();
|
|
if (!global)
|
|
return;
|
|
|
|
if (global->console.screen.resolutions.current.id >= GX_RESOLUTIONS_LAST)
|
|
{
|
|
global->console.screen.resolutions.current.id = 0;
|
|
return;
|
|
}
|
|
|
|
global->console.screen.resolutions.current.id++;
|
|
}
|
|
|
|
static uint32_t gx_get_flags(void *data)
|
|
{
|
|
uint32_t flags = 0;
|
|
|
|
BIT32_SET(flags, GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED);
|
|
|
|
return flags;
|
|
}
|
|
|
|
static const video_poke_interface_t gx_poke_interface = {
|
|
gx_get_flags,
|
|
NULL,
|
|
NULL,
|
|
gx_set_video_mode,
|
|
NULL, /* get_refresh_rate */
|
|
NULL,
|
|
gx_get_video_output_size,
|
|
gx_get_video_output_prev,
|
|
gx_get_video_output_next,
|
|
NULL,
|
|
NULL,
|
|
gx_set_aspect_ratio,
|
|
gx_apply_state_changes,
|
|
gx_set_texture_frame,
|
|
gx_set_texture_enable,
|
|
NULL, /* set_osd_msg */
|
|
NULL, /* show_mouse */
|
|
NULL, /* grab_mouse_toggle */
|
|
NULL, /* get_current_shader */
|
|
NULL, /* get_current_software_framebuffer */
|
|
NULL, /* get_hw_render_interface */
|
|
NULL, /* set_hdr_max_nits */
|
|
NULL, /* set_hdr_paper_white_nits */
|
|
NULL, /* set_hdr_contrast */
|
|
NULL /* set_hdr_expand_gamut */
|
|
};
|
|
|
|
static void gx_get_poke_interface(void *data,
|
|
const video_poke_interface_t **iface)
|
|
{
|
|
(void)data;
|
|
*iface = &gx_poke_interface;
|
|
}
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
static void gx_overlay_tex_geom(void *data, unsigned image,
|
|
float x, float y, float w, float h)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
struct gx_overlay_data *o = NULL;
|
|
|
|
if (gx)
|
|
o = (struct gx_overlay_data*)&gx->overlay[image];
|
|
|
|
if (!o)
|
|
return;
|
|
|
|
o->tex_coord[0] = x;
|
|
o->tex_coord[1] = y;
|
|
o->tex_coord[2] = x + w;
|
|
o->tex_coord[3] = y;
|
|
o->tex_coord[4] = x;
|
|
o->tex_coord[5] = y + h;
|
|
o->tex_coord[6] = x + w;
|
|
o->tex_coord[7] = y + h;
|
|
}
|
|
|
|
static void gx_overlay_vertex_geom(void *data, unsigned image,
|
|
float x, float y, float w, float h)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
struct gx_overlay_data *o = NULL;
|
|
|
|
/* Flipped, so we preserve top-down semantics. */
|
|
y = 1.0f - y;
|
|
h = -h;
|
|
|
|
/* expand from 0 - 1 to -1 - 1 */
|
|
x = (x * 2.0f) - 1.0f;
|
|
y = (y * 2.0f) - 1.0f;
|
|
w = (w * 2.0f);
|
|
h = (h * 2.0f);
|
|
|
|
if (gx)
|
|
o = (struct gx_overlay_data*)&gx->overlay[image];
|
|
|
|
if (!o)
|
|
return;
|
|
|
|
o->vertex_coord[0] = x;
|
|
o->vertex_coord[1] = y;
|
|
o->vertex_coord[2] = x + w;
|
|
o->vertex_coord[3] = y;
|
|
o->vertex_coord[4] = x;
|
|
o->vertex_coord[5] = y + h;
|
|
o->vertex_coord[6] = x + w;
|
|
o->vertex_coord[7] = y + h;
|
|
}
|
|
|
|
static void gx_free_overlay(gx_video_t *gx)
|
|
{
|
|
if (gx)
|
|
{
|
|
free(gx->overlay);
|
|
gx->overlay = NULL;
|
|
gx->overlays = 0;
|
|
}
|
|
GX_InvalidateTexAll();
|
|
}
|
|
|
|
static bool gx_overlay_load(void *data,
|
|
const void *image_data, unsigned num_images)
|
|
{
|
|
unsigned i;
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
if (!gx)
|
|
return false;
|
|
const struct texture_image *images = (const struct texture_image*)image_data;
|
|
|
|
gx_free_overlay(gx);
|
|
gx->overlay = (struct gx_overlay_data*)calloc(num_images, sizeof(*gx->overlay));
|
|
if (!gx->overlay)
|
|
return false;
|
|
|
|
gx->overlays = num_images;
|
|
|
|
for (i = 0; i < num_images; i++)
|
|
{
|
|
struct gx_overlay_data *o = (struct gx_overlay_data*)&gx->overlay[i];
|
|
|
|
GX_InitTexObj(&o->tex, images[i].pixels, images[i].width,
|
|
images[i].height,
|
|
GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE);
|
|
GX_InitTexObjFilterMode(&g_tex.obj, GX_LINEAR, GX_LINEAR);
|
|
DCFlushRange(images[i].pixels, images[i].width *
|
|
images[i].height * sizeof(uint32_t));
|
|
|
|
/* Default. Stretch to whole screen. */
|
|
gx_overlay_tex_geom(gx, i, 0, 0, 1, 1);
|
|
gx_overlay_vertex_geom(gx, i, 0, 0, 1, 1);
|
|
gx->overlay[i].alpha_mod = 1.0f;
|
|
}
|
|
|
|
GX_InvalidateTexAll();
|
|
return true;
|
|
}
|
|
|
|
static void gx_overlay_enable(void *data, bool state)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
if (gx)
|
|
gx->overlay_enable = state;
|
|
}
|
|
|
|
static void gx_overlay_full_screen(void *data, bool enable)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
if (gx)
|
|
gx->overlay_full_screen = enable;
|
|
}
|
|
|
|
static void gx_overlay_set_alpha(void *data, unsigned image, float mod)
|
|
{
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
if (gx)
|
|
gx->overlay[image].alpha_mod = mod;
|
|
}
|
|
|
|
static void gx_render_overlay(void *data)
|
|
{
|
|
unsigned i;
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
if (!gx)
|
|
return;
|
|
|
|
GX_SetCurrentMtx(GX_PNMTX1);
|
|
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
|
|
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
|
|
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
|
|
|
|
for (i = 0; i < gx->overlays; i++)
|
|
{
|
|
GX_LoadTexObj(&gx->overlay[i].tex, GX_TEXMAP0);
|
|
|
|
GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 4);
|
|
GX_Position3f32(gx->overlay[i].vertex_coord[0],
|
|
gx->overlay[i].vertex_coord[1], -0.5);
|
|
GX_Color4u8(255, 255, 255, (u8)(gx->overlay[i].alpha_mod * 255.0f));
|
|
GX_TexCoord2f32(gx->overlay[i].tex_coord[0],
|
|
gx->overlay[i].tex_coord[1]);
|
|
|
|
GX_Position3f32(gx->overlay[i].vertex_coord[2],
|
|
gx->overlay[i].vertex_coord[3], -0.5);
|
|
GX_Color4u8(255, 255, 255, (u8)(gx->overlay[i].alpha_mod * 255.0f));
|
|
GX_TexCoord2f32(gx->overlay[i].tex_coord[2],
|
|
gx->overlay[i].tex_coord[3]);
|
|
|
|
GX_Position3f32(gx->overlay[i].vertex_coord[4],
|
|
gx->overlay[i].vertex_coord[5], -0.5);
|
|
GX_Color4u8(255, 255, 255, (u8)(gx->overlay[i].alpha_mod * 255.0f));
|
|
GX_TexCoord2f32(gx->overlay[i].tex_coord[4],
|
|
gx->overlay[i].tex_coord[5]);
|
|
|
|
GX_Position3f32(gx->overlay[i].vertex_coord[6],
|
|
gx->overlay[i].vertex_coord[7], -0.5);
|
|
GX_Color4u8(255, 255, 255, (u8)(gx->overlay[i].alpha_mod * 255.0f));
|
|
GX_TexCoord2f32(gx->overlay[i].tex_coord[6],
|
|
gx->overlay[i].tex_coord[7]);
|
|
GX_End();
|
|
}
|
|
|
|
GX_SetVtxDesc(GX_VA_POS, GX_INDEX8);
|
|
GX_SetVtxDesc(GX_VA_TEX0, GX_INDEX8);
|
|
GX_SetVtxDesc(GX_VA_CLR0, GX_INDEX8);
|
|
}
|
|
|
|
static const video_overlay_interface_t gx_overlay_interface = {
|
|
gx_overlay_enable,
|
|
gx_overlay_load,
|
|
gx_overlay_tex_geom,
|
|
gx_overlay_vertex_geom,
|
|
gx_overlay_full_screen,
|
|
gx_overlay_set_alpha,
|
|
};
|
|
|
|
static void gx_get_overlay_interface(void *data,
|
|
const video_overlay_interface_t **iface)
|
|
{
|
|
(void)data;
|
|
*iface = &gx_overlay_interface;
|
|
}
|
|
#endif
|
|
|
|
static void gx_free(void *data)
|
|
{
|
|
#ifdef HAVE_OVERLAY
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
|
|
gx_free_overlay(gx);
|
|
#endif
|
|
|
|
GX_DrawDone();
|
|
GX_AbortFrame();
|
|
GX_Flush();
|
|
VIDEO_SetBlack(true);
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
|
|
if (g_video_cond)
|
|
OSCloseThreadQueue(g_video_cond);
|
|
g_video_cond = 0;
|
|
|
|
free(data);
|
|
}
|
|
|
|
static bool gx_frame(void *data, const void *frame,
|
|
unsigned width, unsigned height,
|
|
uint64_t frame_count, unsigned pitch,
|
|
const char *msg,
|
|
video_frame_info_t *video_info)
|
|
{
|
|
char fps_text_buf[128];
|
|
settings_t *settings = config_get_ptr();
|
|
gx_video_t *gx = (gx_video_t*)data;
|
|
u8 clear_efb = GX_FALSE;
|
|
uint32_t level = 0;
|
|
unsigned overscan_corr_top = settings->uints.video_overscan_correction_top;
|
|
unsigned overscan_corr_bottom = settings->uints.video_overscan_correction_bottom;
|
|
bool video_smooth = settings->bools.video_smooth;
|
|
unsigned video_aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
|
|
#ifdef HAVE_MENU
|
|
bool menu_is_alive = video_info->menu_is_alive;
|
|
#endif
|
|
bool fps_show = video_info->fps_show;
|
|
|
|
fps_text_buf[0] = '\0';
|
|
|
|
if (!gx || (!frame && !gx->menu_texture_enable))
|
|
return true;
|
|
|
|
if (!frame)
|
|
width = height = 4; /* draw a black square in the background */
|
|
|
|
if ((gx->overscan_correction_top != overscan_corr_top) ||
|
|
(gx->overscan_correction_bottom != overscan_corr_bottom))
|
|
{
|
|
gx->overscan_correction_top = overscan_corr_top;
|
|
gx->overscan_correction_bottom = overscan_corr_bottom;
|
|
gx->should_resize = true;
|
|
}
|
|
|
|
if (gx->should_resize)
|
|
{
|
|
gx_resize(gx,
|
|
video_smooth,
|
|
video_aspect_ratio_idx,
|
|
overscan_corr_top,
|
|
overscan_corr_bottom);
|
|
clear_efb = GX_TRUE;
|
|
}
|
|
|
|
while (((gx->vsync || gx->menu_texture_enable)) && !g_draw_done)
|
|
OSSleepThread(g_video_cond);
|
|
|
|
width = MIN(g_tex.width, width);
|
|
height = MIN(g_tex.height, height);
|
|
|
|
if (width != gx->old_width || height != gx->old_height)
|
|
{
|
|
init_texture(gx, width, height,
|
|
video_smooth ? GX_LINEAR : GX_NEAR);
|
|
gx->old_width = width;
|
|
gx->old_height = height;
|
|
}
|
|
|
|
g_draw_done = false;
|
|
gx->current_framebuf ^= 1;
|
|
|
|
if (frame)
|
|
{
|
|
if (gx->rgb32)
|
|
convert_texture32(frame, g_tex.data, width, height, pitch);
|
|
else if (gx->menu_texture_enable)
|
|
convert_texture16_conv(frame, g_tex.data, width, height, pitch);
|
|
else
|
|
convert_texture16(frame, g_tex.data, width, height, pitch);
|
|
DCFlushRange(g_tex.data, height * (width << (gx->rgb32 ? 2 : 1)));
|
|
}
|
|
|
|
if (gx->menu_texture_enable && gx->menu_data)
|
|
{
|
|
size_t fb_pitch;
|
|
unsigned fb_width, fb_height;
|
|
|
|
gfx_display_get_fb_size(&fb_width, &fb_height,
|
|
&fb_pitch);
|
|
|
|
convert_texture16(
|
|
gx->menu_data,
|
|
menu_tex.data,
|
|
fb_width,
|
|
fb_height,
|
|
fb_pitch);
|
|
DCFlushRange(
|
|
menu_tex.data,
|
|
fb_width * fb_pitch);
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
menu_driver_frame(menu_is_alive, video_info);
|
|
#endif
|
|
|
|
GX_InvalidateTexAll();
|
|
|
|
GX_SetCurrentMtx(GX_PNMTX0);
|
|
GX_LoadTexObj(&g_tex.obj, GX_TEXMAP0);
|
|
GX_CallDispList(display_list, display_list_size);
|
|
|
|
if (gx->menu_texture_enable)
|
|
{
|
|
GX_SetCurrentMtx(GX_PNMTX1);
|
|
GX_LoadTexObj(&menu_tex.obj, GX_TEXMAP0);
|
|
GX_CallDispList(display_list, display_list_size);
|
|
}
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
if (gx->overlay_enable)
|
|
gx_render_overlay(gx);
|
|
#endif
|
|
|
|
_CPU_ISR_Disable(level);
|
|
if (referenceRetraceCount > retraceCount)
|
|
{
|
|
if (gx->vsync)
|
|
VIDEO_WaitVSync();
|
|
}
|
|
|
|
referenceRetraceCount = retraceCount;
|
|
_CPU_ISR_Restore(level);
|
|
|
|
GX_DrawDone();
|
|
|
|
if (fps_show)
|
|
{
|
|
char mem1_txt[128];
|
|
char mem2_txt[128];
|
|
unsigned x = 15;
|
|
unsigned y = 35;
|
|
|
|
mem1_txt[0] = mem2_txt[0] = '\0';
|
|
|
|
(void)mem2_txt;
|
|
|
|
gx_blit_line(gx, x, y, fps_text_buf);
|
|
y += FONT_HEIGHT * (gx->double_strike ? 1 : 2);
|
|
snprintf(mem1_txt, sizeof(mem1_txt), "MEM1: %8d / %8d",
|
|
SYSMEM1_SIZE - SYS_GetArena1Size(), SYSMEM1_SIZE);
|
|
gx_blit_line(gx, x, y, mem1_txt);
|
|
#ifdef HW_RVL
|
|
y += FONT_HEIGHT * (gx->double_strike ? 1 : 2);
|
|
snprintf(mem2_txt, sizeof(mem2_txt), "MEM2: %8d / %8d",
|
|
gx_mem2_used(), gx_mem2_total());
|
|
gx_blit_line(gx, x, y, mem2_txt);
|
|
#endif
|
|
}
|
|
|
|
if (msg && !gx->menu_texture_enable)
|
|
{
|
|
unsigned x = 7 * (gx->double_strike ? 1 : 2);
|
|
unsigned y = gx->vp.full_height - (35 * (gx->double_strike ? 1 : 2));
|
|
|
|
gx_blit_line(gx, x, y, msg);
|
|
clear_efb = GX_TRUE;
|
|
}
|
|
|
|
GX_CopyDisp(gx->framebuf[gx->current_framebuf], clear_efb);
|
|
GX_Flush();
|
|
VIDEO_SetNextFramebuffer(gx->framebuf[gx->current_framebuf]);
|
|
VIDEO_Flush();
|
|
|
|
_CPU_ISR_Disable(level);
|
|
++referenceRetraceCount;
|
|
_CPU_ISR_Restore(level);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool gx_set_shader(void *data,
|
|
enum rarch_shader_type type, const char *path)
|
|
{
|
|
(void)data;
|
|
(void)type;
|
|
(void)path;
|
|
|
|
return false;
|
|
}
|
|
|
|
video_driver_t video_gx = {
|
|
gx_init,
|
|
gx_frame,
|
|
gx_set_nonblock_state,
|
|
gx_alive,
|
|
gx_focus,
|
|
gx_suppress_screensaver,
|
|
NULL, /* has_windowed */
|
|
gx_set_shader,
|
|
gx_free,
|
|
"gx",
|
|
NULL, /* set_viewport */
|
|
gx_set_rotation,
|
|
gx_viewport_info,
|
|
NULL, /* read_viewport */
|
|
NULL, /* read_frame_raw */
|
|
#ifdef HAVE_OVERLAY
|
|
gx_get_overlay_interface,
|
|
#endif
|
|
gx_get_poke_interface,
|
|
};
|