diff --git a/externals/cmake-modules/FindSDL2.cmake b/externals/cmake-modules/FindSDL2.cmake
index 22ce752c5..fd70a1513 100644
--- a/externals/cmake-modules/FindSDL2.cmake
+++ b/externals/cmake-modules/FindSDL2.cmake
@@ -128,6 +128,7 @@ SET(SDL2_SEARCH_PATHS
     /Library/Frameworks
     /usr/local
     /usr
+    /usr/include/SDL # Budgie
     /sw # Fink
     /opt/local # DarwinPorts
     /opt/csw # Blastwave
diff --git a/externals/microprofile/microprofile.h b/externals/microprofile/microprofile.h
index 384863ccc..b5035443a 100644
--- a/externals/microprofile/microprofile.h
+++ b/externals/microprofile/microprofile.h
@@ -243,6 +243,7 @@ typedef uint32_t ThreadIdType;
 #define MICROPROFILE_DEFINE_GPU(var, name, color) MicroProfileToken g_mp_##var = MicroProfileGetToken("GPU", name, color, MicroProfileTokenTypeGpu)
 #define MICROPROFILE_TOKEN_PASTE0(a, b) a ## b
 #define MICROPROFILE_TOKEN_PASTE(a, b)  MICROPROFILE_TOKEN_PASTE0(a,b)
+#define MICROPROFILE_TOKEN(var) g_mp_##var
 #define MICROPROFILE_SCOPE(var) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(g_mp_##var)
 #define MICROPROFILE_SCOPE_TOKEN(token) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(token)
 #define MICROPROFILE_SCOPEI(group, name, color) static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__) = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeCpu); MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo,__LINE__)( MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__))
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 14b41207a..03b70d2a8 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -76,6 +76,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
     }
 
     if (GDBStub::IsServerEnabled()) {
+        Kernel::Thread* thread = kernel->GetCurrentThreadManager().GetCurrentThread();
+        if (thread && running_core) {
+            running_core->SaveContext(thread->context);
+        }
         GDBStub::HandlePacket();
 
         // If the loop is halted and we want to step, use a tiny (1) number of instructions to
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 1e42ff5e4..8093e7328 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -1264,10 +1264,9 @@ void SendTrap(Kernel::Thread* thread, int trap) {
         return;
     }
 
-    if (!halt_loop || current_thread == thread) {
-        current_thread = thread;
-        SendSignal(thread, trap);
-    }
+    current_thread = thread;
+    SendSignal(thread, trap);
+
     halt_loop = true;
     send_trap = false;
 }
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index eb76050fc..a6f9860eb 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -58,6 +58,8 @@ add_library(video_core STATIC
     renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h
     renderer_opengl/texture_filters/bicubic/bicubic.cpp
     renderer_opengl/texture_filters/bicubic/bicubic.h
+    renderer_opengl/texture_filters/scale_force/scale_force.cpp
+    renderer_opengl/texture_filters/scale_force/scale_force.h
     renderer_opengl/texture_filters/texture_filter_base.h
     renderer_opengl/texture_filters/texture_filterer.cpp
     renderer_opengl/texture_filters/texture_filterer.h
@@ -103,6 +105,7 @@ set(SHADER_FILES
     renderer_opengl/texture_filters/anime4k/y_gradient.frag
     renderer_opengl/texture_filters/anime4k/y_gradient.vert
     renderer_opengl/texture_filters/bicubic/bicubic.frag
+    renderer_opengl/texture_filters/scale_force/scale_force.frag
     renderer_opengl/texture_filters/tex_coord.vert
     renderer_opengl/texture_filters/xbrz/xbrz_freescale.frag
     renderer_opengl/texture_filters/xbrz/xbrz_freescale.vert
diff --git a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp
new file mode 100644
index 000000000..b2d085445
--- /dev/null
+++ b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.cpp
@@ -0,0 +1,47 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
+#include "video_core/renderer_opengl/texture_filters/scale_force/scale_force.h"
+
+#include "shaders/scale_force.frag"
+#include "shaders/tex_coord.vert"
+
+namespace OpenGL {
+
+ScaleForce::ScaleForce(u16 scale_factor) : TextureFilterBase(scale_factor) {
+    program.Create(tex_coord_vert.data(), scale_force_frag.data());
+    vao.Create();
+    src_sampler.Create();
+
+    state.draw.shader_program = program.handle;
+    state.draw.vertex_array = vao.handle;
+    state.draw.shader_program = program.handle;
+    state.texture_units[0].sampler = src_sampler.handle;
+
+    glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+void ScaleForce::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
+                        const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle,
+                        GLuint draw_fb_handle) {
+    const OpenGLState cur_state = OpenGLState::GetCurState();
+    state.texture_units[0].texture_2d = src_tex;
+    state.draw.draw_framebuffer = draw_fb_handle;
+    state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
+                      static_cast<GLsizei>(dst_rect.GetWidth()),
+                      static_cast<GLsizei>(dst_rect.GetHeight())};
+    state.Apply();
+
+    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0);
+    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+    cur_state.Apply();
+}
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag
new file mode 100644
index 000000000..884723487
--- /dev/null
+++ b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.frag
@@ -0,0 +1,112 @@
+//? #version 320 es
+
+// from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force
+// shader adapted to GLSL 320 es and debugging outputs stripped
+
+// MIT License
+//
+// Copyright (c) 2020 BreadFish64
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+precision mediump float;
+
+in highp vec2 tex_coord;
+
+out mediump vec4 frag_color;
+
+uniform sampler2D input_texture;
+
+vec4 cubic(float v) {
+    vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
+    vec4 s = n * n * n;
+    float x = s.x;
+    float y = s.y - 4.0 * s.x;
+    float z = s.z - 4.0 * s.y + 6.0 * s.x;
+    float w = 6.0 - x - y - z;
+    return vec4(x, y, z, w) / 6.0;
+}
+
+vec4 textureBicubic(sampler2D sampler, vec2 tex_coords) {
+    vec2 tex_size = vec2(textureSize(sampler, 0));
+    vec2 inv_tex_size = 1.0 / tex_size;
+
+    tex_coords = tex_coords * tex_size - 0.5;
+
+    vec2 fxy = fract(tex_coords);
+    tex_coords -= fxy;
+
+    vec4 xcubic = cubic(fxy.x);
+    vec4 ycubic = cubic(fxy.y);
+
+    vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy;
+
+    vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw);
+    vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s;
+
+    offset *= inv_tex_size.xxyy;
+
+    vec4 sample0 = texture(sampler, offset.xz);
+    vec4 sample1 = texture(sampler, offset.yz);
+    vec4 sample2 = texture(sampler, offset.xw);
+    vec4 sample3 = texture(sampler, offset.yw);
+
+    float sx = s.x / (s.x + s.y);
+    float sy = s.z / (s.z + s.w);
+
+    return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy);
+}
+
+float ColorDist(vec4 a, vec4 b) {
+    // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion
+    const vec3 K = vec3(0.2627, 0.6780, 0.0593);
+    const float luminance_weight = .6;
+    const mat3 MATRIX = mat3(K * luminance_weight, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b),
+                             .5, .5, -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r));
+    vec4 diff = a - b;
+    vec3 YCbCr = diff.rgb * MATRIX;
+    float d = length(YCbCr) * length(vec3(1.0)) / length(vec3(luminance_weight, 1.0, 1.0));
+    return sqrt(a.a * b.a * d * d + diff.a * diff.a);
+}
+
+const int radius = 2;
+
+void main() {
+    vec2 input_size = vec2(textureSize(input_texture, 0));
+    vec4 center_texel = texture(input_texture, tex_coord);
+    vec2 final_offset = vec2(0.0);
+    float total_diff = 0.0;
+
+    for (int y = -radius; y <= radius; ++y) {
+        for (int x = -radius; x <= radius; ++x) {
+            if (0 == (x | y))
+                continue;
+            vec2 offset = vec2(x, y);
+            float weight = pow(length(offset), -length(offset));
+            vec4 texel = texture(input_texture, tex_coord + offset / input_size);
+            float diff = ColorDist(texel, center_texel) * weight;
+            total_diff += diff;
+            final_offset += diff * offset;
+        }
+    }
+
+    float clamp_val = length(final_offset) / total_diff;
+    final_offset = clamp(final_offset, -clamp_val, clamp_val);
+    frag_color = textureBicubic(input_texture, tex_coord - final_offset / input_size);
+}
diff --git a/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.h b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.h
new file mode 100644
index 000000000..d877b3205
--- /dev/null
+++ b/src/video_core/renderer_opengl/texture_filters/scale_force/scale_force.h
@@ -0,0 +1,29 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_state.h"
+#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h"
+
+namespace OpenGL {
+
+class ScaleForce : public TextureFilterBase {
+public:
+    static constexpr std::string_view NAME = "ScaleForce";
+
+    explicit ScaleForce(u16 scale_factor);
+    void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
+                const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle,
+                GLuint draw_fb_handle) override;
+
+private:
+    OpenGLState state{};
+    OGLProgram program{};
+    OGLVertexArray vao{};
+    OGLSampler src_sampler{};
+};
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp b/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp
index 0afa6b876..204cb8b98 100644
--- a/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp
+++ b/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp
@@ -8,6 +8,7 @@
 #include "common/logging/log.h"
 #include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h"
 #include "video_core/renderer_opengl/texture_filters/bicubic/bicubic.h"
+#include "video_core/renderer_opengl/texture_filters/scale_force/scale_force.h"
 #include "video_core/renderer_opengl/texture_filters/texture_filter_base.h"
 #include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
 #include "video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h"
@@ -27,6 +28,7 @@ static const std::unordered_map<std::string_view, TextureFilterContructor> filte
     {TextureFilterer::NONE, [](u16) { return nullptr; }},
     FilterMapPair<Anime4kUltrafast>(),
     FilterMapPair<Bicubic>(),
+    FilterMapPair<ScaleForce>(),
     FilterMapPair<XbrzFreescale>(),
 };