diff --git a/.gitmodules b/.gitmodules
index baa06df721..0dc4538c11 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -43,6 +43,6 @@
 [submodule "teakra"]
     path = externals/teakra
     url = https://github.com/wwylele/teakra.git
-[submodule "externals/lodepng/lodepng"]
-	path = externals/lodepng/lodepng
-	url = https://github.com/lvandeve/lodepng.git
+[submodule "lodepng"]
+    path = externals/lodepng/lodepng
+    url = https://github.com/lvandeve/lodepng.git
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 1e17271658..521a650819 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -56,7 +56,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
 // This must be in alphabetical order according to action name as it must have the same order as
 // UISetting::values.shortcuts, which is alphabetically ordered.
 // clang-format off
-const std::array<UISettings::Shortcut, 19> default_hotkeys{
+const std::array<UISettings::Shortcut, 20> default_hotkeys{
     {{QStringLiteral("Advance Frame"),            QStringLiteral("Main Window"), {QStringLiteral("\\"), Qt::ApplicationShortcut}},
      {QStringLiteral("Capture Screenshot"),       QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
      {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
@@ -75,7 +75,8 @@ const std::array<UISettings::Shortcut, 19> default_hotkeys{
      {QStringLiteral("Toggle Frame Advancing"),   QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
      {QStringLiteral("Toggle Screen Layout"),     QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}},
      {QStringLiteral("Toggle Speed Limit"),       QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
-     {QStringLiteral("Toggle Status Bar"),        QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}}};
+     {QStringLiteral("Toggle Status Bar"),        QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
+     {QStringLiteral("Toggle Texture Dumping"),   QStringLiteral("Main Window"), {QStringLiteral("Ctrl+D"), Qt::ApplicationShortcut}}}};
 // clang-format on
 
 void Config::ReadValues() {
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 2b5bbaf626..fd7a348dfb 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -428,8 +428,11 @@ void GMainWindow::InitializeHotkeys() {
                 Settings::values.use_frame_limit = !Settings::values.use_frame_limit;
                 UpdateStatusBar();
             });
-    // We use "static" here in order to avoid capturing by lambda due to a MSVC bug, which makes the
-    // variable hold a garbage value after this function exits
+    connect(hotkey_registry.GetHotkey("Main Window", "Toggle Texture Dumping", this),
+            &QShortcut::activated, this,
+            [&] { Settings::values.dump_textures = !Settings::values.dump_textures; });
+    // We use "static" here in order to avoid capturing by lambda due to a MSVC bug, which makes
+    // the variable hold a garbage value after this function exits
     static constexpr u16 SPEED_LIMIT_STEP = 5;
     connect(hotkey_registry.GetHotkey("Main Window", "Increase Speed Limit", this),
             &QShortcut::activated, this, [&] {
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 42f952b2ce..13e71615e6 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -46,6 +46,7 @@
 #define SHADER_DIR "shaders"
 #define DUMP_DIR "dump"
 #define LOAD_DIR "load"
+#define SHADER_DIR "shaders"
 
 // Filenames
 // Files in the directory returned by GetUserPath(UserPath::LogDir)
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 65f3000dd7..9f496d3216 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -917,11 +917,21 @@ void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) {
         std::vector<u8> decoded_texture;
         decoded_texture.resize(width * height * 4);
         glBindTexture(GL_TEXTURE_2D, target_tex);
-        if (GLES)
-            GetTexImageOES(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, height, width, 0,
-                           &decoded_texture[0]);
-        else
-            glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &decoded_texture[0]);
+        /*
+           GetTexImageOES is used even if not using OpenGL ES to work around a small issue that
+           happens if using custom textures with texture dumping at the same.
+           Let's say there's 2 textures that are both 32x32 and one of them gets replaced with a
+           higher quality 256x256 texture. If the 256x256 texture is displayed first and the 32x32
+           texture gets uploaded to the same underlying OpenGL texture, the 32x32 texture will
+           appear in the corner of the 256x256 texture.
+           If texture dumping is enabled and the 32x32 is undumped, Citra will attempt to dump it.
+           Since the underlying OpenGL texture is still 256x256, Citra crashes because it thinks the
+           texture is only 32x32.
+           GetTexImageOES conveniently only dumps the specified region, and works on both
+           desktop and ES.
+        */
+        GetTexImageOES(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, height, width, 0,
+                       &decoded_texture[0]);
         glBindTexture(GL_TEXTURE_2D, 0);
         Common::FlipRGBA8Texture(decoded_texture, width, height);
         if (!image_interface->EncodePNG(dump_path, decoded_texture, width, height))