diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9824769cfa..29222babaa 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -18,6 +18,7 @@
 #include "core/loader/loader.h"
 #include "core/settings.h"
 #include "file_sys/vfs_real.h"
+#include "video_core/renderer_base.h"
 #include "video_core/video_core.h"
 
 namespace Core {
@@ -174,7 +175,6 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
         cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
     }
 
-    gpu_core = std::make_unique<Tegra::GPU>();
     telemetry_session = std::make_unique<Core::TelemetrySession>();
     service_manager = std::make_shared<Service::SM::ServiceManager>();
 
@@ -182,10 +182,13 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
     Service::Init(service_manager);
     GDBStub::Init();
 
-    if (!VideoCore::Init(emu_window)) {
+    renderer = VideoCore::CreateRenderer(emu_window);
+    if (!renderer->Init()) {
         return ResultStatus::ErrorVideoCore;
     }
 
+    gpu_core = std::make_unique<Tegra::GPU>(*renderer->Rasterizer());
+
     // Create threads for CPU cores 1-3, and build thread_to_cpu map
     // CPU core 0 is run on the main thread
     thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
@@ -217,7 +220,7 @@ void System::Shutdown() {
                          perf_results.frametime * 1000.0);
 
     // Shutdown emulation session
-    VideoCore::Shutdown();
+    renderer.reset();
     GDBStub::Shutdown();
     Service::Shutdown();
     Kernel::Shutdown();
diff --git a/src/core/core.h b/src/core/core.h
index ed475ac4ef..059db4262c 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -27,6 +27,10 @@ namespace Service::SM {
 class ServiceManager;
 }
 
+namespace VideoCore {
+class RendererBase;
+}
+
 namespace Core {
 
 class System {
@@ -127,11 +131,26 @@ public:
     /// Gets a CPU interface to the CPU core with the specified index
     Cpu& CpuCore(size_t core_index);
 
-    /// Gets the GPU interface
+    /// Gets a mutable reference to the GPU interface
     Tegra::GPU& GPU() {
         return *gpu_core;
     }
 
+    /// Gets an immutable reference to the GPU interface.
+    const Tegra::GPU& GPU() const {
+        return *gpu_core;
+    }
+
+    /// Gets a mutable reference to the renderer.
+    VideoCore::RendererBase& Renderer() {
+        return *renderer;
+    }
+
+    /// Gets an immutable reference to the renderer.
+    const VideoCore::RendererBase& Renderer() const {
+        return *renderer;
+    }
+
     /// Gets the scheduler for the CPU core that is currently running
     Kernel::Scheduler& CurrentScheduler() {
         return *CurrentCpuCore().Scheduler();
@@ -195,6 +214,7 @@ private:
 
     /// AppLoader used to load the current executing application
     std::unique_ptr<Loader::AppLoader> app_loader;
+    std::unique_ptr<VideoCore::RendererBase> renderer;
     std::unique_ptr<Tegra::GPU> gpu_core;
     std::shared_ptr<Tegra::DebugContext> debug_context;
     Kernel::SharedPtr<Kernel::Process> current_process;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index ed69a4325d..2b74e6a339 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -30,9 +30,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
         addr,      offset,   width, height, stride, static_cast<PixelFormat>(format),
         transform, crop_rect};
 
-    Core::System::GetInstance().perf_stats.EndGameFrame();
-
-    VideoCore::g_renderer->SwapBuffers(framebuffer);
+    auto& instance = Core::System::GetInstance();
+    instance.perf_stats.EndGameFrame();
+    instance.Renderer().SwapBuffers(framebuffer);
 }
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 57b128b40a..06151a1ea1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -150,15 +150,16 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
 
     LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
 
-    auto& gpu = Core::System::GetInstance().GPU();
-
-    auto itr = buffer_mappings.find(params.offset);
-
+    const auto itr = buffer_mappings.find(params.offset);
     ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
 
-    // Remove this memory region from the rasterizer cache.
-    VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size);
+    auto& system_instance = Core::System::GetInstance();
 
+    // Remove this memory region from the rasterizer cache.
+    system_instance.Renderer().Rasterizer()->FlushAndInvalidateRegion(params.offset,
+                                                                      itr->second.size);
+
+    auto& gpu = system_instance.GPU();
     params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
 
     buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 5344441e1d..0bf51062cb 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -127,9 +127,11 @@ void NVFlinger::Compose() {
         MicroProfileFlip();
 
         if (buffer == boost::none) {
+            auto& system_instance = Core::System::GetInstance();
+
             // There was no queued buffer to draw, render previous frame
-            Core::System::GetInstance().perf_stats.EndGameFrame();
-            VideoCore::g_renderer->SwapBuffers({});
+            system_instance.perf_stats.EndGameFrame();
+            system_instance.Renderer().SwapBuffers({});
             continue;
         }
 
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4b3bb7b31a..a8f08e1da5 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -326,34 +326,36 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached)
 }
 
 void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
