diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp
index 816811d4cb..0df5c4426a 100644
--- a/Source/Core/Core/Core.cpp
+++ b/Source/Core/Core/Core.cpp
@@ -173,6 +173,13 @@ void DisplayMessage(const std::string& message, int time_in_ms)
   if (!IsRunning())
     return;
 
+  // Actually displaying non-ASCII could cause things to go pear-shaped
+  for (const char& c : message)
+  {
+    if (!std::isprint(c))
+      return;
+  }
+
   OSD::AddMessage(message, time_in_ms);
   Host_UpdateTitle(message);
 }
diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp
index 2253297bb6..6394714a3d 100644
--- a/Source/Core/DolphinWX/Frame.cpp
+++ b/Source/Core/DolphinWX/Frame.cpp
@@ -1444,16 +1444,14 @@ void CFrame::ParseHotkeys()
 
   if (IsHotkey(HK_INCREASE_IR))
   {
+    OSDChoice = 1;
     ++g_Config.iEFBScale;
-    OSDPrintInternalResolution();
   }
   if (IsHotkey(HK_DECREASE_IR))
   {
+    OSDChoice = 1;
     if (--g_Config.iEFBScale < SCALE_AUTO)
-    {
       g_Config.iEFBScale = SCALE_AUTO;
-    }
-    OSDPrintInternalResolution();
   }
   if (IsHotkey(HK_TOGGLE_CROP))
   {
@@ -1461,26 +1459,28 @@ void CFrame::ParseHotkeys()
   }
   if (IsHotkey(HK_TOGGLE_AR))
   {
+    OSDChoice = 2;
     // Toggle aspect ratio
     g_Config.iAspectRatio = (g_Config.iAspectRatio + 1) & 3;
-    OSDPrintAspectRatio();
   }
   if (IsHotkey(HK_TOGGLE_EFBCOPIES))
   {
+    OSDChoice = 3;
     // Toggle EFB copies between EFB2RAM and EFB2Texture
     g_Config.bSkipEFBCopyToRam = !g_Config.bSkipEFBCopyToRam;
-    OSDPrintEFB();
   }
   if (IsHotkey(HK_TOGGLE_FOG))
   {
+    OSDChoice = 4;
     g_Config.bDisableFog = !g_Config.bDisableFog;
-    OSDPrintFog();
   }
   if (IsHotkey(HK_TOGGLE_TEXTURES))
     g_Config.bHiresTextures = !g_Config.bHiresTextures;
   Core::SetIsThrottlerTempDisabled(IsHotkey(HK_TOGGLE_THROTTLE, true));
   if (IsHotkey(HK_DECREASE_EMULATION_SPEED))
   {
+    OSDChoice = 5;
+
     if (SConfig::GetInstance().m_EmulationSpeed <= 0.0f)
       SConfig::GetInstance().m_EmulationSpeed = 1.0f;
     else if (SConfig::GetInstance().m_EmulationSpeed >= 0.2f)
@@ -1491,19 +1491,17 @@ void CFrame::ParseHotkeys()
     if (SConfig::GetInstance().m_EmulationSpeed >= 0.95f &&
         SConfig::GetInstance().m_EmulationSpeed <= 1.05f)
       SConfig::GetInstance().m_EmulationSpeed = 1.0f;
-
-    OSDPrintEmulationSpeed();
   }
   if (IsHotkey(HK_INCREASE_EMULATION_SPEED))
   {
+    OSDChoice = 5;
+
     if (SConfig::GetInstance().m_EmulationSpeed > 0.0f)
       SConfig::GetInstance().m_EmulationSpeed += 0.1f;
 
     if (SConfig::GetInstance().m_EmulationSpeed >= 0.95f &&
         SConfig::GetInstance().m_EmulationSpeed <= 1.05f)
       SConfig::GetInstance().m_EmulationSpeed = 1.0f;
-
-    OSDPrintEmulationSpeed();
   }
   if (IsHotkey(HK_SAVE_STATE_SLOT_SELECTED))
   {
@@ -1718,79 +1716,3 @@ void CFrame::HandleSignal(wxTimerEvent& event)
     return;
   Close();
 }
