Graphics: Implement native integer scaling

This commit is contained in:
Amaryllis Kulla 2021-12-07 07:13:53 +01:00
parent f6db7be538
commit 9cadb91ed1
2 changed files with 181 additions and 9 deletions

View File

@ -477,6 +477,11 @@ struct GraphicsPrivate
TEXFBO frozenScene; TEXFBO frozenScene;
Quad screenQuad; Quad screenQuad;
Vec2i integerScaleFactor;
TEXFBO integerScaleBuffer;
bool integerScaleActive;
bool integerLastMileScaling;
/* Global list of all live Disposables /* Global list of all live Disposables
* (disposed on reset) */ * (disposed on reset) */
IntruList<Disposable> dispList; IntruList<Disposable> dispList;
@ -492,9 +497,18 @@ struct GraphicsPrivate
frameCount(0), frameCount(0),
brightness(255), brightness(255),
fpsLimiter(frameRate), fpsLimiter(frameRate),
frozen(false) frozen(false),
integerScaleFactor(0, 0),
integerScaleActive(rtData->config.integerScaling.active),
integerLastMileScaling(rtData->config.integerScaling.lastMileScaling)
{ {
recalculateScreenSize(rtData); if (integerScaleActive)
{
integerScaleFactor = Vec2i(1, 1);
rebuildIntegerScaleBuffer();
}
recalculateScreenSize(rtData->config.fixedAspectRatio);
updateScreenResoRatio(rtData); updateScreenResoRatio(rtData);
TEXFBO::init(frozenScene); TEXFBO::init(frozenScene);
@ -510,6 +524,7 @@ struct GraphicsPrivate
~GraphicsPrivate() ~GraphicsPrivate()
{ {
TEXFBO::fini(frozenScene); TEXFBO::fini(frozenScene);
TEXFBO::fini(integerScaleBuffer);
} }
void updateScreenResoRatio(RGSSThreadData *rtData) void updateScreenResoRatio(RGSSThreadData *rtData)
@ -522,16 +537,27 @@ struct GraphicsPrivate
} }
/* Enforces fixed aspect ratio, if desired */ /* Enforces fixed aspect ratio, if desired */
void recalculateScreenSize(RGSSThreadData *rtData) void recalculateScreenSize(bool fixedAspectRatio)
{ {
scSize = winSize; scSize = winSize;
if (!rtData->config.fixedAspectRatio) if (!fixedAspectRatio && integerLastMileScaling)
{ {
scOffset = Vec2i(0, 0); scOffset = Vec2i(0, 0);
return; return;
} }
/* Last mile scaling disabled: just center the integer scale buffer
* inside the window space */
if (integerScaleActive && !integerLastMileScaling)
{
scOffset.x = (winSize.x - scRes.x * integerScaleFactor.x) / 2;
scOffset.y = (winSize.y - scRes.y * integerScaleFactor.y) / 2;
scSize = Vec2i(scRes.x * integerScaleFactor.x, scRes.y * integerScaleFactor.y);
return;
}
float resRatio = (float) scRes.x / scRes.y; float resRatio = (float) scRes.x / scRes.y;
float winRatio = (float) winSize.x / winSize.y; float winRatio = (float) winSize.x / winSize.y;
@ -544,10 +570,68 @@ struct GraphicsPrivate
scOffset.y = (winSize.y - scSize.y) / 2.f; scOffset.y = (winSize.y - scSize.y) / 2.f;
} }
static int findHighestFittingScale(int base, int target)
{
int scale = 1;
while (base * scale <= target)
scale += 1;
return scale - 1;
}
/* Returns whether a new scale was found */
bool findHighestIntegerScale()
{
Vec2i newScale(findHighestFittingScale(scRes.x, winSize.x),
findHighestFittingScale(scRes.y, winSize.y));
if (threadData->config.fixedAspectRatio)
{
/* Limit both factors to the smaller of the two */
newScale.x = newScale.y = std::min(newScale.x, newScale.y);
}
if (newScale == integerScaleFactor)
return false;
integerScaleFactor = newScale;
return true;
}
void rebuildIntegerScaleBuffer()
{
TEXFBO::fini(integerScaleBuffer);
TEXFBO::init(integerScaleBuffer);
TEXFBO::allocEmpty(integerScaleBuffer, scRes.x * integerScaleFactor.x,
scRes.y * integerScaleFactor.y);
TEXFBO::linkFBO(integerScaleBuffer);
}
bool integerScaleStepApplicable() const
{
if (!integerScaleActive)
return false;
if (integerScaleFactor.x < 1 || integerScaleFactor.y < 1) // XXX should be < 2, this is for testing only
return false;
return true;
}
void checkResize() void checkResize()
{ {
if (threadData->windowSizeMsg.poll(winSize)) if (threadData->windowSizeMsg.poll(winSize))
{ {
/* Query the acutal size in pixels, not units */
SDL_GL_GetDrawableSize(threadData->window, &winSize.x, &winSize.y);
/* Make sure integer buffers are rebuilt before screen offsets are
* calculated so we have the final allocated buffer size ready */
if (integerScaleActive)
if (findHighestIntegerScale())
rebuildIntegerScaleBuffer();
/* some GL drivers change the viewport on window resize */ /* some GL drivers change the viewport on window resize */
glState.viewport.refresh(); glState.viewport.refresh();
recalculateScreenSize(threadData); recalculateScreenSize(threadData);
@ -600,20 +684,64 @@ struct GraphicsPrivate
void metaBlitBufferFlippedScaled() void metaBlitBufferFlippedScaled()
{ {
GLMeta::blitRectangle(IntRect(0, 0, scRes.x, scRes.y), metaBlitBufferFlippedScaled(scRes);
}
void metaBlitBufferFlippedScaled(const Vec2i &sourceSize, bool forceNearestNeighbour = false)
{
GLMeta::blitRectangle(IntRect(0, 0, sourceSize.x, sourceSize.y),
IntRect(scOffset.x, scSize.y+scOffset.y, scSize.x, -scSize.y), IntRect(scOffset.x, scSize.y+scOffset.y, scSize.x, -scSize.y),
threadData->config.smoothScaling); !forceNearestNeighbour && threadData->config.smoothScaling);
} }
void redrawScreen() void redrawScreen()
{ {
screen.composite(); screen.composite();
// maybe unspaghetti this later
if (integerScaleStepApplicable() && !integerLastMileScaling)
{
GLMeta::blitBeginScreen(winSize);
GLMeta::blitSource(screen.getPP().frontBuffer());
FBO::clear();
metaBlitBufferFlippedScaled(scRes, true);
GLMeta::blitEnd();
swapGLBuffer();
return;
}
if (integerScaleStepApplicable())
{
assert(integerScaleBuffer.tex != TEX::ID(0));
GLMeta::blitBegin(integerScaleBuffer);
GLMeta::blitSource(screen.getPP().frontBuffer());
GLMeta::blitRectangle(IntRect(0, 0, scRes.x, scRes.y),
IntRect(0, 0, integerScaleBuffer.width, integerScaleBuffer.height),
false);
GLMeta::blitEnd();
}
GLMeta::blitBeginScreen(winSize); GLMeta::blitBeginScreen(winSize);
GLMeta::blitSource(screen.getPP().frontBuffer());
Vec2i sourceSize;
if (integerScaleActive)
{
GLMeta::blitSource(integerScaleBuffer);
sourceSize = Vec2i(integerScaleBuffer.width, integerScaleBuffer.height);
}
else
{
GLMeta::blitSource(screen.getPP().frontBuffer());
sourceSize = scRes;
}
FBO::clear(); FBO::clear();
metaBlitBufferFlippedScaled(); metaBlitBufferFlippedScaled(sourceSize);
GLMeta::blitEnd(); GLMeta::blitEnd();
@ -1026,7 +1154,48 @@ bool Graphics::getFixedAspectRatio() const
void Graphics::setFixedAspectRatio(bool value) void Graphics::setFixedAspectRatio(bool value)
{ {
shState->config().fixedAspectRatio = value; shState->config().fixedAspectRatio = value;
p->recalculateScreenSize(p->threadData); p->findHighestIntegerScale();
p->recalculateScreenSize(p->threadData->config.fixedAspectRatio);
p->updateScreenResoRatio(p->threadData);
}
bool Graphics::getSmoothScaling() const
{
// Same deal as with fixed aspect ratio
return shState->config().smoothScaling;
}
void Graphics::setSmoothScaling(bool value)
{
shState->config().smoothScaling = value;
}
bool Graphics::getIntegerScaling() const
{
return p->integerScaleActive;
}
void Graphics::setIntegerScaling(bool value)
{
p->integerScaleActive = value;
p->findHighestIntegerScale();
p->rebuildIntegerScaleBuffer();
p->recalculateScreenSize(p->threadData->config.fixedAspectRatio);
p->updateScreenResoRatio(p->threadData);
}
bool Graphics::getLastMileScaling() const
{
return p->integerLastMileScaling;
}
void Graphics::setLastMileScaling(bool value)
{
p->integerLastMileScaling = value;
p->recalculateScreenSize(p->threadData->config.fixedAspectRatio);
p->updateScreenResoRatio(p->threadData); p->updateScreenResoRatio(p->threadData);
} }

View File

@ -63,6 +63,9 @@ public:
DECL_ATTR( ShowCursor, bool ) DECL_ATTR( ShowCursor, bool )
DECL_ATTR( FixedAspectRatio, bool ) DECL_ATTR( FixedAspectRatio, bool )
DECL_ATTR( SmoothScaling, bool )
DECL_ATTR( IntegerScaling, bool )
DECL_ATTR( LastMileScaling, bool )
/* <internal> */ /* <internal> */
Scene *getScreen() const; Scene *getScreen() const;