+    auto& system_instance = Core::System::GetInstance();
+
     // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
     // null here
-    if (VideoCore::g_renderer == nullptr) {
+    if (!system_instance.IsPoweredOn()) {
         return;
     }
 
     VAddr end = start + size;
 
-    auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
+    const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
         if (start >= region_end || end <= region_start) {
             // No overlap with region
             return;
         }
 
-        VAddr overlap_start = std::max(start, region_start);
-        VAddr overlap_end = std::min(end, region_end);
+        const VAddr overlap_start = std::max(start, region_start);
+        const VAddr overlap_end = std::min(end, region_end);
 
-        std::vector<Tegra::GPUVAddr> gpu_addresses =
-            Core::System::GetInstance().GPU().memory_manager->CpuToGpuAddress(overlap_start);
+        const std::vector<Tegra::GPUVAddr> gpu_addresses =
+            system_instance.GPU().memory_manager->CpuToGpuAddress(overlap_start);
 
         if (gpu_addresses.empty()) {
             return;
         }
 
-        u64 overlap_size = overlap_end - overlap_start;
+        const u64 overlap_size = overlap_end - overlap_start;
 
         for (const auto& gpu_address : gpu_addresses) {
-            auto* rasterizer = VideoCore::g_renderer->Rasterizer();
+            auto* rasterizer = system_instance.Renderer().Rasterizer();
             switch (mode) {
             case FlushMode::Flush:
                 rasterizer->FlushRegion(gpu_address, overlap_size);
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 79e0b347b8..a4623223d1 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -2,6 +2,7 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include "core/core.h"
 #include "core/gdbstub/gdbstub.h"
 #include "core/hle/service/hid/hid.h"
 #include "core/settings.h"
@@ -19,8 +20,9 @@ void Apply() {
 
     VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
 
-    if (VideoCore::g_renderer) {
-        VideoCore::g_renderer->UpdateCurrentFramebufferLayout();
+    auto& system_instance = Core::System::GetInstance();
+    if (system_instance.IsPoweredOn()) {
+        system_instance.Renderer().UpdateCurrentFramebufferLayout();
     }
 
     Service::HID::ReloadInputDevices();
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 0e205ed72e..a235b543e5 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -19,8 +19,8 @@ namespace Engines {
 /// First register id that is actually a Macro call.
 constexpr u32 MacroRegistersStart = 0xE00;
 
-Maxwell3D::Maxwell3D(MemoryManager& memory_manager)
-    : memory_manager(memory_manager), macro_interpreter(*this) {}
+Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
+    : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
 
 void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
     auto macro_code = uploaded_macros.find(method);
@@ -130,7 +130,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
         break;
     }
 
-    VideoCore::g_renderer->Rasterizer()->NotifyMaxwellRegisterChanged(method);
+    rasterizer.NotifyMaxwellRegisterChanged(method);
 
     if (debug_context) {
         debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
@@ -218,7 +218,7 @@ void Maxwell3D::DrawArrays() {
     }
 
     const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
-    VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed);
+    rasterizer.AccelerateDrawBatch(is_indexed);
 
     // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
     // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
@@ -393,7 +393,7 @@ void Maxwell3D::ProcessClearBuffers() {
            regs.clear_buffers.R == regs.clear_buffers.B &&
            regs.clear_buffers.R == regs.clear_buffers.A);
 
-    VideoCore::g_renderer->Rasterizer()->Clear();
+    rasterizer.Clear();
 }
 
 } // namespace Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 3c32f10671..4d0ff96a57 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -17,6 +17,10 @@
 #include "video_core/memory_manager.h"
 #include "video_core/textures/texture.h"
 
+namespace VideoCore {
+class RasterizerInterface;
+}
+
 namespace Tegra::Engines {
 
 #define MAXWELL3D_REG_INDEX(field_name)                                                            \
@@ -24,7 +28,7 @@ namespace Tegra::Engines {
 
 class Maxwell3D final {
 public:
-    explicit Maxwell3D(MemoryManager& memory_manager);
+    explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
     ~Maxwell3D() = default;
 
     /// Register structure of the Maxwell3D engine.
@@ -818,6 +822,8 @@ public:
     Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
 
 private:
+    VideoCore::RasterizerInterface& rasterizer;
+
     std::unordered_map<u32, std::vector<u32>> uploaded_macros;
 
     /// Macro method that is currently being executed / being fed parameters.
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 141e20444f..b2a83ce0b0 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -7,12 +7,13 @@
 #include "video_core/engines/maxwell_compute.h"
 #include "video_core/engines/maxwell_dma.h"
 #include "video_core/gpu.h"
+#include "video_core/rasterizer_interface.h"
 
 namespace Tegra {
 
-GPU::GPU() {
+GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
     memory_manager = std::make_unique<MemoryManager>();
-    maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
+    maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
     fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
     maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
     maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 08aa75503b..440505c9d1 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -11,6 +11,10 @@
 #include "core/hle/service/nvflinger/buffer_queue.h"
 #include "video_core/memory_manager.h"
 
+namespace VideoCore {
+class RasterizerInterface;
+}
+
 namespace Tegra {
 
 enum class RenderTargetFormat : u32 {
@@ -98,7 +102,7 @@ enum class EngineID {
 
 class GPU final {
 public:
-    GPU();
+    explicit GPU(VideoCore::RasterizerInterface& rasterizer);
     ~GPU();
 
     /// Processes a command list stored at the specified address in GPU memory.
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index dbe3edf09a..3ca3502433 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -7,6 +7,8 @@
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_opengl/gl_rasterizer.h"
 
+namespace VideoCore {
+
 RendererBase::RendererBase(EmuWindow& window) : render_window{window} {}
 RendererBase::~RendererBase() = default;
 
@@ -21,3 +23,5 @@ void RendererBase::RefreshRasterizerSetting() {
         rasterizer = std::make_unique<RasterizerOpenGL>(render_window);
     }
 }
+
+} // namespace VideoCore
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 1cb161b7f9..effb9aed3b 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -13,6 +13,8 @@
 
 class EmuWindow;
 
+namespace VideoCore {
+
 class RendererBase : NonCopyable {
 public:
     /// Used to reference a framebuffer
@@ -44,7 +46,7 @@ public:
         return m_current_frame;
     }
 
-    VideoCore::RasterizerInterface* Rasterizer() const {
+    RasterizerInterface* Rasterizer() const {
         return rasterizer.get();
     }
 
@@ -52,7 +54,9 @@ public:
 
 protected:
     EmuWindow& render_window; ///< Reference to the render window handle.
-    std::unique_ptr<VideoCore::RasterizerInterface> rasterizer;
+    std::unique_ptr<RasterizerInterface> rasterizer;
     f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
     int m_current_frame = 0;  ///< Current frame, should be set by the renderer
 };
+
+} // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 74383c7cfc..50846975f0 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -103,7 +103,7 @@ ScopeAcquireGLContext::~ScopeAcquireGLContext() {
     }
 }
 
-RendererOpenGL::RendererOpenGL(EmuWindow& window) : RendererBase{window} {}
+RendererOpenGL::RendererOpenGL(EmuWindow& window) : VideoCore::RendererBase{window} {}
 RendererOpenGL::~RendererOpenGL() = default;
 
 /// Swap buffers (render frame)
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index ab7de41c8b..428afa3b71 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -41,7 +41,7 @@ private:
     EmuWindow& emu_window;
 };
 
-class RendererOpenGL : public RendererBase {
+class RendererOpenGL : public VideoCore::RendererBase {
 public:
     explicit RendererOpenGL(EmuWindow& window);
     ~RendererOpenGL() override;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 06b13e6817..5085ef96bd 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,37 +3,16 @@
 // Refer to the license.txt file included.
 
 #include <memory>
-#include "common/logging/log.h"
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_opengl/renderer_opengl.h"
 #include "video_core/video_core.h"
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Video Core namespace
-
 namespace VideoCore {
 
-std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
-
 std::atomic<bool> g_toggle_framelimit_enabled;
 
-/// Initialize the video core
-bool Init(EmuWindow& emu_window) {
-    g_renderer = std::make_unique<RendererOpenGL>(emu_window);
-    if (g_renderer->Init()) {
-        LOG_DEBUG(Render, "initialized OK");
-    } else {
-        LOG_CRITICAL(Render, "initialization failed !");
-        return false;
-    }
-    return true;
-}
-
-/// Shutdown the video core
-void Shutdown() {
-    g_renderer.reset();
-
-    LOG_DEBUG(Render, "shutdown OK");
+std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window) {
+    return std::make_unique<RendererOpenGL>(emu_window);
 }
 
 } // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 519b757f50..7c01c0b8da 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -8,25 +8,23 @@
 #include <memory>
 
 class EmuWindow;
-class RendererBase;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Video Core namespace
 
 namespace VideoCore {
 
-enum class Renderer { Software, OpenGL };
+class RendererBase;
 
-extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
+enum class Renderer { Software, OpenGL };
 
 // TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
 // qt ui)
 extern std::atomic<bool> g_toggle_framelimit_enabled;
 
-/// Initialize the video core
-bool Init(EmuWindow& emu_window);
-
-/// Shutdown the video core
-void Shutdown();
+/**
+ * Creates a renderer instance.
+ *
+ * @note The returned renderer instance is simply allocated. Its Init()
+ *       function still needs to be called to fully complete its setup.
+ */
+std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window);
 
 } // namespace VideoCore
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 341867f7b3..955353a2c6 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -445,7 +445,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
 
         case Core::System::ResultStatus::ErrorVideoCore:
             QMessageBox::critical(
-                this, tr("An error occured in the video core."),
+                this, tr("An error occurred initializing the video core."),
                 tr("yuzu has encountered an error while running the video core, please see the "
                    "log for more details."
                    "For more information on accessing the log, please see the following page: "
@@ -459,7 +459,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
         default:
             QMessageBox::critical(
                 this, tr("Error while loading ROM!"),
-                tr("An unknown error occured. Please see the log for more details."));
+                tr("An unknown error occurred. Please see the log for more details."));
             break;
         }
         return false;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 5ff6266bff..5911ec1778 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -187,7 +187,7 @@ int main(int argc, char** argv) {
         LOG_CRITICAL(Frontend, "Failed to determine system mode!");
         return -1;
     case Core::System::ResultStatus::ErrorVideoCore:
-        LOG_CRITICAL(Frontend, "VideoCore not initialized");
+        LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
         return -1;
     case Core::System::ResultStatus::Success:
         break; // Expected case