diff --git a/externals/fmt b/externals/fmt
index 7512a55aa..4b8f8fac9 160000
--- a/externals/fmt
+++ b/externals/fmt
@@ -1 +1 @@
-Subproject commit 7512a55aa3ae309587ca89668ef9ec4074a51a1f
+Subproject commit 4b8f8fac96a7819f28f4be523ca10a2d5d8aaaf2
diff --git a/src/citra/lodepng_image_interface.cpp b/src/citra/lodepng_image_interface.cpp
index e490f40a7..e537de4e4 100644
--- a/src/citra/lodepng_image_interface.cpp
+++ b/src/citra/lodepng_image_interface.cpp
@@ -26,4 +26,4 @@ bool LodePNGImageInterface::EncodePNG(const std::string& path, const std::vector
         return false;
     }
     return true;
-}
\ No newline at end of file
+}
diff --git a/src/citra/lodepng_image_interface.h b/src/citra/lodepng_image_interface.h
index 1c233961a..6880b10a0 100644
--- a/src/citra/lodepng_image_interface.h
+++ b/src/citra/lodepng_image_interface.h
@@ -11,4 +11,4 @@ public:
     bool DecodePNG(std::vector<u8>& dst, u32& width, u32& height, const std::string& path) override;
     bool EncodePNG(const std::string& path, const std::vector<u8>& src, u32 width,
                    u32 height) override;
