Integer Scaling + Screen-Level Linear Interpolation Options (#1976)
This commit is contained in:
parent
7e7f645f37
commit
71341a27e4
|
@ -99,6 +99,8 @@ Option<int> SkipFrame("ta.skip");
|
|||
Option<int> MaxThreads("pvr.MaxThreads", 3);
|
||||
Option<int> AutoSkipFrame("pvr.AutoSkipFrame", 0);
|
||||
Option<int> RenderResolution("rend.Resolution", 480);
|
||||
Option<bool> IntegerScale("rend.IntegerScale", false);
|
||||
Option<bool> LinearInterpolation("rend.LinearInterpolation", true);
|
||||
Option<bool> VSync("rend.vsync", true);
|
||||
Option<int64_t> PixelBufferSize("rend.PixelBufferSize", 512_MB);
|
||||
Option<int> AnisotropicFiltering("rend.AnisotropicFiltering", 1);
|
||||
|
|
|
@ -460,6 +460,8 @@ extern Option<int> SkipFrame;
|
|||
extern Option<int> MaxThreads;
|
||||
extern Option<int> AutoSkipFrame; // 0: none, 1: some, 2: more
|
||||
extern Option<int> RenderResolution;
|
||||
extern Option<bool> IntegerScale;
|
||||
extern Option<bool> LinearInterpolation;
|
||||
extern Option<bool> VSync;
|
||||
extern Option<int64_t> PixelBufferSize;
|
||||
extern Option<int> AnisotropicFiltering;
|
||||
|
|
|
@ -559,14 +559,12 @@ void DX11Renderer::displayFramebuffer()
|
|||
std::swap(shiftX, shiftY);
|
||||
renderAR = 1 / renderAR;
|
||||
}
|
||||
float screenAR = (float)outwidth / outheight;
|
||||
|
||||
int dy = 0;
|
||||
int dx = 0;
|
||||
if (renderAR > screenAR)
|
||||
dy = (int)roundf(outheight * (1 - screenAR / renderAR) / 2.f);
|
||||
else
|
||||
dx = (int)roundf(outwidth * (1 - renderAR / screenAR) / 2.f);
|
||||
|
||||
// handles the rotation on its own, so never pass config::Rotate90
|
||||
getWindowboxDimensions(outwidth, outheight, renderAR, dx, dy, false);
|
||||
|
||||
float x = (float)dx;
|
||||
float y = (float)dy;
|
||||
float w = (float)(outwidth - 2 * dx);
|
||||
|
@ -581,7 +579,7 @@ void DX11Renderer::displayFramebuffer()
|
|||
x += shiftX;
|
||||
y += shiftY;
|
||||
deviceContext->OMSetBlendState(blendStates.getState(false), nullptr, 0xffffffff);
|
||||
quad->draw(fbTextureView, samplers->getSampler(config::TextureFiltering != 1), nullptr, x, y, w, h, config::Rotate90);
|
||||
quad->draw(fbTextureView, samplers->getSampler(config::LinearInterpolation), nullptr, x, y, w, h, config::Rotate90);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ void D3DRenderer::RenderFramebuffer(const FramebufferInfo& info)
|
|||
{
|
||||
ReadFramebuffer<BGRAPacker>(info, pb, width, height);
|
||||
}
|
||||
|
||||
|
||||
if (dcfbTexture)
|
||||
{
|
||||
D3DSURFACE_DESC desc;
|
||||
|
@ -1216,13 +1216,10 @@ void D3DRenderer::displayFramebuffer()
|
|||
{
|
||||
devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
|
||||
device->ColorFill(backbuffer, 0, D3DCOLOR_ARGB(255, VO_BORDER_COL._red, VO_BORDER_COL._green, VO_BORDER_COL._blue));
|
||||
float screenAR = (float)settings.display.width / settings.display.height;
|
||||
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
if (aspectRatio > screenAR)
|
||||
dy = (int)roundf(settings.display.height * (1 - screenAR / aspectRatio) / 2.f);
|
||||
else
|
||||
dx = (int)roundf(settings.display.width * (1 - aspectRatio / screenAR) / 2.f);
|
||||
getWindowboxDimensions(settings.display.width, settings.display.height, aspectRatio, dx, dy, config::Rotate90);
|
||||
|
||||
float shiftX, shiftY;
|
||||
getVideoShift(shiftX, shiftY);
|
||||
|
@ -1231,7 +1228,7 @@ void D3DRenderer::displayFramebuffer()
|
|||
RECT rs { 0, 0, (long)width, (long)height };
|
||||
RECT rd { dx, dy, settings.display.width - dx, settings.display.height - dy };
|
||||
device->StretchRect(framebufferSurface, &rs, backbuffer, &rd,
|
||||
config::TextureFiltering == 1 ? D3DTEXF_POINT : D3DTEXF_LINEAR); // This can fail if window is minimized
|
||||
config::LinearInterpolation ? D3DTEXF_LINEAR : D3DTEXF_POINT); // This can fail if window is minimized
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1241,8 +1238,8 @@ void D3DRenderer::displayFramebuffer()
|
|||
device->SetRenderState(D3DRS_ZENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
|
||||
device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
|
||||
device->SetSamplerState(0, D3DSAMP_MINFILTER, config::TextureFiltering == 1 ? D3DTEXF_POINT : D3DTEXF_LINEAR);
|
||||
device->SetSamplerState(0, D3DSAMP_MAGFILTER, config::TextureFiltering == 1 ? D3DTEXF_POINT : D3DTEXF_LINEAR);
|
||||
device->SetSamplerState(0, D3DSAMP_MINFILTER, config::LinearInterpolation ? D3DTEXF_LINEAR : D3DTEXF_POINT);
|
||||
device->SetSamplerState(0, D3DSAMP_MAGFILTER, config::LinearInterpolation ? D3DTEXF_LINEAR : D3DTEXF_POINT);
|
||||
|
||||
glm::mat4 identity = glm::identity<glm::mat4>();
|
||||
glm::mat4 projection = glm::translate(glm::vec3(-1.f / settings.display.width, 1.f / settings.display.height, 0));
|
||||
|
|
|
@ -777,17 +777,11 @@ bool OpenGLRenderer::renderLastFrame()
|
|||
GlFramebuffer *framebuffer = gl.ofbo2.ready ? gl.ofbo2.framebuffer.get() : gl.ofbo.framebuffer.get();
|
||||
if (framebuffer == nullptr)
|
||||
return false;
|
||||
|
||||
glcache.Disable(GL_SCISSOR_TEST);
|
||||
float screenAR = (float)settings.display.width / settings.display.height;
|
||||
float renderAR = gl.ofbo.aspectRatio;
|
||||
|
||||
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
if (renderAR > screenAR)
|
||||
dy = (int)roundf(settings.display.height * (1 - screenAR / renderAR) / 2.f);
|
||||
else
|
||||
dx = (int)roundf(settings.display.width * (1 - renderAR / screenAR) / 2.f);
|
||||
glcache.Disable(GL_SCISSOR_TEST);
|
||||
getWindowboxDimensions(settings.display.width, settings.display.height, gl.ofbo.aspectRatio, dx, dy, config::Rotate90);
|
||||
|
||||
if (gl.bogusBlitFramebuffer || config::Rotate90)
|
||||
{
|
||||
|
@ -795,8 +789,8 @@ bool OpenGLRenderer::renderLastFrame()
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
|
||||
glcache.ClearColor(VO_BORDER_COL.red(), VO_BORDER_COL.green(), VO_BORDER_COL.blue(), 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, config::TextureFiltering == 1 ? GL_NEAREST : GL_LINEAR);
|
||||
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, config::TextureFiltering == 1 ? GL_NEAREST : GL_LINEAR);
|
||||
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, config::LinearInterpolation ? GL_LINEAR : GL_NEAREST);
|
||||
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, config::LinearInterpolation ? GL_LINEAR : GL_NEAREST);
|
||||
float *vertices = nullptr;
|
||||
if (gl.ofbo.shiftX != 0 || gl.ofbo.shiftY != 0)
|
||||
{
|
||||
|
@ -824,7 +818,7 @@ bool OpenGLRenderer::renderLastFrame()
|
|||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glBlitFramebuffer(-gl.ofbo.shiftX, -gl.ofbo.shiftY, framebuffer->getWidth() - gl.ofbo.shiftX, framebuffer->getHeight() - gl.ofbo.shiftY,
|
||||
dx, settings.display.height - dy, settings.display.width - dx, dy,
|
||||
GL_COLOR_BUFFER_BIT, config::TextureFiltering == 1 ? GL_NEAREST : GL_LINEAR);
|
||||
GL_COLOR_BUFFER_BIT, config::LinearInterpolation ? GL_LINEAR : GL_NEAREST);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -284,6 +284,33 @@ inline static float getDCFramebufferAspectRatio()
|
|||
return aspectRatio * config::ScreenStretching / 100.f;
|
||||
}
|
||||
|
||||
inline static void getWindowboxDimensions(int outwidth, int outheight, float renderAR, int& dx, int& dy, bool rotate) {
|
||||
if (config::IntegerScale) {
|
||||
int fbh = config::RenderResolution;
|
||||
int fbw = (int)((rotate ? 1 / renderAR : renderAR) * fbh);
|
||||
if (rotate)
|
||||
std::swap(fbw, fbh);
|
||||
|
||||
int scale = std::min(outwidth / fbw, outheight / fbh);
|
||||
if (scale == 0) {
|
||||
scale = std::max(fbw / outwidth, fbh / outheight) + 1;
|
||||
dx = (outwidth - fbw / scale) / 2;
|
||||
dy = (outheight - fbh / scale) / 2;
|
||||
}
|
||||
else {
|
||||
dx = (outwidth - fbw * scale) / 2;
|
||||
dy = (outheight - fbh * scale) / 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
float screenAR = (float)outwidth / outheight;
|
||||
if (renderAR > screenAR)
|
||||
dy = (int)roundf(outheight * (1 - screenAR / renderAR) / 2.f);
|
||||
else
|
||||
dx = (int)roundf(outwidth * (1 - renderAR / screenAR) / 2.f);
|
||||
}
|
||||
}
|
||||
|
||||
inline static void getVideoShift(float& x, float& y)
|
||||
{
|
||||
const bool vga = FB_R_CTRL.vclk_div == 1;
|
||||
|
|
|
@ -1007,21 +1007,17 @@ void VulkanContext::DrawFrame(vk::ImageView imageView, const vk::Extent2D& exten
|
|||
else
|
||||
quadPipeline->BindPipeline(commandBuffer);
|
||||
|
||||
float screenAR = (float)width / height;
|
||||
float dx = 0;
|
||||
float dy = 0;
|
||||
if (aspectRatio > screenAR)
|
||||
dy = height * (1 - screenAR / aspectRatio) / 2;
|
||||
else
|
||||
dx = width * (1 - aspectRatio / screenAR) / 2;
|
||||
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
getWindowboxDimensions(width, height, aspectRatio, dx, dy, config::Rotate90);
|
||||
|
||||
vk::Viewport viewport(dx, dy, width - dx * 2, height - dy * 2);
|
||||
commandBuffer.setViewport(0, viewport);
|
||||
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(dx, dy), vk::Extent2D(width - dx * 2, height - dy * 2)));
|
||||
if (config::Rotate90)
|
||||
quadRotateDrawer->Draw(commandBuffer, imageView, vtx, config::TextureFiltering == 1);
|
||||
quadRotateDrawer->Draw(commandBuffer, imageView, vtx, !config::LinearInterpolation);
|
||||
else
|
||||
quadDrawer->Draw(commandBuffer, imageView, vtx, config::TextureFiltering == 1);
|
||||
quadDrawer->Draw(commandBuffer, imageView, vtx, !config::LinearInterpolation);
|
||||
}
|
||||
|
||||
void VulkanContext::WaitIdle() const
|
||||
|
|
|
@ -2827,7 +2827,8 @@ static void gui_settings_video()
|
|||
ImGui::Text("Internal Resolution");
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("Internal render resolution. Higher is better, but more demanding on the GPU. Values higher than your display resolution (but no more than double your display resolution) can be used for supersampling, which provides high-quality antialiasing without reducing sharpness.");
|
||||
|
||||
OptionCheckbox("Integer Scaling", config::IntegerScale, "Scales the output by the maximum integer multiple allowed by the display resolution.");
|
||||
OptionCheckbox("Linear Interpolation", config::LinearInterpolation, "Scales the output with linear interpolation. Will use nearest neighbor interpolation otherwise. Disable with integer scaling.");
|
||||
#ifndef TARGET_IPHONE
|
||||
OptionCheckbox("VSync", config::VSync, "Synchronizes the frame rate with the screen refresh rate. Recommended");
|
||||
if (isVulkan(config::RendererType))
|
||||
|
@ -2854,11 +2855,11 @@ static void gui_settings_video()
|
|||
OptionCheckbox("Widescreen", config::Widescreen,
|
||||
"Draw geometry outside of the normal 4:3 aspect ratio. May produce graphical glitches in the revealed areas.\nAspect Fit and shows the full 16:9 content.");
|
||||
{
|
||||
DisabledScope scope(!config::Widescreen);
|
||||
DisabledScope scope(!config::Widescreen || config::IntegerScale);
|
||||
|
||||
ImGui::Indent();
|
||||
OptionCheckbox("Super Widescreen", config::SuperWidescreen,
|
||||
"Use the full width of the screen or window when its aspect ratio is greater than 16:9.\nAspect Fill and remove black bars.");
|
||||
"Use the full width of the screen or window when its aspect ratio is greater than 16:9.\nAspect Fill and remove black bars. Not compatible with integer scaling.");
|
||||
ImGui::Unindent();
|
||||
}
|
||||
OptionCheckbox("Widescreen Game Cheats", config::WidescreenGameHacks,
|
||||
|
|
|
@ -82,6 +82,8 @@ Option<int> SkipFrame(CORE_OPTION_NAME "_frame_skipping");
|
|||
Option<int> MaxThreads("", 3);
|
||||
Option<int> AutoSkipFrame(CORE_OPTION_NAME "_auto_skip_frame", 0);
|
||||
Option<int> RenderResolution("", 480);
|
||||
Option<bool> IntegerScale("");
|
||||
Option<bool> LinearInterpolation("", true);
|
||||
Option<bool> VSync("", true);
|
||||
Option<bool> ThreadedRendering(CORE_OPTION_NAME "_threaded_rendering", true);
|
||||
Option<int> AnisotropicFiltering(CORE_OPTION_NAME "_anisotropic_filtering");
|
||||
|
|
Loading…
Reference in New Issue