mirror of https://github.com/mgba-emu/mgba.git
3DS: Implement 3x sharp bilinear scaling and make it the default
- Old method was 2x. 3x looks quite a bit sharper in aspect-fit mode on non-O2DS as the width is an integer number of half-width pixels. Since resulting upscaling ratio is 3x:1.5x, this gives very good results, althought it might be too sharp for your liking (YMMV). Not as much of a difference in screen-fit mode - Remove duplicate rendertarget as it was not necessary
This commit is contained in:
parent
c541a79e95
commit
4ee633925c
|
@ -47,8 +47,9 @@ static enum FilterMode {
|
|||
FM_NEAREST,
|
||||
FM_LINEAR_1x,
|
||||
FM_LINEAR_2x,
|
||||
FM_LINEAR_3x,
|
||||
FM_MAX
|
||||
} filterMode = FM_LINEAR_2x;
|
||||
} filterMode = FM_LINEAR_3x;
|
||||
|
||||
static enum DarkenMode {
|
||||
DM_NATIVE,
|
||||
|
@ -97,9 +98,8 @@ static int bufferId = 0;
|
|||
static bool frameLimiter = true;
|
||||
static u32 frameCounter;
|
||||
|
||||
static C3D_RenderTarget* topScreen[2];
|
||||
static C3D_RenderTarget* bottomScreen[2];
|
||||
static int doubleBuffer = 0;
|
||||
static C3D_RenderTarget* topScreen;
|
||||
static C3D_RenderTarget* bottomScreen;
|
||||
static bool frameStarted = false;
|
||||
|
||||
static C3D_RenderTarget* upscaleBuffer;
|
||||
|
@ -115,28 +115,26 @@ static bool _initGpu(void) {
|
|||
}
|
||||
|
||||
if (gfxIsWide()) {
|
||||
topScreen[0] = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0);
|
||||
topScreen[1] = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0);
|
||||
topScreen = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0);
|
||||
} else {
|
||||
topScreen[0] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0);
|
||||
topScreen[1] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0);
|
||||
topScreen = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0);
|
||||
}
|
||||
bottomScreen[0] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0);
|
||||
bottomScreen[1] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0);
|
||||
if (!topScreen[0] || !topScreen[1] || !bottomScreen[0] || !bottomScreen[1]) {
|
||||
bottomScreen = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0);
|
||||
if (!topScreen || !bottomScreen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
C3D_FrameBegin(0);
|
||||
C3D_FrameDrawOn(bottomScreen[0]);
|
||||
C3D_RenderTargetClear(bottomScreen[0], C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_FrameDrawOn(topScreen[0]);
|
||||
C3D_RenderTargetClear(topScreen[0], C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_RenderTargetSetOutput(topScreen[0], GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_RenderTargetSetOutput(bottomScreen[0], GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_FrameDrawOn(bottomScreen);
|
||||
C3D_RenderTargetClear(bottomScreen, C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_FrameDrawOn(topScreen);
|
||||
C3D_RenderTargetClear(topScreen, C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_RenderTargetSetOutput(topScreen, GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_RenderTargetSetOutput(bottomScreen, GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_FrameEnd(0);
|
||||
|
||||
if (!C3D_TexInitVRAM(&upscaleBufferTex, 512, 512, GPU_RGB8)) {
|
||||
if (!C3D_TexInitVRAM(&upscaleBufferTex, 1024, 512, GPU_RGB8)) {
|
||||
__builtin_trap();
|
||||
return false;
|
||||
}
|
||||
upscaleBuffer = C3D_RenderTargetCreateFromTex(&upscaleBufferTex, GPU_TEXFACE_2D, 0, 0);
|
||||
|
@ -164,10 +162,8 @@ static void _cleanup(void) {
|
|||
screenshotBuffer = NULL;
|
||||
}
|
||||
|
||||
C3D_RenderTargetDelete(topScreen[0]);
|
||||
C3D_RenderTargetDelete(topScreen[1]);
|
||||
C3D_RenderTargetDelete(bottomScreen[0]);
|
||||
C3D_RenderTargetDelete(bottomScreen[1]);
|
||||
C3D_RenderTargetDelete(topScreen);
|
||||
C3D_RenderTargetDelete(bottomScreen);
|
||||
C3D_RenderTargetDelete(upscaleBuffer);
|
||||
C3D_TexDelete(&upscaleBufferTex);
|
||||
C3D_TexDelete(&outputTexture[0]);
|
||||
|
@ -216,10 +212,10 @@ static void _drawStart(void) {
|
|||
C3D_FrameBegin(0);
|
||||
ctrStartFrame();
|
||||
|
||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
||||
C3D_RenderTargetClear(bottomScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
||||
C3D_RenderTargetClear(topScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_FrameDrawOn(bottomScreen);
|
||||
C3D_RenderTargetClear(bottomScreen, C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_FrameDrawOn(topScreen);
|
||||
C3D_RenderTargetClear(topScreen, C3D_CLEAR_COLOR, 0, 0);
|
||||
}
|
||||
|
||||
static void _drawEnd(void) {
|
||||
|
@ -227,12 +223,10 @@ static void _drawEnd(void) {
|
|||
return;
|
||||
}
|
||||
ctrEndFrame();
|
||||
C3D_RenderTargetSetOutput(topScreen[doubleBuffer], GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_RenderTargetSetOutput(bottomScreen[doubleBuffer], GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_RenderTargetSetOutput(topScreen, GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_RenderTargetSetOutput(bottomScreen, GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_FrameEnd(0);
|
||||
frameStarted = false;
|
||||
|
||||
doubleBuffer ^= 1;
|
||||
}
|
||||
|
||||
static int _batteryState(void) {
|
||||
|
@ -256,7 +250,7 @@ static int _batteryState(void) {
|
|||
}
|
||||
|
||||
static void _guiPrepare(void) {
|
||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
||||
C3D_FrameDrawOn(bottomScreen);
|
||||
ctrSetViewportSize(320, 240, true);
|
||||
}
|
||||
|
||||
|
@ -476,21 +470,33 @@ static u32 _setupTex(int out, bool faded) {
|
|||
static void _drawTex(struct mCore* core, bool faded, bool both) {
|
||||
unsigned screen_w, screen_h;
|
||||
bool isWide = screenMode >= SM_PA_TOP && gfxIsWide();
|
||||
|
||||
if (filterMode < FM_LINEAR_1x || filterMode > FM_LINEAR_3x) {
|
||||
// Out-of-range filtering modes are not supported
|
||||
filterMode = FM_LINEAR_3x;
|
||||
}
|
||||
int mult = 1 + filterMode - FM_LINEAR_1x;
|
||||
|
||||
switch (screenMode) {
|
||||
case SM_PA_BOTTOM:
|
||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
||||
C3D_FrameDrawOn(bottomScreen);
|
||||
screen_w = 320;
|
||||
screen_h = 240;
|
||||
break;
|
||||
case SM_PA_TOP:
|
||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
||||
C3D_FrameDrawOn(topScreen);
|
||||
screen_w = isWide ? 800 : 400;
|
||||
screen_h = 240;
|
||||
break;
|
||||
default:
|
||||
C3D_FrameDrawOn(upscaleBuffer);
|
||||
screen_w = 512;
|
||||
screen_h = 512;
|
||||
// PICA200 erratum: if viewport X coord exceeds 1023, entire polygon
|
||||
// is not rendered. If viewport Y coord exceeds 1016, GPU hangs.
|
||||
// This can not be mitigated by scissor testing.
|
||||
// C3D_FrameDrawOn sets the viewport dims to the texture's dims,
|
||||
// thus we must re-set the viewport ourselves.
|
||||
screen_w = 256 * mult;
|
||||
screen_h = 256 * mult;
|
||||
break;
|
||||
}
|
||||
int wide = isWide ? 2 : 1;
|
||||
|
@ -523,17 +529,13 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
case SM_AF_BOTTOM:
|
||||
case SM_SF_TOP:
|
||||
case SM_SF_BOTTOM:
|
||||
default:
|
||||
if (filterMode == FM_LINEAR_1x) {
|
||||
w = corew;
|
||||
h = coreh;
|
||||
} else {
|
||||
w = corew * 2;
|
||||
h = coreh * 2;
|
||||
}
|
||||
default: {
|
||||
w = corew * mult;
|
||||
h = coreh * mult;
|
||||
ctrSetViewportSize(screen_w, screen_h, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t color = _setupTex(activeOutputTexture, faded);
|
||||
ctrAddRectEx(color, x, y, w, h, 0, 0, corew, coreh, 0);
|
||||
|
@ -549,10 +551,10 @@ static void _drawTex(struct mCore* core, bool faded, bool both) {
|
|||
coreh = h;
|
||||
screen_h = 240;
|
||||
if (screenMode < SM_PA_TOP) {
|
||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
||||
C3D_FrameDrawOn(bottomScreen);
|
||||
screen_w = 320;
|
||||
} else {
|
||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
||||
C3D_FrameDrawOn(topScreen);
|
||||
screen_w = isWide ? 800 : 400;
|
||||
}
|
||||
ctrSetViewportSize(screen_w, screen_h, true);
|
||||
|
@ -979,13 +981,14 @@ int main(int argc, char* argv[]) {
|
|||
.title = "Filtering",
|
||||
.data = GUI_V_S("filterMode"),
|
||||
.submenu = 0,
|
||||
.state = FM_LINEAR_2x,
|
||||
.state = FM_LINEAR_3x,
|
||||
.validStates = (const char*[]) {
|
||||
NULL, // Disable choosing nearest neighbor; it always looks bad
|
||||
"Bilinear (smoother)",
|
||||
"Bilinear (pixelated)",
|
||||
"Bilinear (ultrasharp)",
|
||||
},
|
||||
.nStates = 3
|
||||
.nStates = 4
|
||||
},
|
||||
{
|
||||
.title = "Screen darkening",
|
||||
|
|
Loading…
Reference in New Issue