-};
\ No newline at end of file
+};
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 8c6a12103..c75754fce 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -46,12 +46,9 @@ add_executable(citra-qt
     configuration/configure_camera.ui
     configuration/configure_debug.cpp
     configuration/configure_debug.h
-<<<<<<< HEAD
     configuration/configure_debug.ui
-=======
     configuration/configure_enhancements.cpp
     configuration/configure_enhancements.h
->>>>>>> 76ca777b... reorder graphics tab, move speed to general
     configuration/configure_dialog.cpp
     configuration/configure_dialog.h
     configuration/configure_general.cpp
@@ -160,38 +157,6 @@ add_executable(citra-qt
     util/spinbox.h
     util/util.cpp
     util/util.h
-<<<<<<< HEAD
-=======
-    compatdb.cpp
-    compatdb.h
-)
-
-set(UIS
-    configuration/configure.ui
-    configuration/configure_audio.ui
-    configuration/configure_camera.ui
-    configuration/configure_debug.ui
-    configuration/configure_enhancements.ui
-    configuration/configure_general.ui
-    configuration/configure_graphics.ui
-    configuration/configure_hotkeys.ui
-    configuration/configure_input.ui
-    configuration/configure_motion_touch.ui
-    configuration/configure_system.ui
-    configuration/configure_ui.ui
-    configuration/configure_web.ui
-    debugger/registers.ui
-    multiplayer/direct_connect.ui
-    multiplayer/lobby.ui
-    multiplayer/chat_room.ui
-    multiplayer/client_room.ui
-    multiplayer/host_room.ui
-    multiplayer/moderation_dialog.ui
-    aboutdialog.ui
-    cheats.ui
-    main.ui
-    compatdb.ui
->>>>>>> 76ca777b... reorder graphics tab, move speed to general
 )
 
 file(GLOB COMPAT_LIST
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 521a65081..feed246b4 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -236,6 +236,7 @@ void Config::ReadUtilityValues() {
 
     Settings::values.dump_textures = ReadSetting("dump_textures", false).toBool();
     Settings::values.custom_textures = ReadSetting("custom_textures", false).toBool();
+    Settings::values.preload_textures = ReadSetting("preload_textures", false).toBool();
 
     qt_config->endGroup();
 }
@@ -708,6 +709,7 @@ void Config::SaveUtilityValues() {
 
     WriteSetting("dump_textures", Settings::values.dump_textures, false);
     WriteSetting("custom_textures", Settings::values.custom_textures, false);
+    WriteSetting("preload_textures", Settings::values.preload_textures, false);
 
     qt_config->endGroup();
 }
diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp
index 8c742ca1e..9c1214917 100644
--- a/src/citra_qt/configuration/configure_dialog.cpp
+++ b/src/citra_qt/configuration/configure_dialog.cpp
@@ -75,20 +75,11 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
 void ConfigureDialog::PopulateSelectionList() {
     ui->selectorList->clear();
 
-<<<<<<< HEAD
     const std::array<std::pair<QString, QList<QWidget*>>, 4> items{
         {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
          {tr("System"), {ui->systemTab, ui->audioTab, ui->cameraTab}},
-         {tr("Graphics"), {ui->graphicsTab}},
+         {tr("Graphics"), {ui->enhancementsTab, ui->graphicsTab}},
          {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}};
-=======
-    const std::array<std::pair<QString, QStringList>, 4> items{
-        {{tr("General"),
-          {QT_TR_NOOP("General"), QT_TR_NOOP("Web"), QT_TR_NOOP("Debug"), QT_TR_NOOP("UI")}},
-         {tr("System"), {QT_TR_NOOP("System"), QT_TR_NOOP("Audio"), QT_TR_NOOP("Camera")}},
-         {tr("Graphics"), {QT_TR_NOOP("Enhancements"), QT_TR_NOOP("Advanced")}},
-         {tr("Controls"), {QT_TR_NOOP("Input"), QT_TR_NOOP("Hotkeys")}}}};
->>>>>>> 76ca777b... reorder graphics tab, move speed to general
 
     for (const auto& entry : items) {
         auto* const item = new QListWidgetItem(entry.first);
@@ -133,12 +124,17 @@ void ConfigureDialog::UpdateVisibleTabs() {
     if (items.isEmpty())
         return;
 
-    const std::map<QWidget*, QString> widgets = {
-        {ui->generalTab, tr("General")},   {ui->systemTab, tr("System")},
-        {ui->inputTab, tr("Input")},       {ui->hotkeysTab, tr("Hotkeys")},
-        {ui->graphicsTab, tr("Graphics")}, {ui->audioTab, tr("Audio")},
-        {ui->cameraTab, tr("Camera")},     {ui->debugTab, tr("Debug")},
-        {ui->webTab, tr("Web")},           {ui->uiTab, tr("UI")}};
+    const std::map<QWidget*, QString> widgets = {{ui->generalTab, tr("General")},
+                                                 {ui->systemTab, tr("System")},
+                                                 {ui->inputTab, tr("Input")},
+                                                 {ui->hotkeysTab, tr("Hotkeys")},
+                                                 {ui->enhancementsTab, tr("Enhancements")},
+                                                 {ui->graphicsTab, tr("Advanced")},
+                                                 {ui->audioTab, tr("Audio")},
+                                                 {ui->cameraTab, tr("Camera")},
+                                                 {ui->debugTab, tr("Debug")},
+                                                 {ui->webTab, tr("Web")},
+                                                 {ui->uiTab, tr("UI")}};
 
     ui->tabWidget->clear();
 
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index fd7a348df..11da2be20 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -50,8 +50,8 @@
 #include "citra_qt/hotkeys.h"
 #include "citra_qt/main.h"
 #include "citra_qt/multiplayer/state.h"
-#include "citra_qt/uisettings.h"
 #include "citra_qt/qt_image_interface.h"
+#include "citra_qt/uisettings.h"
 #include "citra_qt/updater/updater.h"
 #include "citra_qt/util/clickable_label.h"
 #include "common/common_paths.h"
diff --git a/src/citra_qt/qt_image_interface.cpp b/src/citra_qt/qt_image_interface.cpp
index 5e75ccfdd..00a5e1384 100644
--- a/src/citra_qt/qt_image_interface.cpp
+++ b/src/citra_qt/qt_image_interface.cpp
@@ -35,4 +35,4 @@ bool QtImageInterface::EncodePNG(const std::string& path, const std::vector<u8>&
         return false;
     }
     return true;
-}
\ No newline at end of file
+}
diff --git a/src/citra_qt/qt_image_interface.h b/src/citra_qt/qt_image_interface.h
index 944db2c59..53b49b7b8 100644
--- a/src/citra_qt/qt_image_interface.h
+++ b/src/citra_qt/qt_image_interface.h
@@ -11,4 +11,4 @@ public:
     bool DecodePNG(std::vector<u8>& dst, u32& width, u32& height, const std::string& path) override;
     bool EncodePNG(const std::string& path, const std::vector<u8>& src, u32 width,
                    u32 height) override;
-};
\ No newline at end of file
+};
diff --git a/src/common/texture.h b/src/common/texture.h
index bdb03e8b4..2de56bc09 100644
--- a/src/common/texture.h
+++ b/src/common/texture.h
@@ -9,4 +9,4 @@
 
 namespace Common {
 void FlipRGBA8Texture(std::vector<u8>& tex, u64 width, u64 height);
-}
\ No newline at end of file
+}
diff --git a/src/core/core.h b/src/core/core.h
index 2042c2f15..b533c4bc3 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -212,12 +212,6 @@ public:
     /// Gets a const reference to the cheat engine
     const Cheats::CheatEngine& CheatEngine() const;
 
-    /// Gets a reference to the video dumper backend
-    VideoDumper::Backend& VideoDumper();
-
-    /// Gets a const reference to the video dumper backend
-    const VideoDumper::Backend& VideoDumper() const;
-
     /// Gets a reference to the custom texture cache system
     Core::CustomTexCache& CustomTexCache();
 
@@ -233,6 +227,7 @@ public:
     /// Gets a const reference to the video dumper backend
     const VideoDumper::Backend& VideoDumper() const;
 
+    std::unique_ptr<PerfStats> perf_stats;
     FrameLimiter frame_limiter;
 
     void SetStatus(ResultStatus new_status, const char* details = nullptr) {
diff --git a/src/core/custom_tex_cache.cpp b/src/core/custom_tex_cache.cpp
index c0b42f515..c878016f7 100644
--- a/src/core/custom_tex_cache.cpp
+++ b/src/core/custom_tex_cache.cpp
@@ -82,8 +82,9 @@ void CustomTexCache::PreloadTextures() {
         if (image_interface->DecodePNG(tex_info.tex, tex_info.width, tex_info.height,
                                        path_info.path)) {
             // Make sure the texture size is a power of 2
-            if ((ceil(log2(tex_info.width)) == floor(log2(tex_info.width))) &&
-                (ceil(log2(tex_info.height)) == floor(log2(tex_info.height)))) {
+            std::bitset<32> width_bits(tex_info.width);
+            std::bitset<32> height_bits(tex_info.height);
+            if (width_bits.count() == 1 && height_bits.count() == 1) {
                 LOG_DEBUG(Render_OpenGL, "Loaded custom texture from {}", path_info.path);
                 Common::FlipRGBA8Texture(tex_info.tex, tex_info.width, tex_info.height);
                 CacheTexture(path_info.hash, tex_info.tex, tex_info.width, tex_info.height);
diff --git a/src/core/custom_tex_cache.h b/src/core/custom_tex_cache.h
index 8fc65c333..2e0e5dd65 100644
--- a/src/core/custom_tex_cache.h
+++ b/src/core/custom_tex_cache.h
@@ -48,4 +48,4 @@ private:
     std::unordered_map<u64, CustomTexInfo> custom_textures;
     std::unordered_map<u64, CustomTexPathInfo> custom_texture_paths;
 };
-} // namespace Core
\ No newline at end of file
+} // namespace Core
diff --git a/src/core/frontend/image_interface.h b/src/core/frontend/image_interface.h
index 7caa63f36..7febe3331 100644
--- a/src/core/frontend/image_interface.h
+++ b/src/core/frontend/image_interface.h
@@ -21,4 +21,4 @@ public:
                            u32 height) = 0;
 };
 
-} // namespace Frontend
\ No newline at end of file
+} // namespace Frontend
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 9f496d321..af2a918a6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -98,9 +98,12 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
  * Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp
  */
 static inline void GetTexImageOES(GLenum target, GLint level, GLenum format, GLenum type,
-                                  GLint height, GLint width, GLint depth, GLubyte* pixels) {
+                                  GLint height, GLint width, GLint depth, GLubyte* pixels,
+                                  GLuint size) {
+    memset(pixels, 0x80, size);
 
-    memset(pixels, 0x80, height * width * 4);
+    OpenGLState cur_state = OpenGLState::GetCurState();
+    OpenGLState state;
 
     GLenum texture_binding = GL_NONE;
     switch (target) {
@@ -127,11 +130,10 @@ static inline void GetTexImageOES(GLenum target, GLint level, GLenum format, GLe
         return;
     }
 
-    GLint prev_fbo = 0;
-    GLuint fbo = 0;
-    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
-    glGenFramebuffers(1, &fbo);
-    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    OGLFramebuffer fbo;
+    fbo.Create();
+    state.draw.read_framebuffer = fbo.handle;
+    state.Apply();
 
     switch (target) {
     case GL_TEXTURE_2D:
@@ -141,8 +143,9 @@ static inline void GetTexImageOES(GLenum target, GLint level, GLenum format, GLe
     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: {
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
-        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
+                               level);
+        GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
         if (status != GL_FRAMEBUFFER_COMPLETE) {
             LOG_DEBUG(Render_OpenGL, "Framebuffer is incomplete, status: {:X}", status);
         }
@@ -151,16 +154,16 @@ static inline void GetTexImageOES(GLenum target, GLint level, GLenum format, GLe
     }
     case GL_TEXTURE_3D_OES:
         for (int i = 0; i < depth; i++) {
-            glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture,
-                                   level, i);
+            glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D,
+                                   texture, level, i);
             glReadPixels(0, 0, width, height, format, type, pixels + 4 * i * width * height);
         }
         break;
     }
 
-    glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
+    cur_state.Apply();
 
-    glDeleteFramebuffers(1, &fbo);
+    fbo.Release();
 }
 
 template <typename Map, typename Interval>
