From 195fedccf07b909c95e5905c7154c595bb260fc7 Mon Sep 17 00:00:00 2001
From: Yuri Kunde Schlesner <yuriks@yuriks.net>
Date: Sun, 6 Dec 2015 19:06:12 -0800
Subject: [PATCH] VideoCore: Unify interface to OpenGL and SW rasterizers

This removes explicit checks sprinkled all over the codebase to instead
just have the SW rasterizer expose an implementation with no-ops for
most operations.
---
 src/core/hle/service/gsp_gpu.cpp              |  8 +++---
 src/core/hle/service/y2r_u.cpp                |  2 +-
 src/core/hw/gpu.cpp                           | 12 ++++----
 src/video_core/CMakeLists.txt                 |  5 +++-
 src/video_core/clipper.cpp                    |  2 +-
 src/video_core/clipper.h                      |  2 +-
 src/video_core/command_processor.cpp          | 25 ++++++-----------
 src/video_core/debug_utils/debug_utils.cpp    |  6 ++--
 ...sterizer_base.h => rasterizer_interface.h} |  9 ++++--
 src/video_core/renderer_base.cpp              | 28 +++++++++++++++++++
 src/video_core/renderer_base.h                |  8 ++++--
 .../renderer_opengl/gl_rasterizer.cpp         |  9 ------
 .../renderer_opengl/gl_rasterizer.h           | 19 ++-----------
 .../renderer_opengl/renderer_opengl.cpp       | 15 ++--------
 src/video_core/swrasterizer.cpp               | 16 +++++++++++
 src/video_core/swrasterizer.h                 | 26 +++++++++++++++++
 16 files changed, 115 insertions(+), 77 deletions(-)
 rename src/video_core/{hwrasterizer_base.h => rasterizer_interface.h} (93%)
 create mode 100644 src/video_core/renderer_base.cpp
 create mode 100644 src/video_core/swrasterizer.cpp
 create mode 100644 src/video_core/swrasterizer.h

diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 3e4c70e184..98b11c7989 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -275,7 +275,7 @@ static void FlushDataCache(Service::Interface* self) {
     u32 size    = cmd_buff[2];
     u32 process = cmd_buff[4];
 
-    VideoCore::g_renderer->hw_rasterizer->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size);
+    VideoCore::g_renderer->rasterizer->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size);
 
     // TODO(purpasmart96): Verify return header on HW
 
@@ -365,7 +365,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
 
     // GX request DMA - typically used for copying memory from GSP heap to VRAM
     case CommandId::REQUEST_DMA:
-        VideoCore::g_renderer->hw_rasterizer->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
+        VideoCore::g_renderer->rasterizer->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
                                                             command.dma_request.size);
 
         memcpy(Memory::GetPointer(command.dma_request.dest_address),
@@ -373,7 +373,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
                command.dma_request.size);
         SignalInterrupt(InterruptId::DMA);
 
-        VideoCore::g_renderer->hw_rasterizer->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
+        VideoCore::g_renderer->rasterizer->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
                                                           command.dma_request.size);
         break;
 
@@ -467,7 +467,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
             if (region.size == 0)
                 break;
 
-            VideoCore::g_renderer->hw_rasterizer->InvalidateRegion(
+            VideoCore::g_renderer->rasterizer->InvalidateRegion(
                 Memory::VirtualToPhysicalAddress(region.address), region.size);
         }
         break;
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index face1d7b02..0429927f29 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -267,7 +267,7 @@ static void StartConversion(Service::Interface* self) {
     // dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
     u32 total_output_size = conversion.input_lines *
         (conversion.dst.transfer_unit + conversion.dst.gap);
-    VideoCore::g_renderer->hw_rasterizer->InvalidateRegion(
+    VideoCore::g_renderer->rasterizer->InvalidateRegion(
         Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size);
 
     LOG_DEBUG(Service_Y2R, "called");
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 55e215600d..4bd3a632da 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -26,7 +26,7 @@
 #include "core/tracer/recorder.h"
 
 #include "video_core/command_processor.h"
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/rasterizer_interface.h"
 #include "video_core/renderer_base.h"
 #include "video_core/utils.h"
 #include "video_core/video_core.h"
@@ -141,7 +141,7 @@ inline void Write(u32 addr, const T data) {
                     GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1);
                 }
 
-                VideoCore::g_renderer->hw_rasterizer->InvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress());
+                VideoCore::g_renderer->rasterizer->InvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress());
             }
 
             // Reset "trigger" flag and set the "finish" flag
@@ -172,7 +172,7 @@ inline void Write(u32 addr, const T data) {
                 u32 output_gap = config.texture_copy.output_gap * 16;
 
                 size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap);
