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_NEAREST,
|
||||||
FM_LINEAR_1x,
|
FM_LINEAR_1x,
|
||||||
FM_LINEAR_2x,
|
FM_LINEAR_2x,
|
||||||
|
FM_LINEAR_3x,
|
||||||
FM_MAX
|
FM_MAX
|
||||||
} filterMode = FM_LINEAR_2x;
|
} filterMode = FM_LINEAR_3x;
|
||||||
|
|
||||||
static enum DarkenMode {
|
static enum DarkenMode {
|
||||||
DM_NATIVE,
|
DM_NATIVE,
|
||||||
|
@ -97,9 +98,8 @@ static int bufferId = 0;
|
||||||
static bool frameLimiter = true;
|
static bool frameLimiter = true;
|
||||||
static u32 frameCounter;
|
static u32 frameCounter;
|
||||||
|
|
||||||
static C3D_RenderTarget* topScreen[2];
|
static C3D_RenderTarget* topScreen;
|
||||||
static C3D_RenderTarget* bottomScreen[2];
|
static C3D_RenderTarget* bottomScreen;
|
||||||
static int doubleBuffer = 0;
|
|
||||||
static bool frameStarted = false;
|
static bool frameStarted = false;
|
||||||
|
|
||||||
static C3D_RenderTarget* upscaleBuffer;
|
static C3D_RenderTarget* upscaleBuffer;
|
||||||
|
@ -115,28 +115,26 @@ static bool _initGpu(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gfxIsWide()) {
|
if (gfxIsWide()) {
|
||||||
topScreen[0] = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0);
|
topScreen = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0);
|
||||||
topScreen[1] = C3D_RenderTargetCreate(240, 800, GPU_RB_RGB8, 0);
|
|
||||||
} else {
|
} else {
|
||||||
topScreen[0] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0);
|
topScreen = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0);
|
||||||
topScreen[1] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0);
|
|
||||||
}
|
}
|
||||||
bottomScreen[0] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0);
|
bottomScreen = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0);
|
||||||
bottomScreen[1] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0);
|
if (!topScreen || !bottomScreen) {
|
||||||
if (!topScreen[0] || !topScreen[1] || !bottomScreen[0] || !bottomScreen[1]) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
C3D_FrameBegin(0);
|
C3D_FrameBegin(0);
|
||||||
C3D_FrameDrawOn(bottomScreen[0]);
|
C3D_FrameDrawOn(bottomScreen);
|
||||||
C3D_RenderTargetClear(bottomScreen[0], C3D_CLEAR_COLOR, 0, 0);
|
C3D_RenderTargetClear(bottomScreen, C3D_CLEAR_COLOR, 0, 0);
|
||||||
C3D_FrameDrawOn(topScreen[0]);
|
C3D_FrameDrawOn(topScreen);
|
||||||
C3D_RenderTargetClear(topScreen[0], C3D_CLEAR_COLOR, 0, 0);
|
C3D_RenderTargetClear(topScreen, 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(topScreen, 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_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);
|
C3D_FrameEnd(0);
|
||||||
|
|
||||||
if (!C3D_TexInitVRAM(&upscaleBufferTex, 512, 512, GPU_RGB8)) {
|
if (!C3D_TexInitVRAM(&upscaleBufferTex, 1024, 512, GPU_RGB8)) {
|
||||||
|
__builtin_trap();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
upscaleBuffer = C3D_RenderTargetCreateFromTex(&upscaleBufferTex, GPU_TEXFACE_2D, 0, 0);
|
upscaleBuffer = C3D_RenderTargetCreateFromTex(&upscaleBufferTex, GPU_TEXFACE_2D, 0, 0);
|
||||||
|
@ -164,10 +162,8 @@ static void _cleanup(void) {
|
||||||
screenshotBuffer = NULL;
|
screenshotBuffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
C3D_RenderTargetDelete(topScreen[0]);
|
C3D_RenderTargetDelete(topScreen);
|
||||||
C3D_RenderTargetDelete(topScreen[1]);
|
C3D_RenderTargetDelete(bottomScreen);
|
||||||
C3D_RenderTargetDelete(bottomScreen[0]);
|
|
||||||
C3D_RenderTargetDelete(bottomScreen[1]);
|
|
||||||
C3D_RenderTargetDelete(upscaleBuffer);
|
C3D_RenderTargetDelete(upscaleBuffer);
|
||||||
C3D_TexDelete(&upscaleBufferTex);
|
C3D_TexDelete(&upscaleBufferTex);
|
||||||
C3D_TexDelete(&outputTexture[0]);
|
C3D_TexDelete(&outputTexture[0]);
|
||||||
|
@ -216,10 +212,10 @@ static void _drawStart(void) {
|
||||||
C3D_FrameBegin(0);
|
C3D_FrameBegin(0);
|
||||||
ctrStartFrame();
|
ctrStartFrame();
|
||||||
|
|
||||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
C3D_FrameDrawOn(bottomScreen);
|
||||||
C3D_RenderTargetClear(bottomScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0);
|
C3D_RenderTargetClear(bottomScreen, C3D_CLEAR_COLOR, 0, 0);
|
||||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
C3D_FrameDrawOn(topScreen);
|
||||||
C3D_RenderTargetClear(topScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0);
|
C3D_RenderTargetClear(topScreen, C3D_CLEAR_COLOR, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _drawEnd(void) {
|
static void _drawEnd(void) {
|
||||||
|
@ -227,12 +223,10 @@ static void _drawEnd(void) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctrEndFrame();
|
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(topScreen, 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(bottomScreen, GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||||
C3D_FrameEnd(0);
|
C3D_FrameEnd(0);
|
||||||
frameStarted = false;
|
frameStarted = false;
|
||||||
|
|
||||||
doubleBuffer ^= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _batteryState(void) {
|
static int _batteryState(void) {
|
||||||
|
@ -256,7 +250,7 @@ static int _batteryState(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _guiPrepare(void) {
|
static void _guiPrepare(void) {
|
||||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
C3D_FrameDrawOn(bottomScreen);
|
||||||
ctrSetViewportSize(320, 240, true);
|
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) {
|
static void _drawTex(struct mCore* core, bool faded, bool both) {
|
||||||
unsigned screen_w, screen_h;
|
unsigned screen_w, screen_h;
|
||||||
bool isWide = screenMode >= SM_PA_TOP && gfxIsWide();
|
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) {
|
switch (screenMode) {
|
||||||
case SM_PA_BOTTOM:
|
case SM_PA_BOTTOM:
|
||||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
C3D_FrameDrawOn(bottomScreen);
|
||||||
screen_w = 320;
|
screen_w = 320;
|
||||||
screen_h = 240;
|
screen_h = 240;
|
||||||
break;
|
break;
|
||||||
case SM_PA_TOP:
|
case SM_PA_TOP:
|
||||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
C3D_FrameDrawOn(topScreen);
|
||||||
screen_w = isWide ? 800 : 400;
|
screen_w = isWide ? 800 : 400;
|
||||||
screen_h = 240;
|
screen_h = 240;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
C3D_FrameDrawOn(upscaleBuffer);
|
C3D_FrameDrawOn(upscaleBuffer);
|
||||||
screen_w = 512;
|
// PICA200 erratum: if viewport X coord exceeds 1023, entire polygon
|
||||||
screen_h = 512;
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
int wide = isWide ? 2 : 1;
|
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_AF_BOTTOM:
|
||||||
case SM_SF_TOP:
|
case SM_SF_TOP:
|
||||||
case SM_SF_BOTTOM:
|
case SM_SF_BOTTOM:
|
||||||
default:
|
default: {
|
||||||
if (filterMode == FM_LINEAR_1x) {
|
w = corew * mult;
|
||||||
w = corew;
|
h = coreh * mult;
|
||||||
h = coreh;
|
|
||||||
} else {
|
|
||||||
w = corew * 2;
|
|
||||||
h = coreh * 2;
|
|
||||||
}
|
|
||||||
ctrSetViewportSize(screen_w, screen_h, false);
|
ctrSetViewportSize(screen_w, screen_h, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t color = _setupTex(activeOutputTexture, faded);
|
uint32_t color = _setupTex(activeOutputTexture, faded);
|
||||||
ctrAddRectEx(color, x, y, w, h, 0, 0, corew, coreh, 0);
|
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;
|
coreh = h;
|
||||||
screen_h = 240;
|
screen_h = 240;
|
||||||
if (screenMode < SM_PA_TOP) {
|
if (screenMode < SM_PA_TOP) {
|
||||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
C3D_FrameDrawOn(bottomScreen);
|
||||||
screen_w = 320;
|
screen_w = 320;
|
||||||
} else {
|
} else {
|
||||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
C3D_FrameDrawOn(topScreen);
|
||||||
screen_w = isWide ? 800 : 400;
|
screen_w = isWide ? 800 : 400;
|
||||||
}
|
}
|
||||||
ctrSetViewportSize(screen_w, screen_h, true);
|
ctrSetViewportSize(screen_w, screen_h, true);
|
||||||
|
@ -979,13 +981,14 @@ int main(int argc, char* argv[]) {
|
||||||
.title = "Filtering",
|
.title = "Filtering",
|
||||||
.data = GUI_V_S("filterMode"),
|
.data = GUI_V_S("filterMode"),
|
||||||
.submenu = 0,
|
.submenu = 0,
|
||||||
.state = FM_LINEAR_2x,
|
.state = FM_LINEAR_3x,
|
||||||
.validStates = (const char*[]) {
|
.validStates = (const char*[]) {
|
||||||
NULL, // Disable choosing nearest neighbor; it always looks bad
|
NULL, // Disable choosing nearest neighbor; it always looks bad
|
||||||
"Bilinear (smoother)",
|
"Bilinear (smoother)",
|
||||||
"Bilinear (pixelated)",
|
"Bilinear (pixelated)",
|
||||||
|
"Bilinear (ultrasharp)",
|
||||||
},
|
},
|
||||||
.nStates = 3
|
.nStates = 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.title = "Screen darkening",
|
.title = "Screen darkening",
|
||||||
|
|
Loading…
Reference in New Issue