@@ -869,9 +872,9 @@ bool CachedSurface::LoadCustomTexture(u64 tex_hash, Core::CustomTexInfo& tex_inf
             const auto& path_info = custom_tex_cache.LookupTexturePathInfo(tex_hash);
             if (image_interface->DecodePNG(tex_info.tex, tex_info.width, tex_info.height,
                                            path_info.path)) {
-                // Make sure the texture size is a power of 2
-                if ((ceil(log2(tex_info.width)) == floor(log2(tex_info.width))) &&
-                    (ceil(log2(tex_info.height)) == floor(log2(tex_info.height)))) {
+                std::bitset<32> width_bits(tex_info.width);
+                std::bitset<32> height_bits(tex_info.height);
+                if (width_bits.count() == 1 && height_bits.count() == 1) {
                     LOG_DEBUG(Render_OpenGL, "Loaded custom texture from {}", path_info.path);
                     Common::FlipRGBA8Texture(tex_info.tex, tex_info.width, tex_info.height);
                     custom_tex_cache.CacheTexture(tex_hash, tex_info.tex, tex_info.width,
@@ -931,7 +934,7 @@ void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) {
            desktop and ES.
         */
         GetTexImageOES(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, height, width, 0,
-                       &decoded_texture[0]);
+                       &decoded_texture[0], decoded_texture.size());
         glBindTexture(GL_TEXTURE_2D, 0);
         Common::FlipRGBA8Texture(decoded_texture, width, height);
         if (!image_interface->EncodePNG(dump_path, decoded_texture, width, height))
@@ -1086,7 +1089,8 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect, GLuint
         glActiveTexture(GL_TEXTURE0);
         if (GLES) {
             GetTexImageOES(GL_TEXTURE_2D, 0, tuple.format, tuple.type, rect.GetHeight(),
-                           rect.GetWidth(), 0, &gl_buffer[buffer_offset]);
+                           rect.GetWidth(), 0, &gl_buffer[buffer_offset],
+                           gl_buffer_size - buffer_offset);
         } else {
             glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]);
         }