-
-void CFrame::OSDPrintInternalResolution()
-{
-  std::string text;
-  switch (g_Config.iEFBScale)
-  {
-  case SCALE_AUTO:
-    text = "Auto (fractional)";
-    break;
-  case SCALE_AUTO_INTEGRAL:
-    text = "Auto (integral)";
-    break;
-  case SCALE_1X:
-    text = "Native";
-    break;
-  case SCALE_1_5X:
-    text = "1.5x";
-    break;
-  case SCALE_2X:
-    text = "2x";
-    break;
-  case SCALE_2_5X:
-    text = "2.5x";
-    break;
-  default:
-    text = StringFromFormat("%dx", g_Config.iEFBScale - 3);
-    break;
-  }
-
-  OSD::AddMessage("Internal Resolution: " + text);
-}
-
-void CFrame::OSDPrintAspectRatio()
-{
-  std::string text;
-  switch (g_Config.iAspectRatio)
-  {
-  case ASPECT_AUTO:
-    text = "Auto";
-    break;
-  case ASPECT_STRETCH:
-    text = "Stretch";
-    break;
-  case ASPECT_ANALOG:
-    text = "Force 4:3";
-    break;
-  case ASPECT_ANALOG_WIDE:
-    text = "Force 16:9";
-    break;
-  }
-
-  OSD::AddMessage("Aspect Ratio: " + text + (g_Config.bCrop ? " (crop)" : ""));
-}
-
-void CFrame::OSDPrintEFB()
-{
-  OSD::AddMessage(std::string("Copy EFB: ") +
-                  (g_Config.bSkipEFBCopyToRam ? "to Texture" : "to RAM"));
-}
-
-void CFrame::OSDPrintFog()
-{
-  OSD::AddMessage(std::string("Fog: ") + (g_Config.bDisableFog ? "Disabled" : "Enabled"));
-}
-
-void CFrame::OSDPrintEmulationSpeed()
-{
-  std::string text = "Speed Limit: ";
-
-  if (SConfig::GetInstance().m_EmulationSpeed <= 0)
-    text += "Unlimited";
-  else
-    text += StringFromFormat("%li%%", std::lround(SConfig::GetInstance().m_EmulationSpeed * 100.f));
-
-  OSD::AddMessage(text);
-}
diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h
index 3e03ea0373..6a24e3768f 100644
--- a/Source/Core/DolphinWX/Frame.h
+++ b/Source/Core/DolphinWX/Frame.h
@@ -347,13 +347,6 @@ private:
 
   bool InitControllers();
 
-  // OSD
-  void OSDPrintInternalResolution();
-  void OSDPrintAspectRatio();
-  void OSDPrintEFB();
-  void OSDPrintFog();
-  void OSDPrintEmulationSpeed();
-
   // Event table
   DECLARE_EVENT_TABLE();
 };
diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp
index d0f6f24825..62997cd150 100644
--- a/Source/Core/VideoCommon/OnScreenDisplay.cpp
+++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp
@@ -21,24 +21,18 @@ static std::multimap<CallbackType, Callback> s_callbacks;
 static std::multimap<MessageType, Message> s_messages;
 static std::mutex s_messages_mutex;
 
-static std::string CleanMessage(std::string message)
-{
-  std::replace_if(message.begin(), message.end(), [](char c) { return !std::isprint(c); }, '?');
-  return message;
-}
-
 void AddTypedMessage(MessageType type, const std::string& message, u32 ms, u32 rgba)
 {
   std::lock_guard<std::mutex> lock(s_messages_mutex);
   s_messages.erase(type);
-  s_messages.emplace(type, Message(CleanMessage(message), Common::Timer::GetTimeMs() + ms, rgba));
+  s_messages.emplace(type, Message(message, Common::Timer::GetTimeMs() + ms, rgba));
 }
 
 void AddMessage(const std::string& message, u32 ms, u32 rgba)
 {
   std::lock_guard<std::mutex> lock(s_messages_mutex);
   s_messages.emplace(MessageType::Typeless,
-                     Message(CleanMessage(message), Common::Timer::GetTimeMs() + ms, rgba));
+                     Message(message, Common::Timer::GetTimeMs() + ms, rgba));
 }
 
 void DrawMessage(const Message& msg, int top, int left, int time_left)
@@ -58,7 +52,7 @@ void DrawMessages()
     std::lock_guard<std::mutex> lock(s_messages_mutex);
 
     u32 now = Common::Timer::GetTimeMs();
-    int left = 20, top = 20;
+    int left = 20, top = 35;
 
     auto it = s_messages.begin();
     while (it != s_messages.end())
diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h
index cf8d66db72..a05f4058ef 100644
--- a/Source/Core/VideoCommon/OnScreenDisplay.h
+++ b/Source/Core/VideoCommon/OnScreenDisplay.h
@@ -22,14 +22,6 @@ struct Message
 
 enum class MessageType
 {
-  FPS,
-  FrameCount,
-  RTC,
-
-  MovieInputCount,
-  MovieLag,
-  MovieInput,
-
   NetPlayPing,
   NetPlayBuffer,
 
diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp
index 519e4fcbc6..92817805e3 100644
--- a/Source/Core/VideoCommon/RenderBase.cpp
+++ b/Source/Core/VideoCommon/RenderBase.cpp
@@ -34,7 +34,6 @@
 #include "Core/Host.h"
 #include "Core/Movie.h"
 
-#include "OnScreenDisplay.h"
 #include "VideoCommon/AVIDump.h"
 #include "VideoCommon/BPMemory.h"
 #include "VideoCommon/CPMemory.h"
@@ -43,6 +42,7 @@
 #include "VideoCommon/FPSCounter.h"
 #include "VideoCommon/FramebufferManagerBase.h"
 #include "VideoCommon/ImageWrite.h"
+#include "VideoCommon/OnScreenDisplay.h"
 #include "VideoCommon/PostProcessing.h"
 #include "VideoCommon/RenderBase.h"
 #include "VideoCommon/Statistics.h"
@@ -52,6 +52,8 @@
 
 // TODO: Move these out of here.
 int frameCount;
+int OSDChoice;
+static int OSDTime;
 
 std::unique_ptr<Renderer> g_renderer;
 
@@ -104,6 +106,9 @@ Renderer::Renderer()
 {
   UpdateActiveConfig();
   TextureCacheBase::OnConfigChanged(g_ActiveConfig);
+
+  OSDChoice = 0;
+  OSDTime = 0;
 }
 
 Renderer::~Renderer()
@@ -305,46 +310,146 @@ void Renderer::SetScreenshot(const std::string& filename)
   s_bScreenshot = true;
 }
 