-                VideoCore::g_renderer->hw_rasterizer->FlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size);
+                VideoCore::g_renderer->rasterizer->FlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size);
 
                 u32 remaining_size = config.texture_copy.size;
                 u32 remaining_input = input_width;
@@ -205,7 +205,7 @@ inline void Write(u32 addr, const T data) {
                     config.flags);
 
                 size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap);
-                VideoCore::g_renderer->hw_rasterizer->InvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size);
+                VideoCore::g_renderer->rasterizer->InvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size);
 
                 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
                 break;
@@ -232,7 +232,7 @@ inline void Write(u32 addr, const T data) {
             u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format);
             u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format);
 
-            VideoCore::g_renderer->hw_rasterizer->FlushRegion(config.GetPhysicalInputAddress(), input_size);
+            VideoCore::g_renderer->rasterizer->FlushRegion(config.GetPhysicalInputAddress(), input_size);
 
             for (u32 y = 0; y < output_height; ++y) {
                 for (u32 x = 0; x < output_width; ++x) {
@@ -339,7 +339,7 @@ inline void Write(u32 addr, const T data) {
             g_regs.display_transfer_config.trigger = 0;
             GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
 
-            VideoCore::g_renderer->hw_rasterizer->InvalidateRegion(config.GetPhysicalOutputAddress(), output_size);
+            VideoCore::g_renderer->rasterizer->InvalidateRegion(config.GetPhysicalOutputAddress(), output_size);
         }
         break;
     }
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 2a924f4ad0..c3d7294d58 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -11,8 +11,10 @@ set(SRCS
             pica.cpp
             primitive_assembly.cpp
             rasterizer.cpp
+            renderer_base.cpp
             shader/shader.cpp
             shader/shader_interpreter.cpp
+            swrasterizer.cpp
             utils.cpp
             video_core.cpp
             )
@@ -30,13 +32,14 @@ set(HEADERS
             clipper.h
             command_processor.h
             gpu_debugger.h
-            hwrasterizer_base.h
             pica.h
             primitive_assembly.h
             rasterizer.h
+            rasterizer_interface.h
             renderer_base.h
             shader/shader.h
             shader/shader_interpreter.h
+            swrasterizer.h
             utils.h
             video_core.h
             )
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp
index ed99c4f134..5d609da065 100644
--- a/src/video_core/clipper.cpp
+++ b/src/video_core/clipper.cpp
@@ -78,7 +78,7 @@ static void InitScreenCoordinates(OutputVertex& vtx)
     vtx.screenpos[2] = viewport.offset_z + vtx.pos.z * inv_w * viewport.zscale;
 }
 
-void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
+void ProcessTriangle(const OutputVertex &v0, const OutputVertex &v1, const OutputVertex &v2) {
     using boost::container::static_vector;
 
     // Clipping a planar n-gon against a plane will remove at least 1 vertex and introduces 2 at
diff --git a/src/video_core/clipper.h b/src/video_core/clipper.h
index 6ed01e8772..f85d8d4c90 100644
--- a/src/video_core/clipper.h
+++ b/src/video_core/clipper.h
@@ -14,7 +14,7 @@ namespace Clipper {
 
 using Shader::OutputVertex;
 
-void ProcessTriangle(OutputVertex& v0, OutputVertex& v1, OutputVertex& v2);
+void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2);
 
 } // namespace
 
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index bd1b09a4b8..35b976c604 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -336,19 +336,14 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
                     }
                 }
 
-                if (Settings::values.use_hw_renderer) {
-                    // Send to hardware renderer
-                    static auto AddHWTriangle = [](const Pica::Shader::OutputVertex& v0,
-                                                   const Pica::Shader::OutputVertex& v1,
-                                                   const Pica::Shader::OutputVertex& v2) {
-                        VideoCore::g_renderer->hw_rasterizer->AddTriangle(v0, v1, v2);
-                    };
+                // Send to renderer
+                using Pica::Shader::OutputVertex;
+                auto AddTriangle = [](
+                        const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2) {
+                    VideoCore::g_renderer->rasterizer->AddTriangle(v0, v1, v2);
+                };
 
-                    primitive_assembler.SubmitVertex(output, AddHWTriangle);
-                } else {
-                    // Send to triangle clipper
-                    primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle);
-                }
+                primitive_assembler.SubmitVertex(output, AddTriangle);
             }
 
             for (auto& range : memory_accesses.ranges) {
@@ -356,9 +351,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
                                                           range.second, range.first);
             }
 
-            if (Settings::values.use_hw_renderer) {
-                VideoCore::g_renderer->hw_rasterizer->DrawTriangles();
-            }
+            VideoCore::g_renderer->rasterizer->DrawTriangles();
 
 #if PICA_DUMP_GEOMETRY
             geometry_dumper.Dump();
