From ebcb0e2486ca5b549b4d6285664cddeb79196d4b Mon Sep 17 00:00:00 2001 From: Splendide Imaginarius <119545140+Splendide-Imaginarius@users.noreply.github.com> Date: Thu, 21 Mar 2024 02:07:12 +0000 Subject: [PATCH] Allow independent upscale/downscale interpolation methods --- mkxp.json | 9 ++++- src/config.cpp | 2 + src/config.h | 1 + src/display/gl/gl-meta.cpp | 45 +++++++++++++++------ src/display/gl/gl-meta.h | 9 +++-- src/display/graphics.cpp | 80 +++++++++++++++++++------------------- src/etc/etc.h | 8 ++++ 7 files changed, 98 insertions(+), 56 deletions(-) diff --git a/mkxp.json b/mkxp.json index e5883d38..19de7176 100644 --- a/mkxp.json +++ b/mkxp.json @@ -89,8 +89,15 @@ // "smoothScaling": 0, + // Apply smooth interpolation when game screen + // is downscaled (same values as smoothScaling) + // (default: 0) + // + // "smoothScalingDown": 0, + + // Apply mipmap interpolation when game screen - // is downscaled + // is downscaled (requires "smoothScalingDown": 1) // (default: false) // // "smoothScalingMipmaps": false, diff --git a/src/config.cpp b/src/config.cpp index 03948acc..416e2f20 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -135,6 +135,7 @@ void Config::read(int argc, char *argv[]) { {"fullscreen", false}, {"fixedAspectRatio", true}, {"smoothScaling", 0}, + {"smoothScalingDown", 0}, {"smoothScalingMipmaps", false}, {"bicubicSharpness", 100}, #ifdef MKXPZ_SSL @@ -270,6 +271,7 @@ try { exp } catch (...) {} SET_OPT(fullscreen, boolean); SET_OPT(fixedAspectRatio, boolean); SET_OPT(smoothScaling, integer); + SET_OPT(smoothScalingDown, integer); SET_OPT(smoothScalingMipmaps, boolean); SET_OPT(bicubicSharpness, integer); #ifdef MKXPZ_SSL diff --git a/src/config.h b/src/config.h index 873c7142..df5c33f5 100644 --- a/src/config.h +++ b/src/config.h @@ -44,6 +44,7 @@ struct Config { bool fullscreen; bool fixedAspectRatio; int smoothScaling; + int smoothScalingDown; bool smoothScalingMipmaps; int bicubicSharpness; #ifdef MKXPZ_SSL diff --git a/src/display/gl/gl-meta.cpp b/src/display/gl/gl-meta.cpp index b4598b42..22487f60 100644 --- a/src/display/gl/gl-meta.cpp +++ b/src/display/gl/gl-meta.cpp @@ -140,7 +140,7 @@ void vaoUnbind(VAO &vao) #define HAVE_NATIVE_BLIT gl.BlitFramebuffer -bool blitScaleIsOne(TEXFBO &target, bool targetPreferHires, const IntRect &targetRect, TEXFBO &source, const IntRect &sourceRect) +int blitScaleIsSpecial(TEXFBO &target, bool targetPreferHires, const IntRect &targetRect, TEXFBO &source, const IntRect &sourceRect) { int targetWidth = targetRect.w; int targetHeight = targetRect.h; @@ -166,10 +166,33 @@ bool blitScaleIsOne(TEXFBO &target, bool targetPreferHires, const IntRect &targe sourceHeight /= source.height; } - return targetWidth == sourceWidth && targetHeight == sourceHeight; + if (targetWidth == sourceWidth && targetHeight == sourceHeight) + { + return SameScale; + } + + if (targetWidth < sourceWidth && targetHeight < sourceHeight) + { + return DownScale; + } + + return UpScale; } -static void _blitBegin(FBO::ID fbo, const Vec2i &size, bool scaleIsOne) +int smoothScalingMethod(int scaleIsSpecial) +{ + switch (scaleIsSpecial) + { + case SameScale: + return NearestNeighbor; + case DownScale: + return shState->config().smoothScalingDown; + } + + return shState->config().smoothScaling; +} + +static void _blitBegin(FBO::ID fbo, const Vec2i &size, int scaleIsSpecial) { if (HAVE_NATIVE_BLIT) { @@ -181,7 +204,7 @@ static void _blitBegin(FBO::ID fbo, const Vec2i &size, bool scaleIsOne) FBO::bind(fbo); glState.viewport.pushSet(IntRect(0, 0, size.x, size.y)); - switch (scaleIsOne ? NearestNeighbor : shState->config().smoothScaling) + switch (smoothScalingMethod(scaleIsSpecial)) { case Bicubic: { @@ -239,7 +262,7 @@ int blitSrcWidthHires = 1; int blitSrcHeightLores = 1; int blitSrcHeightHires = 1; -void blitBegin(TEXFBO &target, bool preferHires, bool scaleIsOne) +void blitBegin(TEXFBO &target, bool preferHires, int scaleIsSpecial) { blitDstWidthLores = target.width; blitDstHeightLores = target.height; @@ -247,26 +270,26 @@ void blitBegin(TEXFBO &target, bool preferHires, bool scaleIsOne) if (preferHires && target.selfHires != nullptr) { blitDstWidthHires = target.selfHires->width; blitDstHeightHires = target.selfHires->height; - _blitBegin(target.selfHires->fbo, Vec2i(target.selfHires->width, target.selfHires->height), scaleIsOne); + _blitBegin(target.selfHires->fbo, Vec2i(target.selfHires->width, target.selfHires->height), scaleIsSpecial); } else { blitDstWidthHires = blitDstWidthLores; blitDstHeightHires = blitDstHeightLores; - _blitBegin(target.fbo, Vec2i(target.width, target.height), scaleIsOne); + _blitBegin(target.fbo, Vec2i(target.width, target.height), scaleIsSpecial); } } -void blitBeginScreen(const Vec2i &size, bool scaleIsOne) +void blitBeginScreen(const Vec2i &size, int scaleIsSpecial) { blitDstWidthLores = 1; blitDstWidthHires = 1; blitDstHeightLores = 1; blitDstHeightHires = 1; - _blitBegin(FBO::ID(0), size, scaleIsOne); + _blitBegin(FBO::ID(0), size, scaleIsSpecial); } -void blitSource(TEXFBO &source, bool scaleIsOne) +void blitSource(TEXFBO &source, int scaleIsSpecial) { blitSrcWidthLores = source.width; blitSrcHeightLores = source.height; @@ -285,7 +308,7 @@ void blitSource(TEXFBO &source, bool scaleIsOne) } else { - switch (scaleIsOne ? NearestNeighbor : shState->config().smoothScaling) + switch (smoothScalingMethod(scaleIsSpecial)) { case Bicubic: { diff --git a/src/display/gl/gl-meta.h b/src/display/gl/gl-meta.h index 5dc1fc72..8572490f 100644 --- a/src/display/gl/gl-meta.h +++ b/src/display/gl/gl-meta.h @@ -65,10 +65,11 @@ void vaoBind(VAO &vao); void vaoUnbind(VAO &vao); /* EXT_framebuffer_blit */ -bool blitScaleIsOne(TEXFBO &target, bool targetPreferHires, const IntRect &targetRect, TEXFBO &source, const IntRect &sourceRect); -void blitBegin(TEXFBO &target, bool preferHires = false, bool scaleIsOne = false); -void blitBeginScreen(const Vec2i &size, bool scaleIsOne = false); -void blitSource(TEXFBO &source, bool scaleIsOne = false); +int blitScaleIsSpecial(TEXFBO &target, bool targetPreferHires, const IntRect &targetRect, TEXFBO &source, const IntRect &sourceRect); +int smoothScalingMethod(int scaleIsSpecial); +void blitBegin(TEXFBO &target, bool preferHires = false, int scaleIsOne = 0); +void blitBeginScreen(const Vec2i &size, int scaleIsOne = 0); +void blitSource(TEXFBO &source, int scaleIsOne = 0); void blitRectangle(const IntRect &src, const Vec2i &dstPos); void blitRectangle(const IntRect &src, const IntRect &dst, bool smooth = false); diff --git a/src/display/graphics.cpp b/src/display/graphics.cpp index 8957bd33..772eecd0 100644 --- a/src/display/graphics.cpp +++ b/src/display/graphics.cpp @@ -543,10 +543,10 @@ public: * be turned on, so turn it off temporarily */ glState.scissorTest.pushSet(false); - bool scaleIsOne = GLMeta::blitScaleIsOne(pp.frontBuffer(), false, geometry.rect, pp.backBuffer(), geometry.rect); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(pp.frontBuffer(), false, geometry.rect, pp.backBuffer(), geometry.rect); - GLMeta::blitBegin(pp.frontBuffer(), false, scaleIsOne); - GLMeta::blitSource(pp.backBuffer(), scaleIsOne); + GLMeta::blitBegin(pp.frontBuffer(), false, scaleIsSpecial); + GLMeta::blitSource(pp.backBuffer(), scaleIsSpecial); GLMeta::blitRectangle(geometry.rect, Vec2i()); GLMeta::blitEnd(); @@ -1011,29 +1011,29 @@ struct GraphicsPrivate { void compositeToBufferScaled(TEXFBO &buffer, int destWidth, int destHeight) { screen.composite(); - bool scaleIsOne = GLMeta::blitScaleIsOne(buffer, false, IntRect(0, 0, destWidth, destHeight), screen.getPP().frontBuffer(), IntRect(0, 0, scRes.x, scRes.y)); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(buffer, false, IntRect(0, 0, destWidth, destHeight), screen.getPP().frontBuffer(), IntRect(0, 0, scRes.x, scRes.y)); - GLMeta::blitBegin(buffer, false, scaleIsOne); - GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsOne); + GLMeta::blitBegin(buffer, false, scaleIsSpecial); + GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsSpecial); GLMeta::blitRectangle(IntRect(0, 0, scRes.x, scRes.y), IntRect(0, 0, destWidth, destHeight)); GLMeta::blitEnd(); } - void metaBlitBufferFlippedScaled() { - metaBlitBufferFlippedScaled(scRes); + void metaBlitBufferFlippedScaled(int scaleIsSpecial) { + metaBlitBufferFlippedScaled(scRes, scaleIsSpecial); GLMeta::blitRectangle( IntRect(0, 0, scRes.x, scRes.y), IntRect(scOffset.x, (scSize.y + scOffset.y), scSize.x, -scSize.y), - threadData->config.smoothScaling == Bilinear); + GLMeta::smoothScalingMethod(scaleIsSpecial) == Bilinear); } - void metaBlitBufferFlippedScaled(const Vec2i &sourceSize, bool forceNearestNeighbor=false) { + void metaBlitBufferFlippedScaled(const Vec2i &sourceSize, int scaleIsSpecial, bool forceNearestNeighbor=false) { GLMeta::blitRectangle(IntRect(0, 0, sourceSize.x, sourceSize.y), IntRect(scOffset.x, scSize.y+scOffset.y, scSize.x, -scSize.y), - !forceNearestNeighbor && threadData->config.smoothScaling == Bilinear); + !forceNearestNeighbor && GLMeta::smoothScalingMethod(scaleIsSpecial) == Bilinear); } void redrawScreen() { @@ -1042,13 +1042,13 @@ struct GraphicsPrivate { // maybe unspaghetti this later if (integerScaleStepApplicable() && !integerLastMileScaling) { - bool scaleIsOne = GLMeta::blitScaleIsOne(integerScaleBuffer, false, IntRect(0, 0, scSize.x, scSize.y), screen.getPP().frontBuffer(), IntRect(0, 0, scRes.x, scRes.y)); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(integerScaleBuffer, false, IntRect(0, 0, scSize.x, scSize.y), screen.getPP().frontBuffer(), IntRect(0, 0, scRes.x, scRes.y)); - GLMeta::blitBeginScreen(winSize, scaleIsOne); - GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsOne); + GLMeta::blitBeginScreen(winSize, scaleIsSpecial); + GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsSpecial); FBO::clear(); - metaBlitBufferFlippedScaled(scRes, true); + metaBlitBufferFlippedScaled(scRes, scaleIsSpecial, true); GLMeta::blitEnd(); swapGLBuffer(); @@ -1057,11 +1057,11 @@ struct GraphicsPrivate { if (integerScaleStepApplicable()) { - bool scaleIsOne = GLMeta::blitScaleIsOne(integerScaleBuffer, false, IntRect(0, 0, integerScaleBuffer.width, integerScaleBuffer.height), screen.getPP().frontBuffer(), IntRect(0, 0, scRes.x, scRes.y)); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(integerScaleBuffer, false, IntRect(0, 0, integerScaleBuffer.width, integerScaleBuffer.height), screen.getPP().frontBuffer(), IntRect(0, 0, scRes.x, scRes.y)); assert(integerScaleBuffer.tex != TEX::ID(0)); - GLMeta::blitBegin(integerScaleBuffer, false, scaleIsOne); - GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsOne); + GLMeta::blitBegin(integerScaleBuffer, false, scaleIsSpecial); + GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsSpecial); GLMeta::blitRectangle(IntRect(0, 0, scRes.x, scRes.y), IntRect(0, 0, integerScaleBuffer.width, integerScaleBuffer.height), @@ -1082,22 +1082,22 @@ struct GraphicsPrivate { sourceSize = scRes; } - bool scaleIsOne = GLMeta::blitScaleIsOne(integerScaleBuffer, false, IntRect(0, 0, scSize.x, scSize.y), integerScaleActive ? integerScaleBuffer : screen.getPP().frontBuffer(), IntRect(0, 0, sourceSize.x, sourceSize.y)); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(integerScaleBuffer, false, IntRect(0, 0, scSize.x, scSize.y), integerScaleActive ? integerScaleBuffer : screen.getPP().frontBuffer(), IntRect(0, 0, sourceSize.x, sourceSize.y)); - GLMeta::blitBeginScreen(winSize, scaleIsOne); - //GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsOne); + GLMeta::blitBeginScreen(winSize, scaleIsSpecial); + //GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsSpecial); if (integerScaleActive) { - GLMeta::blitSource(integerScaleBuffer, scaleIsOne); + GLMeta::blitSource(integerScaleBuffer, scaleIsSpecial); } else { - GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsOne); + GLMeta::blitSource(screen.getPP().frontBuffer(), scaleIsSpecial); } FBO::clear(); - metaBlitBufferFlippedScaled(sourceSize); + metaBlitBufferFlippedScaled(sourceSize, scaleIsSpecial); GLMeta::blitEnd(); @@ -1327,11 +1327,11 @@ void Graphics::transition(int duration, const char *filename, int vague) { FBO::unbind(); FBO::clear(); - bool scaleIsOne = GLMeta::blitScaleIsOne(p->integerScaleBuffer, false, IntRect(0, 0, p->scSize.x, p->scSize.y), transBuffer, IntRect(0, 0, p->scRes.x, p->scRes.y)); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(p->integerScaleBuffer, false, IntRect(0, 0, p->scSize.x, p->scSize.y), transBuffer, IntRect(0, 0, p->scRes.x, p->scRes.y)); - GLMeta::blitBeginScreen(Vec2i(p->winSize), scaleIsOne); - GLMeta::blitSource(transBuffer, scaleIsOne); - p->metaBlitBufferFlippedScaled(); + GLMeta::blitBeginScreen(Vec2i(p->winSize), scaleIsSpecial); + GLMeta::blitSource(transBuffer, scaleIsSpecial); + p->metaBlitBufferFlippedScaled(scaleIsSpecial); GLMeta::blitEnd(); p->swapGLBuffer(); @@ -1388,13 +1388,13 @@ void Graphics::fadeout(int duration) { setBrightness(diff + (curr / duration) * i); if (p->frozen) { - bool scaleIsOne = GLMeta::blitScaleIsOne(p->integerScaleBuffer, false, IntRect(0, 0, p->scSize.x, p->scSize.y), p->frozenScene, IntRect(0, 0, p->scRes.x, p->scRes.y)); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(p->integerScaleBuffer, false, IntRect(0, 0, p->scSize.x, p->scSize.y), p->frozenScene, IntRect(0, 0, p->scRes.x, p->scRes.y)); - GLMeta::blitBeginScreen(p->scSize, scaleIsOne); - GLMeta::blitSource(p->frozenScene, scaleIsOne); + GLMeta::blitBeginScreen(p->scSize, scaleIsSpecial); + GLMeta::blitSource(p->frozenScene, scaleIsSpecial); FBO::clear(); - p->metaBlitBufferFlippedScaled(); + p->metaBlitBufferFlippedScaled(scaleIsSpecial); GLMeta::blitEnd(); @@ -1415,13 +1415,13 @@ void Graphics::fadein(int duration) { setBrightness(curr + (diff / duration) * i); if (p->frozen) { - bool scaleIsOne = GLMeta::blitScaleIsOne(p->integerScaleBuffer, false, IntRect(0, 0, p->scSize.x, p->scSize.y), p->frozenScene, IntRect(0, 0, p->scRes.x, p->scRes.y)); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(p->integerScaleBuffer, false, IntRect(0, 0, p->scSize.x, p->scSize.y), p->frozenScene, IntRect(0, 0, p->scRes.x, p->scRes.y)); - GLMeta::blitBeginScreen(p->scSize, scaleIsOne); - GLMeta::blitSource(p->frozenScene, scaleIsOne); + GLMeta::blitBeginScreen(p->scSize, scaleIsSpecial); + GLMeta::blitSource(p->frozenScene, scaleIsSpecial); FBO::clear(); - p->metaBlitBufferFlippedScaled(); + p->metaBlitBufferFlippedScaled(scaleIsSpecial); GLMeta::blitEnd(); @@ -1726,10 +1726,10 @@ void Graphics::repaintWait(const AtomicFlag &exitCond, bool checkReset) { /* Repaint the screen with the last good frame we drew */ TEXFBO &lastFrame = p->screen.getPP().frontBuffer(); - bool scaleIsOne = GLMeta::blitScaleIsOne(p->integerScaleBuffer, false, IntRect(0, 0, p->scSize.x, p->scSize.y), lastFrame, IntRect(0, 0, p->scRes.x, p->scRes.y)); + int scaleIsSpecial = GLMeta::blitScaleIsSpecial(p->integerScaleBuffer, false, IntRect(0, 0, p->scSize.x, p->scSize.y), lastFrame, IntRect(0, 0, p->scRes.x, p->scRes.y)); - GLMeta::blitBeginScreen(p->winSize, scaleIsOne); - GLMeta::blitSource(lastFrame, scaleIsOne); + GLMeta::blitBeginScreen(p->winSize, scaleIsSpecial); + GLMeta::blitSource(lastFrame, scaleIsSpecial); while (!exitCond) { shState->checkShutdown(); @@ -1738,7 +1738,7 @@ void Graphics::repaintWait(const AtomicFlag &exitCond, bool checkReset) { shState->checkReset(); FBO::clear(); - p->metaBlitBufferFlippedScaled(); + p->metaBlitBufferFlippedScaled(scaleIsSpecial); SDL_GL_SwapWindow(p->threadData->window); p->fpsLimiter.delay(); diff --git a/src/etc/etc.h b/src/etc/etc.h index 9ef5c40f..9f58649d 100644 --- a/src/etc/etc.h +++ b/src/etc/etc.h @@ -211,6 +211,14 @@ enum InterpolationMethod #endif }; +enum SpecialScale +{ + // If the X and Y scales would yield different results, it's considered UpScale. + UpScale = 0, + SameScale = 1, + DownScale = 2, +}; + /* For internal use. * All drawable classes have properties of one or more of the above * types, which in an interpreted environment act as independent