+// Create On-Screen-Messages
 void Renderer::DrawDebugText()
 {
-  auto draw_text = [](OSD::MessageType type, const std::string& message) {
-    OSD::AddTypedMessage(type, message, OSD::Duration::SHORT, OSD::Color::CYAN);
-  };
+  std::string final_yellow, final_cyan;
 
-  if (g_ActiveConfig.bShowFPS)
+  if (g_ActiveConfig.bShowFPS || SConfig::GetInstance().m_ShowFrameCount)
   {
-    draw_text(OSD::MessageType::FPS,
-              StringFromFormat("FPS: %u", g_renderer->m_fps_counter.GetFPS()));
-  }
+    if (g_ActiveConfig.bShowFPS)
+      final_cyan += StringFromFormat("FPS: %u", g_renderer->m_fps_counter.GetFPS());
 
-  if (SConfig::GetInstance().m_ShowFrameCount)
-  {
-    draw_text(OSD::MessageType::FrameCount,
-              StringFromFormat("Frame: %" PRIx64, Movie::GetCurrentFrame()));
-
-    if (Movie::IsPlayingInput())
+    if (g_ActiveConfig.bShowFPS && SConfig::GetInstance().m_ShowFrameCount)
+      final_cyan += " - ";
+    if (SConfig::GetInstance().m_ShowFrameCount)
     {
-      draw_text(OSD::MessageType::MovieInputCount,
-                StringFromFormat("Input: %" PRIx64 " / %" PRIx64, Movie::GetCurrentInputCount(),
-                                 Movie::GetTotalInputCount()));
+      final_cyan += StringFromFormat("Frame: %llu", (unsigned long long)Movie::GetCurrentFrame());
+      if (Movie::IsPlayingInput())
+        final_cyan += StringFromFormat("\nInput: %llu / %llu",
+                                       (unsigned long long)Movie::GetCurrentInputCount(),
+                                       (unsigned long long)Movie::GetTotalInputCount());
     }
+
+    final_cyan += "\n";
+    final_yellow += "\n";
   }
 
   if (SConfig::GetInstance().m_ShowLag)
   {
-    draw_text(OSD::MessageType::MovieLag,
-              StringFromFormat("Lag: %" PRIu64, Movie::GetCurrentLagCount()));
+    final_cyan += StringFromFormat("Lag: %" PRIu64 "\n", Movie::GetCurrentLagCount());
+    final_yellow += "\n";
   }
 
   if (SConfig::GetInstance().m_ShowInputDisplay)
   {
-    draw_text(OSD::MessageType::MovieInput, Movie::GetInputDisplay());
+    final_cyan += Movie::GetInputDisplay();
+    final_yellow += "\n";
   }
 
   if (SConfig::GetInstance().m_ShowRTC)
   {
-    draw_text(OSD::MessageType::RTC, Movie::GetRTCDisplay());
+    final_cyan += Movie::GetRTCDisplay();
+    final_yellow += "\n";
   }
+
+  // OSD Menu messages
+  if (OSDChoice > 0)
+  {
+    OSDTime = Common::Timer::GetTimeMs() + 3000;
+    OSDChoice = -OSDChoice;
+  }
+
+  if ((u32)OSDTime > Common::Timer::GetTimeMs())
+  {
+    std::string res_text;
+    switch (g_ActiveConfig.iEFBScale)
+    {
+    case SCALE_AUTO:
+      res_text = "Auto (fractional)";
+      break;
+    case SCALE_AUTO_INTEGRAL:
+      res_text = "Auto (integral)";
+      break;
+    case SCALE_1X:
+      res_text = "Native";
+      break;
+    case SCALE_1_5X:
+      res_text = "1.5x";
+      break;
+    case SCALE_2X:
+      res_text = "2x";
+      break;
+    case SCALE_2_5X:
+      res_text = "2.5x";
+      break;
+    default:
+      res_text = StringFromFormat("%dx", g_ActiveConfig.iEFBScale - 3);
+      break;
+    }
+    const char* ar_text = "";
+    switch (g_ActiveConfig.iAspectRatio)
+    {
+    case ASPECT_AUTO:
+      ar_text = "Auto";
+      break;
+    case ASPECT_STRETCH:
+      ar_text = "Stretch";
+      break;
+    case ASPECT_ANALOG:
+      ar_text = "Force 4:3";
+      break;
+    case ASPECT_ANALOG_WIDE:
+      ar_text = "Force 16:9";
+    }
+
+    const char* const efbcopy_text = g_ActiveConfig.bSkipEFBCopyToRam ? "to Texture" : "to RAM";
+
+    // The rows
+    const std::string lines[] = {
+        std::string("Internal Resolution: ") + res_text,
+        std::string("Aspect Ratio: ") + ar_text + (g_ActiveConfig.bCrop ? " (crop)" : ""),
+        std::string("Copy EFB: ") + efbcopy_text,
+        std::string("Fog: ") + (g_ActiveConfig.bDisableFog ? "Disabled" : "Enabled"),
+        SConfig::GetInstance().m_EmulationSpeed <= 0 ?
+            "Speed Limit: Unlimited" :
+            StringFromFormat("Speed Limit: %li%%",
+                             std::lround(SConfig::GetInstance().m_EmulationSpeed * 100.f)),
+    };
+
+    enum
+    {
+      lines_count = sizeof(lines) / sizeof(*lines)
+    };
+
+    // The latest changed setting in yellow
+    for (int i = 0; i != lines_count; ++i)
+    {
+      if (OSDChoice == -i - 1)
+        final_yellow += lines[i];
+      final_yellow += '\n';
+    }
+
+    // The other settings in cyan
+    for (int i = 0; i != lines_count; ++i)
+    {
+      if (OSDChoice != -i - 1)
+        final_cyan += lines[i];
+      final_cyan += '\n';
+    }
+  }
+
+  final_cyan += Common::Profiler::ToString();
+
+  if (g_ActiveConfig.bOverlayStats)
+    final_cyan += Statistics::ToString();
+
+  if (g_ActiveConfig.bOverlayProjStats)
+    final_cyan += Statistics::ToStringProj();
+
+  // and then the text
+  g_renderer->RenderText(final_cyan, 20, 20, 0xFF00FFFF);
+  g_renderer->RenderText(final_yellow, 20, 20, 0xFFFFFF00);
 }
 
 void Renderer::UpdateDrawRectangle(int backbuffer_width, int backbuffer_height)
diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h
index 5c6a98dd21..8b3495b82e 100644
--- a/Source/Core/VideoCommon/RenderBase.h
+++ b/Source/Core/VideoCommon/RenderBase.h
@@ -39,6 +39,7 @@ struct EfbPokeData
 
 // TODO: Move these out of here.
 extern int frameCount;
+extern int OSDChoice;
 
 // Renderer really isn't a very good name for this class - it's more like "Misc".
 // The long term goal is to get rid of this class and replace it with others that make