@@ -475,7 +468,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
             break;
     }
 
-    VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id);
+    VideoCore::g_renderer->rasterizer->NotifyPicaRegisterChanged(id);
 
     if (g_debug_context)
         g_debug_context->OnEvent(DebugContext::Event::PicaCommandProcessed, reinterpret_cast<void*>(&id));
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 0e29661c71..4f66dbd658 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -46,10 +46,8 @@ void DebugContext::OnEvent(Event event, void* data) {
     {
         std::unique_lock<std::mutex> lock(breakpoint_mutex);
 
-        if (Settings::values.use_hw_renderer) {
-            // Commit the hardware renderer's framebuffer so it will show on debug widgets
-            VideoCore::g_renderer->hw_rasterizer->FlushFramebuffer();
-        }
+        // Commit the hardware renderer's framebuffer so it will show on debug widgets
+        VideoCore::g_renderer->rasterizer->FlushFramebuffer();
 
         // TODO: Should stop the CPU thread here once we multithread emulation.
 
diff --git a/src/video_core/hwrasterizer_base.h b/src/video_core/rasterizer_interface.h
similarity index 93%
rename from src/video_core/hwrasterizer_base.h
rename to src/video_core/rasterizer_interface.h
index b6390950a4..008c5827be 100644
--- a/src/video_core/hwrasterizer_base.h
+++ b/src/video_core/rasterizer_interface.h
@@ -12,10 +12,11 @@ struct OutputVertex;
 }
 }
 
-class HWRasterizer {
+namespace VideoCore {
+
+class RasterizerInterface {
 public:
-    virtual ~HWRasterizer() {
-    }
+    virtual ~RasterizerInterface() {}
 
     /// Initialize API-specific GPU objects
     virtual void InitObjects() = 0;
@@ -43,3 +44,5 @@ public:
     /// Notify rasterizer that any caches of the specified region should be discraded and reloaded from 3DS memory.
     virtual void InvalidateRegion(PAddr addr, u32 size) = 0;
 };
+
+}
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
new file mode 100644
index 0000000000..93e980216a
--- /dev/null
+++ b/src/video_core/renderer_base.cpp
@@ -0,0 +1,28 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+
+#include "common/make_unique.h"
+
+#include "core/settings.h"
+
+#include "video_core/renderer_base.h"
+#include "video_core/video_core.h"
+#include "video_core/swrasterizer.h"
+#include "video_core/renderer_opengl/gl_rasterizer.h"
+
+void RendererBase::RefreshRasterizerSetting() {
+    bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled;
+    if (rasterizer == nullptr || opengl_rasterizer_active != hw_renderer_enabled) {
+        opengl_rasterizer_active = hw_renderer_enabled;
+
+        if (hw_renderer_enabled) {
+            rasterizer = Common::make_unique<RasterizerOpenGL>();
+        } else {
+            rasterizer = Common::make_unique<VideoCore::SWRasterizer>();
+        }
+        rasterizer->InitObjects();
+    }
+}
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 6587bcf271..506bff8151 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -8,7 +8,7 @@
 
 #include "common/common_types.h"
 
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/rasterizer_interface.h"
 
 class EmuWindow;
 
@@ -54,10 +54,14 @@ public:
         return m_current_frame;
     }
 
-    std::unique_ptr<HWRasterizer> hw_rasterizer;
+    void RefreshRasterizerSetting();
+
+    std::unique_ptr<VideoCore::RasterizerInterface> rasterizer;
 
 protected:
     f32 m_current_fps;              ///< Current framerate, should be set by the renderer
     int m_current_frame;            ///< Current frame, should be set by the renderer
 
