diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp
index 4e9d80d10..5f6221b9b 100644
--- a/src/video_core/renderer_opengl/gl_blit_screen.cpp
+++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp
@@ -75,8 +75,6 @@ BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
         CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG),
                       GL_FRAGMENT_SHADER);
 
-    fsr = std::make_unique<FSR>();
-
     // Generate presentation sampler
     present_sampler.Create();
     glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -269,7 +267,7 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer,
     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
     glDepthRangeIndexed(0, 0.0, 0.0);
 
-    glBindTextureUnit(0, info.display_texture);
+    GLuint texture = info.display_texture;
 
     auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
     if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) {
@@ -296,10 +294,10 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer,
 
         switch (anti_aliasing) {
         case Settings::AntiAliasing::Fxaa: {
-            glBindTextureUnit(0, fxaa->Draw(program_manager, info.display_texture));
+            texture = fxaa->Draw(program_manager, info.display_texture);
         } break;
         case Settings::AntiAliasing::Smaa: {
-            glBindTextureUnit(0, smaa->Draw(program_manager, info.display_texture));
+            texture = smaa->Draw(program_manager, info.display_texture);
         } break;
         default:
             UNREACHABLE();
@@ -311,34 +309,37 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer,
     glDisablei(GL_SCISSOR_TEST, 0);
 
     if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
-        if (!fsr->AreBuffersInitialized()) {
-            fsr->InitBuffers();
+        GLint old_read_fb;
+        GLint old_draw_fb;
+        glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
+        glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
+
+        if (!fsr || fsr->NeedsRecreation(layout.screen)) {
+            fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
         }
 
-        glBindSampler(0, present_sampler.handle);
-        fsr->Draw(program_manager, layout.screen, info.scaled_width, info.scaled_height, crop);
-    } else {
-        if (fsr->AreBuffersInitialized()) {
-            fsr->ReleaseBuffers();
-        }
+        texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop);
+
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
     }
 
+    glBindTextureUnit(0, texture);
+
     const std::array ortho_matrix =
         MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
 
     const auto fragment_handle = [this]() {
         switch (Settings::values.scaling_filter.GetValue()) {
-        case Settings::ScalingFilter::NearestNeighbor:
-        case Settings::ScalingFilter::Bilinear:
-            return present_bilinear_fragment.handle;
         case Settings::ScalingFilter::Bicubic:
             return present_bicubic_fragment.handle;
         case Settings::ScalingFilter::Gaussian:
             return present_gaussian_fragment.handle;
         case Settings::ScalingFilter::ScaleForce:
             return present_scaleforce_fragment.handle;
+        case Settings::ScalingFilter::NearestNeighbor:
+        case Settings::ScalingFilter::Bilinear:
         case Settings::ScalingFilter::Fsr:
-            return fsr->GetPresentFragmentProgram().handle;
         default:
             return present_bilinear_fragment.handle;
         }
diff --git a/src/video_core/renderer_opengl/present/fsr.cpp b/src/video_core/renderer_opengl/present/fsr.cpp
index a5540bb0c..b764aadae 100644
--- a/src/video_core/renderer_opengl/present/fsr.cpp
+++ b/src/video_core/renderer_opengl/present/fsr.cpp
@@ -19,7 +19,7 @@ using namespace FSR;
 
 using FsrConstants = std::array<u32, 4 * 4>;
 
-FSR::FSR() {
+FSR::FSR(u32 output_width_, u32 output_height_) : width(output_width_), height(output_height_) {
     std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
     ReplaceInclude(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
     ReplaceInclude(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
@@ -29,94 +29,70 @@ FSR::FSR() {
     ReplaceInclude(fsr_easu_source, "opengl_fidelityfx_fsr.frag", fsr_source);
     ReplaceInclude(fsr_rcas_source, "opengl_fidelityfx_fsr.frag", fsr_source);
 
-    fsr_vertex = CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER);
-    fsr_easu_frag = CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER);
-    fsr_rcas_frag = CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER);
+    vert = CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER);
+    easu_frag = CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER);
+    rcas_frag = CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER);
 
-    glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f);
-    glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f);
+    glProgramUniform2f(vert.handle, 0, 1.0f, -1.0f);
+    glProgramUniform2f(vert.handle, 1, 0.0f, 1.0f);
+
+    sampler = CreateBilinearSampler();
+    framebuffer.Create();
+
+    easu_tex.Create(GL_TEXTURE_2D);
+    glTextureStorage2D(easu_tex.handle, 1, GL_RGBA16F, width, height);
+
+    rcas_tex.Create(GL_TEXTURE_2D);
+    glTextureStorage2D(rcas_tex.handle, 1, GL_RGBA16F, width, height);
 }
 
 FSR::~FSR() = default;
 
