diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 5077c8203..d7a059fa2 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -265,7 +265,7 @@
Antialiasing Filter
Reduces the amount of aliasing, resulting in smoother edges. FXAA is extremely lightweight but more aggressive and lower quality. SMAA is heavier but is higher quality and more selective with the edges it smooths.
Output Scaling
- The method by which the game output is scaled to the screen. Nearest doesn\'t smooth visuals but causes uneven pixels at non-integer ratios. Bilinear smooths visuals but may cause aliasing when downscaling. Adaptive uses bilinear for upscaling, but uses area sampling to downscale without creating aliasing. FSR 1 is an upscaler which allows sharpening and allows better visuals without increasing the internal resolution. This should be used with antialiasing and a minimum internal resolution of 2x Native. Sharp Bilinear looks similar to nearest but is slightly smoothed to prevent uneven pixels at non-integer ratios.
+ The method by which the game output is scaled to the screen. Nearest doesn\'t smooth visuals but causes uneven pixels at non-integer ratios. Bilinear smooths visuals but may cause aliasing when downscaling. Adaptive uses bilinear for upscaling, but uses area sampling to downscale without creating aliasing. FSR 1 is an upscaler which allows sharpening and allows better visuals without increasing the internal resolution, but needs to be used with antialiasing and a minimum internal resolution of 2x Native. Sharp Bilinear looks similar to nearest but is slightly smoothed to prevent uneven pixels at non-integer ratios.
FSR Sharpness
Specifies the strength of the sharpening pass when using FSR
Delay Game Render Thread
diff --git a/src/video_core/host_shaders/scaling/FSR/ffx_fsr1.h b/src/video_core/host_shaders/scaling/FSR/ffx_fsr1.h
index 4e0b3d548..ae62d5a32 100644
--- a/src/video_core/host_shaders/scaling/FSR/ffx_fsr1.h
+++ b/src/video_core/host_shaders/scaling/FSR/ffx_fsr1.h
@@ -199,7 +199,7 @@ AF1 outputSizeInPixelsY){
con2[3]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY));
con3[0]=AU1_AF1(AF1_( 0.0)*ARcpF1(inputSizeInPixelsX));
con3[1]=AU1_AF1(AF1_( 4.0)*ARcpF1(inputSizeInPixelsY));
- con3[2]=con3[3]=0;}
+ con3[2]=con3[3]=0u;}
//If the an offset into the input image resource
A_STATIC void FsrEasuConOffset(
@@ -668,8 +668,8 @@ AF1 sharpness){
varAF2(hSharp)=initAF2(sharpness,sharpness);
con[0]=AU1_AF1(sharpness);
con[1]=AU1_AH2_AF2(hSharp);
- con[2]=0;
- con[3]=0;}
+ con[2]=0u;
+ con[3]=0u;}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//_____________________________________________________________/\_______________________________________________________________
diff --git a/src/video_core/host_shaders/scaling/opengl_area_sampling.frag b/src/video_core/host_shaders/scaling/opengl_area_sampling.frag
index f1991de10..9c9647e67 100644
--- a/src/video_core/host_shaders/scaling/opengl_area_sampling.frag
+++ b/src/video_core/host_shaders/scaling/opengl_area_sampling.frag
@@ -52,8 +52,8 @@ vec4 AreaSampling(sampler2D textureSampler, vec2 texCoords) {
avg_color += area_se * texelFetch(textureSampler, ivec2(f_end.x, f_end.y), 0);
// Determine the size of the pixel box.
- int x_range = int(f_end.x - f_beg.x - 0.5);
- int y_range = int(f_end.y - f_beg.y - 0.5);
+ int x_range = int(float(f_end.x) - float(f_beg.x) - 0.5);
+ int y_range = int(float(f_end.y) - float(f_beg.y) - 0.5);
// Accumulate top and bottom edge pixels.
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 5b99a272f..7ad1f8fab 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -124,6 +124,21 @@ RendererOpenGL::RendererOpenGL(Core::System& system, Pica::PicaCore& pica_,
RendererOpenGL::~RendererOpenGL() = default;
void RendererOpenGL::SwapBuffers() {
+#ifdef ANDROID
+ // On Android, if secondary_window is defined at all,
+ // it means we have a second display
+ if (secondary_window) {
+ usingSecondaryLayout = true;
+ } else {
+ usingSecondaryLayout = false;
+ }
+#else
+ if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) {
+ usingSecondaryLayout = true;
+ } else {
+ usingSecondaryLayout = false;
+ }
+#endif
system.perf_stats->StartSwap();
// Maintain the rasterizer's state as a priority
OpenGLState prev_state = OpenGLState::GetCurState();
@@ -138,6 +153,7 @@ void RendererOpenGL::SwapBuffers() {
render_window.SwapBuffers();
#else
const auto& main_layout = render_window.GetFramebufferLayout();
+ isSecondaryWindow = false;
RenderToMailbox(main_layout, render_window.mailbox, false);
#ifdef ANDROID
@@ -145,6 +161,7 @@ void RendererOpenGL::SwapBuffers() {
// it means we have a second display
if (secondary_window) {
const auto& secondary_layout = secondary_window->GetFramebufferLayout();
+ isSecondaryWindow = true;
RenderToMailbox(secondary_layout, secondary_window->mailbox, false);
secondary_window->PollEvents();
}
@@ -152,6 +169,7 @@ void RendererOpenGL::SwapBuffers() {
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) {
ASSERT(secondary_window);
const auto& secondary_layout = secondary_window->GetFramebufferLayout();
+ isSecondaryWindow = true;
RenderToMailbox(secondary_layout, secondary_window->mailbox, false);
secondary_window->PollEvents();
}
@@ -412,17 +430,25 @@ void RendererOpenGL::AllocatePPTextures(){
}
void RendererOpenGL::AllocateOutputSizeTextures(){
- for (int i = 0; i < intermediateOutputSizeTextures.size(); i++){
- if (currOutputScreenRects[i].GetHeight() != 0 && currOutputScreenRects[i].GetWidth() != 0){
- for (int j = 0; j < intermediateOutputSizeTextures[0].size(); j++){
- intermediateOutputSizeTextures[i][j].Release();
- intermediateOutputSizeTextures[i][j].Create();
- intermediateOutputSizeTextures[i][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currOutputScreenRects[i].GetWidth(), currOutputScreenRects[i].GetHeight());
+ for (int i = 0; i < intermediateOutputSizeTextures[isSecondaryWindow].size(); i++){
+ if (currOutputScreenRects[isSecondaryWindow][i].GetHeight() != 0 && currOutputScreenRects[isSecondaryWindow][i].GetWidth() != 0){
+ for (int j = 0; j < intermediateOutputSizeTextures[isSecondaryWindow][0].size(); j++){
+ intermediateOutputSizeTextures[isSecondaryWindow][i][j].Release();
+ intermediateOutputSizeTextures[isSecondaryWindow][i][j].Create();
+ intermediateOutputSizeTextures[isSecondaryWindow][i][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currOutputScreenRects[isSecondaryWindow][i].GetWidth(), currOutputScreenRects[isSecondaryWindow][i].GetHeight());
}
}
}
- LOG_INFO(Render_OpenGL, "Reallocated OutputSize Textures");
+ LOG_INFO(Render_OpenGL, "Reallocated OutputSize Textures. PrevRects:\n{}x{}\n{}x{}\n{}x{}\nCurrRects:\n{}x{}\n{}x{}\n{}x{}",
+ prevOutputScreenRects[isSecondaryWindow][0].GetWidth(), prevOutputScreenRects[isSecondaryWindow][0].GetHeight(),
+ prevOutputScreenRects[isSecondaryWindow][1].GetWidth(), prevOutputScreenRects[isSecondaryWindow][1].GetHeight(),
+ prevOutputScreenRects[isSecondaryWindow][2].GetWidth(), prevOutputScreenRects[isSecondaryWindow][2].GetHeight(),
+ currOutputScreenRects[isSecondaryWindow][0].GetWidth(), currOutputScreenRects[isSecondaryWindow][0].GetHeight(),
+ currOutputScreenRects[isSecondaryWindow][1].GetWidth(), currOutputScreenRects[isSecondaryWindow][1].GetHeight(),
+ currOutputScreenRects[isSecondaryWindow][2].GetWidth(), currOutputScreenRects[isSecondaryWindow][2].GetHeight()
+ );
+ //
}
/**
@@ -589,7 +615,9 @@ void RendererOpenGL::ReloadShader(Settings::StereoRenderOption render_3d) {
FSR_PASS_1_shader_frag_data += HostShaders::OPENGL_FSR_PASS1_PART3_FRAG;
FSR_PASS_1_shader.Create(HostShaders::OPENGL_FSR_PASS1_VERT, FSR_PASS_1_shader_frag_data);
- SharpBilinear_shader.Create(HostShaders::OPENGL_SHARPBILINEAR_VERT, HostShaders::OPENGL_SHARPBILINEAR_FRAG);
+ std::string SharpBilinear_shader_frag_data = fragment_shader_precision_OES;
+ SharpBilinear_shader_frag_data += HostShaders::OPENGL_SHARPBILINEAR_FRAG;
+ SharpBilinear_shader.Create(HostShaders::OPENGL_SHARPBILINEAR_VERT, SharpBilinear_shader_frag_data);
state.Apply();
if (render_3d == Settings::StereoRenderOption::Anaglyph ||
@@ -1085,7 +1113,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
state.viewport.width = screenWidth;
state.viewport.height = screenHeight;
state.Apply();
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[currOutputScreen][0].handle, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][0].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FSR_PASS_0_shader.handle;
state.Apply();
@@ -1104,12 +1132,12 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
state.viewport.width = screenWidth;
state.viewport.height = screenHeight;
state.Apply();
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[currOutputScreen][1].handle, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][1].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FSR_PASS_1_shader.handle;
state.Apply();
AttachUniforms();
- state.texture_units[0].texture_2d = intermediateOutputSizeTextures[currOutputScreen][0].handle;
+ state.texture_units[0].texture_2d = intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][0].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1f(uniform_fsr_sharpening, fsr_sharpening);
glUniform4f(uniform_o_resolution, screenWidth, screenHeight, 1.0f / screenWidth, 1.0f / screenHeight);
@@ -1129,7 +1157,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float scree
state.draw.shader_program = Present_shader.handle;
state.Apply();
AttachUniforms();
- state.texture_units[0].texture_2d = intermediateOutputSizeTextures[currOutputScreen][1].handle;
+ state.texture_units[0].texture_2d = intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][1].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 0);
@@ -1304,15 +1332,21 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
prevBottomTextureHeight = currBottomTextureHeight;
//Track Layout Changes
- currOutputScreenRects[0] = layout.top_screen;
- currOutputScreenRects[1] = layout.bottom_screen;
- currOutputScreenRects[2] = layout.additional_screen;
- if (currOutputScreenRects[0] != prevOutputScreenRects[0] || currOutputScreenRects[1] != prevOutputScreenRects[1] || currOutputScreenRects[2] != prevOutputScreenRects[2]){
- AllocateOutputSizeTextures();
+ currOutputScreenRects[isSecondaryWindow][0] = layout.top_screen;
+ currOutputScreenRects[isSecondaryWindow][1] = layout.bottom_screen;
+ currOutputScreenRects[isSecondaryWindow][2] = layout.additional_screen;
+ if (currOutputScreenRects[isSecondaryWindow][0] != prevOutputScreenRects[isSecondaryWindow][0] || currOutputScreenRects[isSecondaryWindow][1] != prevOutputScreenRects[isSecondaryWindow][1]){
+ if (layout.additional_screen_enabled){
+ if (currOutputScreenRects[isSecondaryWindow][2] != prevOutputScreenRects[isSecondaryWindow][2]){
+ AllocateOutputSizeTextures();
+ }
+ } else {
+ AllocateOutputSizeTextures();
+ }
}
- prevOutputScreenRects[0] = currOutputScreenRects[0];
- prevOutputScreenRects[1] = currOutputScreenRects[1];
- prevOutputScreenRects[2] = currOutputScreenRects[2];
+ prevOutputScreenRects[isSecondaryWindow][0] = currOutputScreenRects[isSecondaryWindow][0];
+ prevOutputScreenRects[isSecondaryWindow][1] = currOutputScreenRects[isSecondaryWindow][1];
+ prevOutputScreenRects[isSecondaryWindow][2] = currOutputScreenRects[isSecondaryWindow][2];
//Set the Viewport
state.viewport.x = 0;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 455959651..da759782f 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -116,9 +116,9 @@ private:
std::array antialiasFBOTexture;
//Intermediate Textures at output size. These are for Top Screen, Bottom Screen and Additional Screen Respectively
- std::array, 3> intermediateOutputSizeTextures;
- std::array, 3> prevOutputScreenRects;
- std::array, 3> currOutputScreenRects;
+ std::array, 3>, 2> intermediateOutputSizeTextures;
+ std::array, 3>, 2> prevOutputScreenRects;
+ std::array, 3>, 2> currOutputScreenRects;
int currOutputScreen;
OGLTexture areatex;
OGLTexture searchtex;
@@ -158,6 +158,11 @@ private:
float currBottomTextureWidth;
float currBottomTextureHeight;
std::array originalViewport;
+
+ // Secondary Layout Fix
+ bool usingSecondaryLayout;
+ bool isSecondaryWindow;
+
};
} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index b0c82a1a4..d1bf2e26f 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -2057,10 +2057,19 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
currOutputScreenRects[0] = layout.top_screen;
currOutputScreenRects[1] = layout.bottom_screen;
currOutputScreenRects[2] = layout.additional_screen;
- if (currOutputScreenRects[0] != prevOutputScreenRects[0] || currOutputScreenRects[1] != prevOutputScreenRects[1] || currOutputScreenRects[2] != prevOutputScreenRects[2]){
- AllocateOutputSizeTextures();
- CreateOutputSizeTextureFramebuffers();
+
+ if (currOutputScreenRects[0] != prevOutputScreenRects[0] || currOutputScreenRects[1] != prevOutputScreenRects[1]){
+ if (layout.additional_screen_enabled){
+ if (currOutputScreenRects[2] != prevOutputScreenRects[2]){
+ AllocateOutputSizeTextures();
+ CreateOutputSizeTextureFramebuffers();
+ }
+ } else {
+ AllocateOutputSizeTextures();
+ CreateOutputSizeTextureFramebuffers();
+ }
}
+
prevOutputScreenRects[0] = currOutputScreenRects[0];
prevOutputScreenRects[1] = currOutputScreenRects[1];
prevOutputScreenRects[2] = currOutputScreenRects[2];
diff --git a/src/video_core/shader/generator/glsl_shader_gen.h b/src/video_core/shader/generator/glsl_shader_gen.h
index 5d6ad8661..e316bf78d 100644
--- a/src/video_core/shader/generator/glsl_shader_gen.h
+++ b/src/video_core/shader/generator/glsl_shader_gen.h
@@ -6,15 +6,17 @@
// High precision may or may not be supported in GLES3. If it isn't, use medium precision instead.
static constexpr char fragment_shader_precision_OES[] = R"(
-#if GL_ES
+#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp int;
precision highp float;
+precision highp sampler2D;
precision highp samplerBuffer;
precision highp uimage2D;
#else
precision mediump int;
precision mediump float;
+precision highp sampler2D;
precision mediump samplerBuffer;
precision mediump uimage2D;
#endif // GL_FRAGMENT_PRECISION_HIGH