+private:
+    bool opengl_rasterizer_active = false;
 };
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cc7830688e..681fdca8d1 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -188,9 +188,6 @@ void RasterizerOpenGL::FlushFramebuffer() {
 void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
     const auto& regs = Pica::g_state.regs;
 
-    if (!Settings::values.use_hw_renderer)
-        return;
-
     switch(id) {
     // Culling
     case PICA_REG_INDEX(cull_mode):
@@ -287,9 +284,6 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
 void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) {
     const auto& regs = Pica::g_state.regs;
 
-    if (!Settings::values.use_hw_renderer)
-        return;
-
     PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
     u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
                             * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
@@ -309,9 +303,6 @@ void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) {
 void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) {
     const auto& regs = Pica::g_state.regs;
 
-    if (!Settings::values.use_hw_renderer)
-        return;
-
     PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
     u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
                             * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 378cdb65c3..92b1f812e8 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -14,7 +14,7 @@
 #include "common/hash.h"
 
 #include "video_core/pica.h"
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/rasterizer_interface.h"
 #include "video_core/renderer_opengl/gl_rasterizer_cache.h"
 #include "video_core/renderer_opengl/gl_state.h"
 #include "video_core/shader/shader_interpreter.h"
@@ -102,36 +102,21 @@ struct hash<PicaShaderConfig> {
 
 } // namespace std
 
-class RasterizerOpenGL : public HWRasterizer {
+class RasterizerOpenGL : public VideoCore::RasterizerInterface {
 public:
 
     RasterizerOpenGL();
     ~RasterizerOpenGL() override;
 
-    /// Initialize API-specific GPU objects
     void InitObjects() override;
-
-    /// Reset the rasterizer, such as flushing all caches and updating all state
     void Reset() override;
-
-    /// Queues the primitive formed by the given vertices for rendering
     void AddTriangle(const Pica::Shader::OutputVertex& v0,
                      const Pica::Shader::OutputVertex& v1,
                      const Pica::Shader::OutputVertex& v2) override;
-
-    /// Draw the current batch of triangles
     void DrawTriangles() override;
-
-    /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
     void FlushFramebuffer() override;
-
-    /// Notify rasterizer that the specified PICA register has been changed
     void NotifyPicaRegisterChanged(u32 id) override;
-
-    /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
     void FlushRegion(PAddr addr, u32 size) override;
-
-    /// Notify rasterizer that a 3DS memory region has been changed
     void InvalidateRegion(PAddr addr, u32 size) override;
 
     /// OpenGL shader generated for a given Pica register state
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 1420229ccd..c14bdb8ab9 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -93,7 +93,6 @@ static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const
 
 /// RendererOpenGL constructor
 RendererOpenGL::RendererOpenGL() {
-    hw_rasterizer.reset(new RasterizerOpenGL());
     resolution_width  = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
     resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
 }
@@ -157,15 +156,7 @@ void RendererOpenGL::SwapBuffers() {
 
     profiler.BeginFrame();
 
-    bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled;
-    if (Settings::values.use_hw_renderer != hw_renderer_enabled) {
-        // TODO: Save new setting value to config file for next startup
-        Settings::values.use_hw_renderer = hw_renderer_enabled;
-
-        if (Settings::values.use_hw_renderer) {
-            hw_rasterizer->Reset();
-        }
-    }
+    RefreshRasterizerSetting();
 
     if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
         Pica::g_debug_context->recorder->FrameFinished();
@@ -286,8 +277,6 @@ void RendererOpenGL::InitOpenGLObjects() {
 
     state.texture_units[0].texture_2d = 0;
     state.Apply();
-
-    hw_rasterizer->InitObjects();
 }
 
 void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@@ -419,6 +408,8 @@ void RendererOpenGL::Init() {
     LOG_INFO(Render_OpenGL, "GL_VENDOR: %s", glGetString(GL_VENDOR));
     LOG_INFO(Render_OpenGL, "GL_RENDERER: %s", glGetString(GL_RENDERER));
     InitOpenGLObjects();
+
+    RefreshRasterizerSetting();
 }
 
 /// Shutdown the renderer
diff --git a/src/video_core/swrasterizer.cpp b/src/video_core/swrasterizer.cpp
new file mode 100644
index 0000000000..03df15b013
--- /dev/null
+++ b/src/video_core/swrasterizer.cpp
@@ -0,0 +1,16 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/clipper.h"
+#include "video_core/swrasterizer.h"
+
+namespace VideoCore {
+
+void SWRasterizer::AddTriangle(const Pica::Shader::OutputVertex& v0,
+        const Pica::Shader::OutputVertex& v1,
+        const Pica::Shader::OutputVertex& v2) {
+    Pica::Clipper::ProcessTriangle(v0, v1, v2);
+}
+
+}
diff --git a/src/video_core/swrasterizer.h b/src/video_core/swrasterizer.h
new file mode 100644
index 0000000000..e9a4e39c6e
--- /dev/null
+++ b/src/video_core/swrasterizer.h
@@ -0,0 +1,26 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "video_core/rasterizer_interface.h"
+
+namespace VideoCore {
+
+class SWRasterizer : public RasterizerInterface {
+    void InitObjects() override {}
+    void Reset() override {}
+    void AddTriangle(const Pica::Shader::OutputVertex& v0,
+            const Pica::Shader::OutputVertex& v1,
+            const Pica::Shader::OutputVertex& v2);
+    void DrawTriangles() override {}
+    void FlushFramebuffer() override {}
+    void NotifyPicaRegisterChanged(u32 id) override {}
+    void FlushRegion(PAddr addr, u32 size) override {}
+    void InvalidateRegion(PAddr addr, u32 size) override {}
+};
+
+}