-void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
-               u32 input_image_width, u32 input_image_height,
-               const Common::Rectangle<f32>& crop_rect) {
-
-    const auto output_image_width = screen.GetWidth();
-    const auto output_image_height = screen.GetHeight();
-
-    if (fsr_intermediate_tex.handle) {
-        GLint fsr_tex_width, fsr_tex_height;
-        glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH,
-                                     &fsr_tex_width);
-        glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT,
-                                     &fsr_tex_height);
-        if (static_cast<u32>(fsr_tex_width) != output_image_width ||
-            static_cast<u32>(fsr_tex_height) != output_image_height) {
-            fsr_intermediate_tex.Release();
-        }
-    }
-    if (!fsr_intermediate_tex.handle) {
-        fsr_intermediate_tex.Create(GL_TEXTURE_2D);
-        glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width,
-                           output_image_height);
-        glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0,
-                                  fsr_intermediate_tex.handle, 0);
-    }
-
-    GLint old_draw_fb;
-    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
-
-    glFrontFace(GL_CW);
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle);
-    glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width),
-                       static_cast<GLfloat>(output_image_height));
-
+GLuint FSR::Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
+                 u32 input_image_height, const Common::Rectangle<f32>& crop_rect) {
     const f32 input_width = static_cast<f32>(input_image_width);
     const f32 input_height = static_cast<f32>(input_image_height);
-    const f32 output_width = static_cast<f32>(screen.GetWidth());
-    const f32 output_height = static_cast<f32>(screen.GetHeight());
+    const f32 output_width = static_cast<f32>(width);
+    const f32 output_height = static_cast<f32>(height);
     const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_width;
     const f32 viewport_x = crop_rect.left * input_width;
     const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_height;
     const f32 viewport_y = crop_rect.top * input_height;
 
-    FsrConstants constants;
-    FsrEasuConOffset(constants.data() + 0, constants.data() + 4, constants.data() + 8,
-                     constants.data() + 12, viewport_width, viewport_height, input_width,
+    FsrConstants easu_con{};
+    FsrConstants rcas_con{};
+
+    FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8,
+                     easu_con.data() + 12, viewport_width, viewport_height, input_width,
                      input_height, output_width, output_height, viewport_x, viewport_y);
 
-    glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants));
-
-    program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle);
-    glDrawArrays(GL_TRIANGLES, 0, 3);
-
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
-    glBindTextureUnit(0, fsr_intermediate_tex.handle);
-
     const float sharpening =
         static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
 
-    FsrRcasCon(constants.data(), sharpening);
-    glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants));
+    FsrRcasCon(rcas_con.data(), sharpening);
+
+    glProgramUniform4uiv(easu_frag.handle, 0, sizeof(easu_con), easu_con.data());
+    glProgramUniform4uiv(rcas_frag.handle, 0, sizeof(rcas_con), rcas_con.data());
+
+    glFrontFace(GL_CW);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
+    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, easu_tex.handle, 0);
+    glViewportIndexedf(0, 0.0f, 0.0f, output_width, output_height);
+    program_manager.BindPresentPrograms(vert.handle, easu_frag.handle);
+    glBindTextureUnit(0, texture);
+    glBindSampler(0, sampler.handle);
+    glDrawArrays(GL_TRIANGLES, 0, 3);
+
+    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, rcas_tex.handle, 0);
+    program_manager.BindPresentPrograms(vert.handle, rcas_frag.handle);
+    glBindTextureUnit(0, easu_tex.handle);
+    glDrawArrays(GL_TRIANGLES, 0, 3);
+
+    return rcas_tex.handle;
 }
 
-void FSR::InitBuffers() {
-    fsr_framebuffer.Create();
-}
-
-void FSR::ReleaseBuffers() {
-    fsr_framebuffer.Release();
-    fsr_intermediate_tex.Release();
-}
-
-const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept {
-    return fsr_rcas_frag;
-}
-
-bool FSR::AreBuffersInitialized() const noexcept {
-    return fsr_framebuffer.handle;
+bool FSR::NeedsRecreation(const Common::Rectangle<u32>& screen) {
+    return screen.GetWidth() != width || screen.GetHeight() != height;
 }
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fsr.h b/src/video_core/renderer_opengl/present/fsr.h
index fa57c6f00..606935a01 100644
--- a/src/video_core/renderer_opengl/present/fsr.h
+++ b/src/video_core/renderer_opengl/present/fsr.h
@@ -16,27 +16,24 @@ class ProgramManager;
 
 class FSR {
 public:
-    explicit FSR();
+    explicit FSR(u32 output_width, u32 output_height);
     ~FSR();
 
-    void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
-              u32 input_image_width, u32 input_image_height,
-              const Common::Rectangle<f32>& crop_rect);
+    GLuint Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
+                u32 input_image_height, const Common::Rectangle<f32>& crop_rect);
 
-    void InitBuffers();
-
-    void ReleaseBuffers();
-
-    [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept;
-
-    [[nodiscard]] bool AreBuffersInitialized() const noexcept;
+    bool NeedsRecreation(const Common::Rectangle<u32>& screen);
 
 private:
-    OGLFramebuffer fsr_framebuffer;
-    OGLProgram fsr_vertex;
-    OGLProgram fsr_easu_frag;
-    OGLProgram fsr_rcas_frag;
-    OGLTexture fsr_intermediate_tex;
+    const u32 width;
+    const u32 height;
+    OGLFramebuffer framebuffer;
+    OGLSampler sampler;
+    OGLProgram vert;
+    OGLProgram easu_frag;
+    OGLProgram rcas_frag;
+    OGLTexture easu_tex;
+    OGLTexture rcas_tex;
 };
 
 } // namespace OpenGL