summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar tfarley2015-05-18 21:21:33 -0700
committerGravatar tfarley2015-05-22 15:51:18 -0700
commit05dc633a8c35221ce8d6abe6ddf027f8b0bab6c2 (patch)
treed080c1efd3b928bda551cb9eee304547e66a4351
parentINI hw/sw renderer toggle (diff)
downloadyuzu-05dc633a8c35221ce8d6abe6ddf027f8b0bab6c2.tar.gz
yuzu-05dc633a8c35221ce8d6abe6ddf027f8b0bab6c2.tar.xz
yuzu-05dc633a8c35221ce8d6abe6ddf027f8b0bab6c2.zip
OpenGL renderer
-rw-r--r--src/citra/citra.cpp4
-rw-r--r--src/citra_qt/main.cpp9
-rw-r--r--src/citra_qt/main.h1
-rw-r--r--src/citra_qt/main.ui9
-rw-r--r--src/common/math_util.h4
-rw-r--r--src/core/hle/service/gsp_gpu.cpp9
-rw-r--r--src/core/hw/gpu.cpp16
-rw-r--r--src/video_core/CMakeLists.txt12
-rw-r--r--src/video_core/command_processor.cpp26
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp4
-rw-r--r--src/video_core/hwrasterizer_base.h40
-rw-r--r--src/video_core/pica.h37
-rw-r--r--src/video_core/rasterizer.cpp20
-rw-r--r--src/video_core/renderer_base.h4
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp879
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h207
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp77
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h36
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp111
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h79
-rw-r--r--src/video_core/renderer_opengl/gl_shaders.h288
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp160
-rw-r--r--src/video_core/renderer_opengl/gl_state.h70
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h105
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp61
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h16
-rw-r--r--src/video_core/video_core.cpp3
-rw-r--r--src/video_core/video_core.h5
28 files changed, 2245 insertions, 47 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index ca93d5b91..ce8d7dd25 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -18,6 +18,8 @@
18#include "citra/config.h" 18#include "citra/config.h"
19#include "citra/emu_window/emu_window_glfw.h" 19#include "citra/emu_window/emu_window_glfw.h"
20 20
21#include "video_core/video_core.h"
22
21/// Application entry point 23/// Application entry point
22int main(int argc, char **argv) { 24int main(int argc, char **argv) {
23 Log::Filter log_filter(Log::Level::Debug); 25 Log::Filter log_filter(Log::Level::Debug);
@@ -34,6 +36,8 @@ int main(int argc, char **argv) {
34 std::string boot_filename = argv[1]; 36 std::string boot_filename = argv[1];
35 EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; 37 EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
36 38
39 VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer;
40
37 System::Init(emu_window); 41 System::Init(emu_window);
38 42
39 Loader::ResultStatus load_result = Loader::LoadFile(boot_filename); 43 Loader::ResultStatus load_result = Loader::LoadFile(boot_filename);
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 24506deab..2ea33ebc5 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -44,6 +44,8 @@
44#include "core/arm/disassembler/load_symbol_map.h" 44#include "core/arm/disassembler/load_symbol_map.h"
45#include "citra_qt/config.h" 45#include "citra_qt/config.h"
46 46
47#include "video_core/video_core.h"
48
47#include "version.h" 49#include "version.h"
48 50
49GMainWindow::GMainWindow() : emu_thread(nullptr) 51GMainWindow::GMainWindow() : emu_thread(nullptr)
@@ -123,6 +125,9 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
123 restoreState(settings.value("state").toByteArray()); 125 restoreState(settings.value("state").toByteArray());
124 render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray()); 126 render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray());
125 127
128 ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer);
129 SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked());
130
126 ui.action_Single_Window_Mode->setChecked(settings.value("singleWindowMode", true).toBool()); 131 ui.action_Single_Window_Mode->setChecked(settings.value("singleWindowMode", true).toBool());
127 ToggleWindowMode(); 132 ToggleWindowMode();
128 133
@@ -135,6 +140,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
135 connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame())); 140 connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
136 connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame())); 141 connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
137 connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame())); 142 connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
143 connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool)));
138 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); 144 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
139 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); 145 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
140 146
@@ -300,6 +306,9 @@ void GMainWindow::OnOpenHotkeysDialog()
300 dialog.exec(); 306 dialog.exec();
301} 307}
302 308
309void GMainWindow::SetHardwareRendererEnabled(bool enabled) {
310 VideoCore::g_hw_renderer_enabled = enabled;
311}
303 312
304void GMainWindow::ToggleWindowMode() { 313void GMainWindow::ToggleWindowMode() {
305 if (ui.action_Single_Window_Mode->isChecked()) { 314 if (ui.action_Single_Window_Mode->isChecked()) {
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 3e29534fb..242b08c39 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -69,6 +69,7 @@ private slots:
69 void OnOpenHotkeysDialog(); 69 void OnOpenHotkeysDialog();
70 void OnConfigure(); 70 void OnConfigure();
71 void OnDisplayTitleBars(bool); 71 void OnDisplayTitleBars(bool);
72 void SetHardwareRendererEnabled(bool);
72 void ToggleWindowMode(); 73 void ToggleWindowMode();
73 74
74private: 75private:
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index 689806465..0942c28c8 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -52,6 +52,7 @@
52 <addaction name="action_Pause"/> 52 <addaction name="action_Pause"/>
53 <addaction name="action_Stop"/> 53 <addaction name="action_Stop"/>
54 <addaction name="separator"/> 54 <addaction name="separator"/>
55 <addaction name="action_Use_Hardware_Renderer"/>
55 <addaction name="action_Configure"/> 56 <addaction name="action_Configure"/>
56 </widget> 57 </widget>
57 <widget class="QMenu" name="menu_View"> 58 <widget class="QMenu" name="menu_View">
@@ -131,6 +132,14 @@
131 <string>Configure &amp;Hotkeys ...</string> 132 <string>Configure &amp;Hotkeys ...</string>
132 </property> 133 </property>
133 </action> 134 </action>
135 <action name="action_Use_Hardware_Renderer">
136 <property name="checkable">
137 <bool>true</bool>
138 </property>
139 <property name="text">
140 <string>Use Hardware Renderer</string>
141 </property>
142 </action>
134 <action name="action_Configure"> 143 <action name="action_Configure">
135 <property name="text"> 144 <property name="text">
136 <string>Configure ...</string> 145 <string>Configure ...</string>
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 0b1400b41..4b0910741 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -11,6 +11,10 @@
11namespace MathUtil 11namespace MathUtil
12{ 12{
13 13
14inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, unsigned length1) {
15 return (std::max(start0, start1) <= std::min(start0 + length0, start1 + length1));
16}
17
14template<typename T> 18template<typename T>
15inline T Clamp(const T val, const T& min, const T& max) 19inline T Clamp(const T val, const T& min, const T& max)
16{ 20{
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index c11c5faba..c56475ae4 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -15,6 +15,7 @@
15#include "core/hw/lcd.h" 15#include "core/hw/lcd.h"
16 16
17#include "video_core/gpu_debugger.h" 17#include "video_core/gpu_debugger.h"
18#include "video_core/video_core.h"
18 19
19// Main graphics debugger object - TODO: Here is probably not the best place for this 20// Main graphics debugger object - TODO: Here is probably not the best place for this
20GraphicsDebugger g_debugger; 21GraphicsDebugger g_debugger;
@@ -264,6 +265,8 @@ static void FlushDataCache(Service::Interface* self) {
264 u32 size = cmd_buff[2]; 265 u32 size = cmd_buff[2];
265 u32 process = cmd_buff[4]; 266 u32 process = cmd_buff[4];
266 267
268 VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(address), size);
269
267 // TODO(purpasmart96): Verify return header on HW 270 // TODO(purpasmart96): Verify return header on HW
268 271
269 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 272 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -352,10 +355,16 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
352 355
353 // GX request DMA - typically used for copying memory from GSP heap to VRAM 356 // GX request DMA - typically used for copying memory from GSP heap to VRAM
354 case CommandId::REQUEST_DMA: 357 case CommandId::REQUEST_DMA:
358 VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
359 command.dma_request.size);
360
355 memcpy(Memory::GetPointer(command.dma_request.dest_address), 361 memcpy(Memory::GetPointer(command.dma_request.dest_address),
356 Memory::GetPointer(command.dma_request.source_address), 362 Memory::GetPointer(command.dma_request.source_address),
357 command.dma_request.size); 363 command.dma_request.size);
358 SignalInterrupt(InterruptId::DMA); 364 SignalInterrupt(InterruptId::DMA);
365
366 VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
367 command.dma_request.size);
359 break; 368 break;
360 369
361 // ctrulib homebrew sends all relevant command list data with this command, 370 // ctrulib homebrew sends all relevant command list data with this command,
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 8ef1f70df..ddc5d647e 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -106,6 +106,8 @@ inline void Write(u32 addr, const T data) {
106 } else { 106 } else {
107 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); 107 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1);
108 } 108 }
109
110 VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress());
109 } 111 }
110 break; 112 break;
111 } 113 }
@@ -129,19 +131,25 @@ inline void Write(u32 addr, const T data) {
129 u32 output_width = config.output_width / horizontal_scale; 131 u32 output_width = config.output_width / horizontal_scale;
130 u32 output_height = config.output_height / vertical_scale; 132 u32 output_height = config.output_height / vertical_scale;
131 133
134 u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format);
135 u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format);
136
137 VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), input_size);
138
132 if (config.raw_copy) { 139 if (config.raw_copy) {
133 // Raw copies do not perform color conversion nor tiled->linear / linear->tiled conversions 140 // Raw copies do not perform color conversion nor tiled->linear / linear->tiled conversions
134 // TODO(Subv): Verify if raw copies perform scaling 141 // TODO(Subv): Verify if raw copies perform scaling
135 memcpy(dst_pointer, src_pointer, config.output_width * config.output_height * 142 memcpy(dst_pointer, src_pointer, output_size);
136 GPU::Regs::BytesPerPixel(config.output_format));
137 143
138 LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), output format: %x, flags 0x%08X, Raw copy", 144 LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), output format: %x, flags 0x%08X, Raw copy",
139 config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format), 145 output_size,
140 config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(), 146 config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(),
141 config.GetPhysicalOutputAddress(), config.output_width.Value(), config.output_height.Value(), 147 config.GetPhysicalOutputAddress(), config.output_width.Value(), config.output_height.Value(),
142 config.output_format.Value(), config.flags); 148 config.output_format.Value(), config.flags);
143 149
144 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); 150 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
151
152 VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size);
145 break; 153 break;
146 } 154 }
147 155
@@ -247,6 +255,8 @@ inline void Write(u32 addr, const T data) {
247 config.output_format.Value(), config.flags); 255 config.output_format.Value(), config.flags);
248 256
249 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); 257 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
258
259 VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size);
250 } 260 }
251 break; 261 break;
252 } 262 }
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 4c1e6449a..9866078d4 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,7 +1,11 @@
1set(SRCS 1set(SRCS
2 renderer_opengl/generated/gl_3_2_core.c 2 renderer_opengl/generated/gl_3_2_core.c
3 renderer_opengl/renderer_opengl.cpp 3 renderer_opengl/gl_rasterizer.cpp
4 renderer_opengl/gl_rasterizer_cache.cpp
5 renderer_opengl/gl_resource_manager.cpp
4 renderer_opengl/gl_shader_util.cpp 6 renderer_opengl/gl_shader_util.cpp
7 renderer_opengl/gl_state.cpp
8 renderer_opengl/renderer_opengl.cpp
5 debug_utils/debug_utils.cpp 9 debug_utils/debug_utils.cpp
6 clipper.cpp 10 clipper.cpp
7 command_processor.cpp 11 command_processor.cpp
@@ -15,13 +19,19 @@ set(SRCS
15set(HEADERS 19set(HEADERS
16 debug_utils/debug_utils.h 20 debug_utils/debug_utils.h
17 renderer_opengl/generated/gl_3_2_core.h 21 renderer_opengl/generated/gl_3_2_core.h
22 renderer_opengl/gl_rasterizer.h
23 renderer_opengl/gl_rasterizer_cache.h
24 renderer_opengl/gl_resource_manager.h
18 renderer_opengl/gl_shader_util.h 25 renderer_opengl/gl_shader_util.h
19 renderer_opengl/gl_shaders.h 26 renderer_opengl/gl_shaders.h
27 renderer_opengl/gl_state.h
28 renderer_opengl/pica_to_gl.h
20 renderer_opengl/renderer_opengl.h 29 renderer_opengl/renderer_opengl.h
21 clipper.h 30 clipper.h
22 color.h 31 color.h
23 command_processor.h 32 command_processor.h
24 gpu_debugger.h 33 gpu_debugger.h
34 hwrasterizer_base.h
25 math.h 35 math.h
26 pica.h 36 pica.h
27 primitive_assembly.h 37 primitive_assembly.h
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 6121df8e3..5c4c04408 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -12,8 +12,10 @@
12#include "pica.h" 12#include "pica.h"
13#include "primitive_assembly.h" 13#include "primitive_assembly.h"
14#include "vertex_shader.h" 14#include "vertex_shader.h"
15#include "video_core.h"
15#include "core/hle/service/gsp_gpu.h" 16#include "core/hle/service/gsp_gpu.h"
16#include "core/hw/gpu.h" 17#include "core/hw/gpu.h"
18#include "core/settings.h"
17 19
18#include "debug_utils/debug_utils.h" 20#include "debug_utils/debug_utils.h"
19 21
@@ -107,7 +109,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
107 bool index_u16 = index_info.format != 0; 109 bool index_u16 = index_info.format != 0;
108 110
109 DebugUtils::GeometryDumper geometry_dumper; 111 DebugUtils::GeometryDumper geometry_dumper;
110 PrimitiveAssembler<VertexShader::OutputVertex> clipper_primitive_assembler(registers.triangle_topology.Value()); 112 PrimitiveAssembler<VertexShader::OutputVertex> primitive_assembler(registers.triangle_topology.Value());
111 PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(registers.triangle_topology.Value()); 113 PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(registers.triangle_topology.Value());
112 114
113 for (unsigned int index = 0; index < registers.num_vertices; ++index) 115 for (unsigned int index = 0; index < registers.num_vertices; ++index)
@@ -185,9 +187,25 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
185 // TODO: Add processed vertex to vertex cache! 187 // TODO: Add processed vertex to vertex cache!
186 } 188 }
187 189
188 // Send to triangle clipper 190 if (Settings::values.use_hw_renderer) {
189 clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle); 191 // Send to hardware renderer
192 static auto AddHWTriangle = [](const Pica::VertexShader::OutputVertex& v0,
193 const Pica::VertexShader::OutputVertex& v1,
194 const Pica::VertexShader::OutputVertex& v2) {
195 VideoCore::g_renderer->hw_rasterizer->AddTriangle(v0, v1, v2);
196 };
197
198 primitive_assembler.SubmitVertex(output, AddHWTriangle);
199 } else {
200 // Send to triangle clipper
201 primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle);
202 }
190 } 203 }
204
205 if (Settings::values.use_hw_renderer) {
206 VideoCore::g_renderer->hw_rasterizer->DrawTriangles();
207 }
208
191 geometry_dumper.Dump(); 209 geometry_dumper.Dump();
192 210
193 if (g_debug_context) 211 if (g_debug_context)
@@ -340,6 +358,8 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
340 break; 358 break;
341 } 359 }
342 360
361 VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id);
362
343 if (g_debug_context) 363 if (g_debug_context)
344 g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id)); 364 g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id));
345} 365}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 883df48a5..9da44ccd6 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -24,6 +24,7 @@
24#include "video_core/math.h" 24#include "video_core/math.h"
25#include "video_core/pica.h" 25#include "video_core/pica.h"
26#include "video_core/utils.h" 26#include "video_core/utils.h"
27#include "video_core/video_core.h"
27 28
28#include "debug_utils.h" 29#include "debug_utils.h"
29 30
@@ -40,6 +41,9 @@ void DebugContext::OnEvent(Event event, void* data) {
40 { 41 {
41 std::unique_lock<std::mutex> lock(breakpoint_mutex); 42 std::unique_lock<std::mutex> lock(breakpoint_mutex);
42 43
44 // Commit the hardware renderer's framebuffer so it will show on debug widgets
45 VideoCore::g_renderer->hw_rasterizer->CommitFramebuffer();
46
43 // TODO: Should stop the CPU thread here once we multithread emulation. 47 // TODO: Should stop the CPU thread here once we multithread emulation.
44 48
45 active_breakpoint = event; 49 active_breakpoint = event;
diff --git a/src/video_core/hwrasterizer_base.h b/src/video_core/hwrasterizer_base.h
new file mode 100644
index 000000000..dec193f8b
--- /dev/null
+++ b/src/video_core/hwrasterizer_base.h
@@ -0,0 +1,40 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/emu_window.h"
8#include "video_core/vertex_shader.h"
9
10class HWRasterizer {
11public:
12 virtual ~HWRasterizer() {
13 }
14
15 /// Initialize API-specific GPU objects
16 virtual void InitObjects() = 0;
17
18 /// Reset the rasterizer, such as flushing all caches and updating all state
19 virtual void Reset() = 0;
20
21 /// Queues the primitive formed by the given vertices for rendering
22 virtual void AddTriangle(const Pica::VertexShader::OutputVertex& v0,
23 const Pica::VertexShader::OutputVertex& v1,
24 const Pica::VertexShader::OutputVertex& v2) = 0;
25
26 /// Draw the current batch of triangles
27 virtual void DrawTriangles() = 0;
28
29 /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
30 virtual void CommitFramebuffer() = 0;
31
32 /// Notify rasterizer that the specified PICA register has been changed
33 virtual void NotifyPicaRegisterChanged(u32 id) = 0;
34
35 /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
36 virtual void NotifyPreRead(PAddr addr, u32 size) = 0;
37
38 /// Notify rasterizer that a 3DS memory region has been changed
39 virtual void NotifyFlush(PAddr addr, u32 size) = 0;
40};
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index e9bc7fb3b..503c09eca 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -490,20 +490,37 @@ struct Regs {
490 } 490 }
491 } 491 }
492 492
493 struct { 493 // Components are laid out in reverse byte order, most significant bits first.
494 // Components are laid out in reverse byte order, most significant bits first. 494 enum ColorFormat : u32 {
495 enum ColorFormat : u32 { 495 RGBA8 = 0,
496 RGBA8 = 0, 496 RGB8 = 1,
497 RGB8 = 1, 497 RGB5A1 = 2,
498 RGB5A1 = 2, 498 RGB565 = 3,
499 RGB565 = 3, 499 RGBA4 = 4,
500 RGBA4 = 4, 500 };
501 };
502 501
502 // Returns the number of bytes in the specified color format
503 static unsigned BytesPerColorPixel(ColorFormat format) {
504 switch (format) {
505 case ColorFormat::RGBA8:
506 return 4;
507 case ColorFormat::RGB8:
508 return 3;
509 case ColorFormat::RGB5A1:
510 case ColorFormat::RGB565:
511 case ColorFormat::RGBA4:
512 return 2;
513 default:
514 LOG_CRITICAL(HW_GPU, "Unknown color format %u", format);
515 UNIMPLEMENTED();
516 }
517 }
518
519 struct {
503 INSERT_PADDING_WORDS(0x6); 520 INSERT_PADDING_WORDS(0x6);
504 521
505 DepthFormat depth_format; 522 DepthFormat depth_format;
506 BitField<16, 3, u32> color_format; 523 BitField<16, 3, ColorFormat> color_format;
507 524
508 INSERT_PADDING_WORDS(0x4); 525 INSERT_PADDING_WORDS(0x4);
509 526
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index 59eff48f9..362efe52e 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -36,23 +36,23 @@ static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
36 u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset; 36 u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset;
37 37
38 switch (registers.framebuffer.color_format) { 38 switch (registers.framebuffer.color_format) {
39 case registers.framebuffer.RGBA8: 39 case Pica::Regs::ColorFormat::RGBA8:
40 Color::EncodeRGBA8(color, dst_pixel); 40 Color::EncodeRGBA8(color, dst_pixel);
41 break; 41 break;
42 42
43 case registers.framebuffer.RGB8: 43 case Pica::Regs::ColorFormat::RGB8:
44 Color::EncodeRGB8(color, dst_pixel); 44 Color::EncodeRGB8(color, dst_pixel);
45 break; 45 break;
46 46
47 case registers.framebuffer.RGB5A1: 47 case Pica::Regs::ColorFormat::RGB5A1:
48 Color::EncodeRGB5A1(color, dst_pixel); 48 Color::EncodeRGB5A1(color, dst_pixel);
49 break; 49 break;
50 50
51 case registers.framebuffer.RGB565: 51 case Pica::Regs::ColorFormat::RGB565:
52 Color::EncodeRGB565(color, dst_pixel); 52 Color::EncodeRGB565(color, dst_pixel);
53 break; 53 break;
54 54
55 case registers.framebuffer.RGBA4: 55 case Pica::Regs::ColorFormat::RGBA4:
56 Color::EncodeRGBA4(color, dst_pixel); 56 Color::EncodeRGBA4(color, dst_pixel);
57 break; 57 break;
58 58
@@ -73,19 +73,19 @@ static const Math::Vec4<u8> GetPixel(int x, int y) {
73 u8* src_pixel = Memory::GetPhysicalPointer(addr) + src_offset; 73 u8* src_pixel = Memory::GetPhysicalPointer(addr) + src_offset;
74 74
75 switch (registers.framebuffer.color_format) { 75 switch (registers.framebuffer.color_format) {
76 case registers.framebuffer.RGBA8: 76 case Pica::Regs::ColorFormat::RGBA8:
77 return Color::DecodeRGBA8(src_pixel); 77 return Color::DecodeRGBA8(src_pixel);
78 78
79 case registers.framebuffer.RGB8: 79 case Pica::Regs::ColorFormat::RGB8:
80 return Color::DecodeRGB8(src_pixel); 80 return Color::DecodeRGB8(src_pixel);
81 81
82 case registers.framebuffer.RGB5A1: 82 case Pica::Regs::ColorFormat::RGB5A1:
83 return Color::DecodeRGB5A1(src_pixel); 83 return Color::DecodeRGB5A1(src_pixel);
84 84
85 case registers.framebuffer.RGB565: 85 case Pica::Regs::ColorFormat::RGB565:
86 return Color::DecodeRGB565(src_pixel); 86 return Color::DecodeRGB565(src_pixel);
87 87
88 case registers.framebuffer.RGBA4: 88 case Pica::Regs::ColorFormat::RGBA4:
89 return Color::DecodeRGBA4(src_pixel); 89 return Color::DecodeRGBA4(src_pixel);
90 90
91 default: 91 default:
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index b62409538..5757ac75d 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -6,6 +6,8 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9#include "video_core/hwrasterizer_base.h"
10
9class RendererBase : NonCopyable { 11class RendererBase : NonCopyable {
10public: 12public:
11 13
@@ -48,6 +50,8 @@ public:
48 return m_current_frame; 50 return m_current_frame;
49 } 51 }
50 52
53 std::unique_ptr<HWRasterizer> hw_rasterizer;
54
51protected: 55protected:
52 f32 m_current_fps; ///< Current framerate, should be set by the renderer 56 f32 m_current_fps; ///< Current framerate, should be set by the renderer
53 int m_current_frame; ///< Current frame, should be set by the renderer 57 int m_current_frame; ///< Current frame, should be set by the renderer
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
new file mode 100644
index 000000000..e44375547
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -0,0 +1,879 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/settings.h"
6#include "core/hw/gpu.h"
7
8#include "video_core/color.h"
9#include "video_core/pica.h"
10#include "video_core/utils.h"
11#include "video_core/renderer_opengl/gl_rasterizer.h"
12#include "video_core/renderer_opengl/gl_shaders.h"
13#include "video_core/renderer_opengl/gl_shader_util.h"
14#include "video_core/renderer_opengl/pica_to_gl.h"
15
16#include "generated/gl_3_2_core.h"
17
18#include <memory>
19
20static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) {
21 return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace &&
22 stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace &&
23 stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
24 stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
25 stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor &&
26 stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha &&
27 stage.GetColorMultiplier() == 1 &&
28 stage.GetAlphaMultiplier() == 1);
29}
30
31RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr(0) { }
32RasterizerOpenGL::~RasterizerOpenGL() { }
33
34void RasterizerOpenGL::InitObjects() {
35 // Create the hardware shader program and get attrib/uniform locations
36 shader.Create(GLShaders::g_vertex_shader_hw, GLShaders::g_fragment_shader_hw);
37 attrib_position = glGetAttribLocation(shader.handle, "vert_position");
38 attrib_color = glGetAttribLocation(shader.handle, "vert_color");
39 attrib_texcoords = glGetAttribLocation(shader.handle, "vert_texcoords");
40
41 uniform_alphatest_enabled = glGetUniformLocation(shader.handle, "alphatest_enabled");
42 uniform_alphatest_func = glGetUniformLocation(shader.handle, "alphatest_func");
43 uniform_alphatest_ref = glGetUniformLocation(shader.handle, "alphatest_ref");
44
45 uniform_tex = glGetUniformLocation(shader.handle, "tex");
46
47 uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.handle, "tev_combiner_buffer_color");
48
49 const auto tev_stages = Pica::registers.GetTevStages();
50 for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
51 auto& uniform_tev_cfg = uniform_tev_cfgs[tev_stage_index];
52
53 std::string tev_ref_str = "tev_cfgs[" + std::to_string(tev_stage_index) + "]";
54 uniform_tev_cfg.enabled = glGetUniformLocation(shader.handle, (tev_ref_str + ".enabled").c_str());
55 uniform_tev_cfg.color_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_sources").c_str());
56 uniform_tev_cfg.alpha_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_sources").c_str());
57 uniform_tev_cfg.color_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_modifiers").c_str());
58 uniform_tev_cfg.alpha_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_modifiers").c_str());
59 uniform_tev_cfg.color_alpha_op = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_op").c_str());
60 uniform_tev_cfg.color_alpha_multiplier = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_multiplier").c_str());
61 uniform_tev_cfg.const_color = glGetUniformLocation(shader.handle, (tev_ref_str + ".const_color").c_str());
62 uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str());
63 }
64
65 // Generate VBO and VAO
66 vertex_buffer.Create();
67 vertex_array.Create();
68
69 // Update OpenGL state
70 state.draw.vertex_array = vertex_array.handle;
71 state.draw.vertex_buffer = vertex_buffer.handle;
72 state.draw.shader_program = shader.handle;
73
74 state.Apply();
75
76 // Set the texture samplers to correspond to different texture units
77 glUniform1i(uniform_tex, 0);
78 glUniform1i(uniform_tex + 1, 1);
79 glUniform1i(uniform_tex + 2, 2);
80
81 // Set vertex attributes
82 glVertexAttribPointer(attrib_position, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
83 glVertexAttribPointer(attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
84 glVertexAttribPointer(attrib_texcoords, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
85 glVertexAttribPointer(attrib_texcoords + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
86 glVertexAttribPointer(attrib_texcoords + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
87 glEnableVertexAttribArray(attrib_position);
88 glEnableVertexAttribArray(attrib_color);
89 glEnableVertexAttribArray(attrib_texcoords);
90 glEnableVertexAttribArray(attrib_texcoords + 1);
91 glEnableVertexAttribArray(attrib_texcoords + 2);
92
93 // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
94 fb_color_texture.texture.Create();
95 ReconfigureColorTexture(fb_color_texture, Pica::Regs::ColorFormat::RGBA8, 1, 1);
96 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
97 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
98 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
99 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
100 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
101
102 fb_depth_texture.texture.Create();
103 ReconfigureDepthTexture(fb_depth_texture, Pica::Regs::DepthFormat::D16, 1, 1);
104 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
105 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
106 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
107 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
108 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
109 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
110 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
111
112 // Configure OpenGL framebuffer
113 framebuffer.Create();
114
115 state.draw.framebuffer = framebuffer.handle;
116
117 // Unbind texture to allow binding to framebuffer
118 state.texture_units[0].enabled_2d = true;
119 state.texture_units[0].texture_2d = 0;
120 state.Apply();
121
122 glActiveTexture(GL_TEXTURE0);
123 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0);
124 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0);
125
126 ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
127 "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER));
128}
129
130void RasterizerOpenGL::Reset() {
131 SyncCullMode();
132 SyncBlendEnabled();
133 SyncBlendFuncs();
134 SyncBlendColor();
135 SyncAlphaTest();
136 SyncStencilTest();
137 SyncDepthTest();
138
139 // TEV stage 0
140 SyncTevSources(0, Pica::registers.tev_stage0);
141 SyncTevModifiers(0, Pica::registers.tev_stage0);
142 SyncTevOps(0, Pica::registers.tev_stage0);
143 SyncTevColor(0, Pica::registers.tev_stage0);
144 SyncTevMultipliers(0, Pica::registers.tev_stage0);
145
146 // TEV stage 1
147 SyncTevSources(1, Pica::registers.tev_stage1);
148 SyncTevModifiers(1, Pica::registers.tev_stage1);
149 SyncTevOps(1, Pica::registers.tev_stage1);
150 SyncTevColor(1, Pica::registers.tev_stage1);
151 SyncTevMultipliers(1, Pica::registers.tev_stage1);
152
153 // TEV stage 2
154 SyncTevSources(2, Pica::registers.tev_stage2);
155 SyncTevModifiers(2, Pica::registers.tev_stage2);
156 SyncTevOps(2, Pica::registers.tev_stage2);
157 SyncTevColor(2, Pica::registers.tev_stage2);
158 SyncTevMultipliers(2, Pica::registers.tev_stage2);
159
160 // TEV stage 3
161 SyncTevSources(3, Pica::registers.tev_stage3);
162 SyncTevModifiers(3, Pica::registers.tev_stage3);
163 SyncTevOps(3, Pica::registers.tev_stage3);
164 SyncTevColor(3, Pica::registers.tev_stage3);
165 SyncTevMultipliers(3, Pica::registers.tev_stage3);
166
167 // TEV stage 4
168 SyncTevSources(4, Pica::registers.tev_stage4);
169 SyncTevModifiers(4, Pica::registers.tev_stage4);
170 SyncTevOps(4, Pica::registers.tev_stage4);
171 SyncTevColor(4, Pica::registers.tev_stage4);
172 SyncTevMultipliers(4, Pica::registers.tev_stage4);
173
174 // TEV stage 5
175 SyncTevSources(5, Pica::registers.tev_stage5);
176 SyncTevModifiers(5, Pica::registers.tev_stage5);
177 SyncTevOps(5, Pica::registers.tev_stage5);
178 SyncTevColor(5, Pica::registers.tev_stage5);
179 SyncTevMultipliers(5, Pica::registers.tev_stage5);
180
181 SyncCombinerColor();
182 SyncCombinerWriteFlags();
183
184 res_cache.FullFlush();
185}
186
187void RasterizerOpenGL::AddTriangle(const Pica::VertexShader::OutputVertex& v0,
188 const Pica::VertexShader::OutputVertex& v1,
189 const Pica::VertexShader::OutputVertex& v2) {
190 vertex_batch.push_back(HardwareVertex(v0));
191 vertex_batch.push_back(HardwareVertex(v1));
192 vertex_batch.push_back(HardwareVertex(v2));
193}
194
195void RasterizerOpenGL::DrawTriangles() {
196 SyncFramebuffer();
197 SyncDrawState();
198
199 glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW);
200 glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
201
202 vertex_batch.clear();
203
204 // TODO: Flush the resource cache at the current depth and color framebuffer addresses for render-to-texture
205}
206
207void RasterizerOpenGL::CommitFramebuffer() {
208 CommitColorBuffer();
209 CommitDepthBuffer();
210}
211
212void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
213 if (!Settings::values.use_hw_renderer)
214 return;
215
216 switch(id) {
217 // Culling
218 case PICA_REG_INDEX(cull_mode):
219 SyncCullMode();
220 break;
221
222 // Blending
223 case PICA_REG_INDEX(output_merger.alphablend_enable):
224 SyncBlendEnabled();
225 break;
226 case PICA_REG_INDEX(output_merger.alpha_blending):
227 SyncBlendFuncs();
228 break;
229 case PICA_REG_INDEX(output_merger.blend_const):
230 SyncBlendColor();
231 break;
232
233 // Alpha test
234 case PICA_REG_INDEX(output_merger.alpha_test):
235 SyncAlphaTest();
236 break;
237
238 // Stencil test
239 case PICA_REG_INDEX(output_merger.stencil_test):
240 SyncStencilTest();
241 break;
242
243 // Depth test
244 case PICA_REG_INDEX(output_merger.depth_test_enable):
245 SyncDepthTest();
246 break;
247
248 // TEV stage 0
249 case PICA_REG_INDEX(tev_stage0.color_source1):
250 SyncTevSources(0, Pica::registers.tev_stage0);
251 break;
252 case PICA_REG_INDEX(tev_stage0.color_modifier1):
253 SyncTevModifiers(0, Pica::registers.tev_stage0);
254 break;
255 case PICA_REG_INDEX(tev_stage0.color_op):
256 SyncTevOps(0, Pica::registers.tev_stage0);
257 break;
258 case PICA_REG_INDEX(tev_stage0.const_r):
259 SyncTevColor(0, Pica::registers.tev_stage0);
260 break;
261 case PICA_REG_INDEX(tev_stage0.color_scale):
262 SyncTevMultipliers(0, Pica::registers.tev_stage0);
263 break;
264
265 // TEV stage 1
266 case PICA_REG_INDEX(tev_stage1.color_source1):
267 SyncTevSources(1, Pica::registers.tev_stage1);
268 break;
269 case PICA_REG_INDEX(tev_stage1.color_modifier1):
270 SyncTevModifiers(1, Pica::registers.tev_stage1);
271 break;
272 case PICA_REG_INDEX(tev_stage1.color_op):
273 SyncTevOps(1, Pica::registers.tev_stage1);
274 break;
275 case PICA_REG_INDEX(tev_stage1.const_r):
276 SyncTevColor(1, Pica::registers.tev_stage1);
277 break;
278 case PICA_REG_INDEX(tev_stage1.color_scale):
279 SyncTevMultipliers(1, Pica::registers.tev_stage1);
280 break;
281
282 // TEV stage 2
283 case PICA_REG_INDEX(tev_stage2.color_source1):
284 SyncTevSources(2, Pica::registers.tev_stage2);
285 break;
286 case PICA_REG_INDEX(tev_stage2.color_modifier1):
287 SyncTevModifiers(2, Pica::registers.tev_stage2);
288 break;
289 case PICA_REG_INDEX(tev_stage2.color_op):
290 SyncTevOps(2, Pica::registers.tev_stage2);
291 break;
292 case PICA_REG_INDEX(tev_stage2.const_r):
293 SyncTevColor(2, Pica::registers.tev_stage2);
294 break;
295 case PICA_REG_INDEX(tev_stage2.color_scale):
296 SyncTevMultipliers(2, Pica::registers.tev_stage2);
297 break;
298
299 // TEV stage 3
300 case PICA_REG_INDEX(tev_stage3.color_source1):
301 SyncTevSources(3, Pica::registers.tev_stage3);
302 break;
303 case PICA_REG_INDEX(tev_stage3.color_modifier1):
304 SyncTevModifiers(3, Pica::registers.tev_stage3);
305 break;
306 case PICA_REG_INDEX(tev_stage3.color_op):
307 SyncTevOps(3, Pica::registers.tev_stage3);
308 break;
309 case PICA_REG_INDEX(tev_stage3.const_r):
310 SyncTevColor(3, Pica::registers.tev_stage3);
311 break;
312 case PICA_REG_INDEX(tev_stage3.color_scale):
313 SyncTevMultipliers(3, Pica::registers.tev_stage3);
314 break;
315
316 // TEV stage 4
317 case PICA_REG_INDEX(tev_stage4.color_source1):
318 SyncTevSources(4, Pica::registers.tev_stage4);
319 break;
320 case PICA_REG_INDEX(tev_stage4.color_modifier1):
321 SyncTevModifiers(4, Pica::registers.tev_stage4);
322 break;
323 case PICA_REG_INDEX(tev_stage4.color_op):
324 SyncTevOps(4, Pica::registers.tev_stage4);
325 break;
326 case PICA_REG_INDEX(tev_stage4.const_r):
327 SyncTevColor(4, Pica::registers.tev_stage4);
328 break;
329 case PICA_REG_INDEX(tev_stage4.color_scale):
330 SyncTevMultipliers(4, Pica::registers.tev_stage4);
331 break;
332
333 // TEV stage 5
334 case PICA_REG_INDEX(tev_stage5.color_source1):
335 SyncTevSources(5, Pica::registers.tev_stage5);
336 break;
337 case PICA_REG_INDEX(tev_stage5.color_modifier1):
338 SyncTevModifiers(5, Pica::registers.tev_stage5);
339 break;
340 case PICA_REG_INDEX(tev_stage5.color_op):
341 SyncTevOps(5, Pica::registers.tev_stage5);
342 break;
343 case PICA_REG_INDEX(tev_stage5.const_r):
344 SyncTevColor(5, Pica::registers.tev_stage5);
345 break;
346 case PICA_REG_INDEX(tev_stage5.color_scale):
347 SyncTevMultipliers(5, Pica::registers.tev_stage5);
348 break;
349
350 // TEV combiner buffer color
351 case PICA_REG_INDEX(tev_combiner_buffer_color):
352 SyncCombinerColor();
353 break;
354
355 // TEV combiner buffer write flags
356 case PICA_REG_INDEX(tev_combiner_buffer_input):
357 SyncCombinerWriteFlags();
358 break;
359 }
360}
361
362void RasterizerOpenGL::NotifyPreRead(PAddr addr, u32 size) {
363 if (!Settings::values.use_hw_renderer)
364 return;
365
366 PAddr cur_fb_color_addr = Pica::registers.framebuffer.GetColorBufferPhysicalAddress();
367 u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(Pica::registers.framebuffer.color_format)
368 * Pica::registers.framebuffer.GetWidth() * Pica::registers.framebuffer.GetHeight();
369
370 PAddr cur_fb_depth_addr = Pica::registers.framebuffer.GetDepthBufferPhysicalAddress();
371 u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(Pica::registers.framebuffer.depth_format)
372 * Pica::registers.framebuffer.GetWidth() * Pica::registers.framebuffer.GetHeight();
373
374 // If source memory region overlaps 3DS framebuffers, commit them before the copy happens
375 if (MathUtil::IntervalsIntersect(addr, size, cur_fb_color_addr, cur_fb_color_size))
376 CommitColorBuffer();
377
378 if (MathUtil::IntervalsIntersect(addr, size, cur_fb_depth_addr, cur_fb_depth_size))
379 CommitDepthBuffer();
380}
381
382void RasterizerOpenGL::NotifyFlush(PAddr addr, u32 size) {
383 if (!Settings::values.use_hw_renderer)
384 return;
385
386 PAddr cur_fb_color_addr = Pica::registers.framebuffer.GetColorBufferPhysicalAddress();
387 u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(Pica::registers.framebuffer.color_format)
388 * Pica::registers.framebuffer.GetWidth() * Pica::registers.framebuffer.GetHeight();
389
390 PAddr cur_fb_depth_addr = Pica::registers.framebuffer.GetDepthBufferPhysicalAddress();
391 u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(Pica::registers.framebuffer.depth_format)
392 * Pica::registers.framebuffer.GetWidth() * Pica::registers.framebuffer.GetHeight();
393
394 // If modified memory region overlaps 3DS framebuffers, reload their contents into OpenGL
395 if (MathUtil::IntervalsIntersect(addr, size, cur_fb_color_addr, cur_fb_color_size))
396 ReloadColorBuffer();
397
398 if (MathUtil::IntervalsIntersect(addr, size, cur_fb_depth_addr, cur_fb_depth_size))
399 ReloadDepthBuffer();
400
401 // Notify cache of flush in case the region touches a cached resource
402 res_cache.NotifyFlush(addr, size);
403}
404
405void RasterizerOpenGL::ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height) {
406 GLint internal_format;
407
408 texture.format = format;
409 texture.width = width;
410 texture.height = height;
411
412 switch (format) {
413 case Pica::Regs::ColorFormat::RGBA8:
414 internal_format = GL_RGBA;
415 texture.gl_format = GL_RGBA;
416 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8;
417 break;
418
419 case Pica::Regs::ColorFormat::RGB8:
420 // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every
421 // specific OpenGL type used in this function using native-endian (that is, little-endian
422 // mostly everywhere) for words or half-words.
423 // TODO: check how those behave on big-endian processors.
424 internal_format = GL_RGB;
425 texture.gl_format = GL_BGR;
426 texture.gl_type = GL_UNSIGNED_BYTE;
427 break;
428
429 case Pica::Regs::ColorFormat::RGB5A1:
430 internal_format = GL_RGBA;
431 texture.gl_format = GL_RGBA;
432 texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1;
433 break;
434
435 case Pica::Regs::ColorFormat::RGB565:
436 internal_format = GL_RGB;
437 texture.gl_format = GL_RGB;
438 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
439 break;
440
441 case Pica::Regs::ColorFormat::RGBA4:
442 internal_format = GL_RGBA;
443 texture.gl_format = GL_RGBA;
444 texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4;
445 break;
446
447 default:
448 LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer texture color format %x", format);
449 UNIMPLEMENTED();
450 break;
451 }
452
453 state.texture_units[0].enabled_2d = true;
454 state.texture_units[0].texture_2d = texture.texture.handle;
455 state.Apply();
456
457 glActiveTexture(GL_TEXTURE0);
458 glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
459 texture.gl_format, texture.gl_type, nullptr);
460}
461
462void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height) {
463 GLint internal_format;
464
465 texture.format = format;
466 texture.width = width;
467 texture.height = height;
468
469 switch (format) {
470 case Pica::Regs::DepthFormat::D16:
471 internal_format = GL_DEPTH_COMPONENT16;
472 texture.gl_format = GL_DEPTH_COMPONENT;
473 texture.gl_type = GL_UNSIGNED_SHORT;
474 break;
475
476 case Pica::Regs::DepthFormat::D24:
477 internal_format = GL_DEPTH_COMPONENT24;
478 texture.gl_format = GL_DEPTH_COMPONENT;
479 texture.gl_type = GL_UNSIGNED_INT_24_8;
480 break;
481
482 case Pica::Regs::DepthFormat::D24S8:
483 internal_format = GL_DEPTH24_STENCIL8;
484 texture.gl_format = GL_DEPTH_STENCIL;
485 texture.gl_type = GL_UNSIGNED_INT_24_8;
486 break;
487
488 default:
489 LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer texture depth format %x", format);
490 UNIMPLEMENTED();
491 break;
492 }
493
494 state.texture_units[0].enabled_2d = true;
495 state.texture_units[0].texture_2d = texture.texture.handle;
496 state.Apply();
497
498 glActiveTexture(GL_TEXTURE0);
499 glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
500 texture.gl_format, texture.gl_type, nullptr);
501}
502
503void RasterizerOpenGL::SyncFramebuffer() {
504 PAddr cur_fb_color_addr = Pica::registers.framebuffer.GetColorBufferPhysicalAddress();
505 Pica::Regs::ColorFormat new_fb_color_format = Pica::registers.framebuffer.color_format;
506
507 PAddr cur_fb_depth_addr = Pica::registers.framebuffer.GetDepthBufferPhysicalAddress();
508 Pica::Regs::DepthFormat new_fb_depth_format = Pica::registers.framebuffer.depth_format;
509
510 bool fb_size_changed = fb_color_texture.width != Pica::registers.framebuffer.GetWidth() ||
511 fb_color_texture.height != Pica::registers.framebuffer.GetHeight();
512
513 bool color_fb_prop_changed = fb_color_texture.format != new_fb_color_format ||
514 fb_size_changed;
515
516 bool depth_fb_prop_changed = fb_depth_texture.format != new_fb_depth_format ||
517 fb_size_changed;
518
519 bool color_fb_modified = last_fb_color_addr != cur_fb_color_addr ||
520 color_fb_prop_changed;
521
522 bool depth_fb_modified = last_fb_depth_addr != cur_fb_depth_addr ||
523 depth_fb_prop_changed;
524
525 // Commit if framebuffer modified in any way
526 if (color_fb_modified)
527 CommitColorBuffer();
528
529 if (depth_fb_modified)
530 CommitDepthBuffer();
531
532 // Reconfigure framebuffer textures if any property has changed
533 if (color_fb_prop_changed) {
534 ReconfigureColorTexture(fb_color_texture, new_fb_color_format,
535 Pica::registers.framebuffer.GetWidth(), Pica::registers.framebuffer.GetHeight());
536 }
537
538 if (depth_fb_prop_changed) {
539 ReconfigureDepthTexture(fb_depth_texture, new_fb_depth_format,
540 Pica::registers.framebuffer.GetWidth(), Pica::registers.framebuffer.GetHeight());
541
542 // Only attach depth buffer as stencil if it supports stencil
543 switch (new_fb_depth_format) {
544 case Pica::Regs::DepthFormat::D16:
545 case Pica::Regs::DepthFormat::D24:
546 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
547 break;
548
549 case Pica::Regs::DepthFormat::D24S8:
550 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0);
551 break;
552
553 default:
554 LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer depth format %x", new_fb_depth_format);
555 UNIMPLEMENTED();
556 break;
557 }
558 }
559
560 // Load buffer data again if fb modified in any way
561 if (color_fb_modified) {
562 last_fb_color_addr = cur_fb_color_addr;
563
564 ReloadColorBuffer();
565 }
566
567 if (depth_fb_modified) {
568 last_fb_depth_addr = cur_fb_depth_addr;
569
570 ReloadDepthBuffer();
571 }
572}
573
574void RasterizerOpenGL::SyncCullMode() {
575 switch (Pica::registers.cull_mode) {
576 case Pica::Regs::CullMode::KeepAll:
577 state.cull.enabled = false;
578 break;
579
580 case Pica::Regs::CullMode::KeepClockWise:
581 state.cull.enabled = true;
582 state.cull.mode = GL_BACK;
583 break;
584
585 case Pica::Regs::CullMode::KeepCounterClockWise:
586 state.cull.enabled = true;
587 state.cull.mode = GL_FRONT;
588 break;
589
590 default:
591 LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %d", Pica::registers.cull_mode.Value());
592 UNIMPLEMENTED();
593 break;
594 }
595}
596
597void RasterizerOpenGL::SyncBlendEnabled() {
598 state.blend.enabled = Pica::registers.output_merger.alphablend_enable;
599}
600
601void RasterizerOpenGL::SyncBlendFuncs() {
602 state.blend.src_rgb_func = PicaToGL::BlendFunc(Pica::registers.output_merger.alpha_blending.factor_source_rgb);
603 state.blend.dst_rgb_func = PicaToGL::BlendFunc(Pica::registers.output_merger.alpha_blending.factor_dest_rgb);
604 state.blend.src_a_func = PicaToGL::BlendFunc(Pica::registers.output_merger.alpha_blending.factor_source_a);
605 state.blend.dst_a_func = PicaToGL::BlendFunc(Pica::registers.output_merger.alpha_blending.factor_dest_a);
606}
607
608void RasterizerOpenGL::SyncBlendColor() {
609 auto blend_color = PicaToGL::ColorRGBA8((u8*)&Pica::registers.output_merger.blend_const.r);
610 state.blend.color.red = blend_color[0];
611 state.blend.color.green = blend_color[1];
612 state.blend.color.blue = blend_color[2];
613 state.blend.color.alpha = blend_color[3];
614}
615
616void RasterizerOpenGL::SyncAlphaTest() {
617 glUniform1i(uniform_alphatest_enabled, Pica::registers.output_merger.alpha_test.enable);
618 glUniform1i(uniform_alphatest_func, Pica::registers.output_merger.alpha_test.func);
619 glUniform1f(uniform_alphatest_ref, Pica::registers.output_merger.alpha_test.ref / 255.0f);
620}
621
622void RasterizerOpenGL::SyncStencilTest() {
623 // TODO: Implement stencil test, mask, and op
624}
625
626void RasterizerOpenGL::SyncDepthTest() {
627 state.depth.test_enabled = Pica::registers.output_merger.depth_test_enable;
628 state.depth.test_func = PicaToGL::CompareFunc(Pica::registers.output_merger.depth_test_func);
629 state.depth.write_mask = Pica::registers.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE;
630}
631
632void RasterizerOpenGL::SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
633 GLint color_srcs[3] = { (GLint)config.color_source1.Value(),
634 (GLint)config.color_source2.Value(),
635 (GLint)config.color_source3.Value() };
636 GLint alpha_srcs[3] = { (GLint)config.alpha_source1.Value(),
637 (GLint)config.alpha_source2.Value(),
638 (GLint)config.alpha_source3.Value() };
639
640 glUniform3iv(uniform_tev_cfgs[stage_index].color_sources, 1, color_srcs);
641 glUniform3iv(uniform_tev_cfgs[stage_index].alpha_sources, 1, alpha_srcs);
642}
643
644void RasterizerOpenGL::SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
645 GLint color_mods[3] = { (GLint)config.color_modifier1.Value(),
646 (GLint)config.color_modifier2.Value(),
647 (GLint)config.color_modifier3.Value() };
648 GLint alpha_mods[3] = { (GLint)config.alpha_modifier1.Value(),
649 (GLint)config.alpha_modifier2.Value(),
650 (GLint)config.alpha_modifier3.Value() };
651
652 glUniform3iv(uniform_tev_cfgs[stage_index].color_modifiers, 1, color_mods);
653 glUniform3iv(uniform_tev_cfgs[stage_index].alpha_modifiers, 1, alpha_mods);
654}
655
656void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
657 glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_op, (GLint)config.color_op.Value(), (GLint)config.alpha_op.Value());
658}
659
660void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
661 auto const_color = PicaToGL::ColorRGBA8((u8*)&config.const_r);
662 glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data());
663}
664
665void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
666 glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_multiplier, config.GetColorMultiplier(), config.GetAlphaMultiplier());
667}
668
669void RasterizerOpenGL::SyncCombinerColor() {
670 auto combiner_color = PicaToGL::ColorRGBA8((u8*)&Pica::registers.tev_combiner_buffer_color.r);
671 glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data());
672}
673
674void RasterizerOpenGL::SyncCombinerWriteFlags() {
675 const auto tev_stages = Pica::registers.GetTevStages();
676 for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
677 glUniform2i(uniform_tev_cfgs[tev_stage_index].updates_combiner_buffer_color_alpha,
678 Pica::registers.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index),
679 Pica::registers.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index));
680 }
681}
682
683void RasterizerOpenGL::SyncDrawState() {
684 // Sync the viewport
685 GLsizei viewport_width = (GLsizei)Pica::float24::FromRawFloat24(Pica::registers.viewport_size_x).ToFloat32() * 2;
686 GLsizei viewport_height = (GLsizei)Pica::float24::FromRawFloat24(Pica::registers.viewport_size_y).ToFloat32() * 2;
687
688 // OpenGL uses different y coordinates, so negate corner offset and flip origin
689 // TODO: Ensure viewport_corner.x should not be negated or origin flipped
690 // TODO: Use floating-point viewports for accuracy if supported
691 glViewport((GLsizei)static_cast<float>(Pica::registers.viewport_corner.x),
692 -(GLsizei)static_cast<float>(Pica::registers.viewport_corner.y)
693 + Pica::registers.framebuffer.GetHeight() - viewport_height,
694 viewport_width, viewport_height);
695
696 // Sync bound texture(s), upload if not cached
697 const auto pica_textures = Pica::registers.GetTextures();
698 for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
699 const auto& texture = pica_textures[texture_index];
700
701 if (texture.enabled) {
702 state.texture_units[texture_index].enabled_2d = true;
703 res_cache.LoadAndBindTexture(state, texture_index, texture);
704 } else {
705 state.texture_units[texture_index].enabled_2d = false;
706 }
707 }
708
709 // Skip processing TEV stages that simply pass the previous stage results through
710 const auto tev_stages = Pica::registers.GetTevStages();
711 for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
712 glUniform1i(uniform_tev_cfgs[tev_stage_index].enabled, !IsPassThroughTevStage(tev_stages[tev_stage_index]));
713 }
714
715 state.Apply();
716}
717
718void RasterizerOpenGL::ReloadColorBuffer() {
719 u8* color_buffer = Memory::GetPhysicalPointer(Pica::registers.framebuffer.GetColorBufferPhysicalAddress());
720
721 if (color_buffer == nullptr)
722 return;
723
724 u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format);
725
726 std::unique_ptr<u8[]> temp_fb_color_buffer(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]);
727
728 // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary.
729 for (int y = 0; y < fb_color_texture.height; ++y) {
730 for (int x = 0; x < fb_color_texture.width; ++x) {
731 const u32 coarse_y = y & ~7;
732 u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel;
733 u32 gl_px_idx = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel;
734
735 u8* pixel = color_buffer + dst_offset;
736 memcpy(&temp_fb_color_buffer[gl_px_idx], pixel, bytes_per_pixel);
737 }
738 }
739
740 state.texture_units[0].enabled_2d = true;
741 state.texture_units[0].texture_2d = fb_color_texture.texture.handle;
742 state.Apply();
743
744 glActiveTexture(GL_TEXTURE0);
745 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_color_texture.width, fb_color_texture.height,
746 fb_color_texture.gl_format, fb_color_texture.gl_type, temp_fb_color_buffer.get());
747}
748
749void RasterizerOpenGL::ReloadDepthBuffer() {
750 // TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil
751 u8* depth_buffer = Memory::GetPhysicalPointer(Pica::registers.framebuffer.GetDepthBufferPhysicalAddress());
752
753 if (depth_buffer == nullptr) {
754 return;
755 }
756
757 u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format);
758
759 // OpenGL needs 4 bpp alignment for D24
760 u32 gl_bpp = bytes_per_pixel == 3 ? 4 : bytes_per_pixel;
761
762 std::unique_ptr<u8[]> temp_fb_depth_buffer(new u8[fb_depth_texture.width * fb_depth_texture.height * gl_bpp]);
763
764 for (int y = 0; y < fb_depth_texture.height; ++y) {
765 for (int x = 0; x < fb_depth_texture.width; ++x) {
766 const u32 coarse_y = y & ~7;
767 u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
768 u32 gl_px_idx = x + y * fb_depth_texture.width;
769
770 switch (fb_depth_texture.format) {
771 case Pica::Regs::DepthFormat::D16:
772 ((u16*)temp_fb_depth_buffer.get())[gl_px_idx] = Color::DecodeD16(depth_buffer + dst_offset);
773 break;
774 case Pica::Regs::DepthFormat::D24:
775 ((u32*)temp_fb_depth_buffer.get())[gl_px_idx] = Color::DecodeD24(depth_buffer + dst_offset);
776 break;
777 case Pica::Regs::DepthFormat::D24S8:
778 {
779 Math::Vec2<u32> depth_stencil = Color::DecodeD24S8(depth_buffer + dst_offset);
780 ((u32*)temp_fb_depth_buffer.get())[gl_px_idx] = (depth_stencil.x << 8) | depth_stencil.y;
781 break;
782 }
783 default:
784 LOG_CRITICAL(Render_OpenGL, "Unknown memory framebuffer depth format %x", fb_depth_texture.format);
785 UNIMPLEMENTED();
786 break;
787 }
788 }
789 }
790
791 state.texture_units[0].enabled_2d = true;
792 state.texture_units[0].texture_2d = fb_depth_texture.texture.handle;
793 state.Apply();
794
795 glActiveTexture(GL_TEXTURE0);
796 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_depth_texture.width, fb_depth_texture.height,
797 fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_fb_depth_buffer.get());
798}
799
800void RasterizerOpenGL::CommitColorBuffer() {
801 if (last_fb_color_addr != 0) {
802 u8* color_buffer = Memory::GetPhysicalPointer(last_fb_color_addr);
803
804 if (color_buffer != nullptr) {
805 u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format);
806
807 std::unique_ptr<u8[]> temp_gl_color_buffer(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]);
808
809 state.texture_units[0].enabled_2d = true;
810 state.texture_units[0].texture_2d = fb_color_texture.texture.handle;
811 state.Apply();
812
813 glActiveTexture(GL_TEXTURE0);
814 glGetTexImage(GL_TEXTURE_2D, 0, fb_color_texture.gl_format, fb_color_texture.gl_type, temp_gl_color_buffer.get());
815
816 // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary.
817 for (int y = 0; y < fb_color_texture.height; ++y) {
818 for (int x = 0; x < fb_color_texture.width; ++x) {
819 const u32 coarse_y = y & ~7;
820 u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel;
821 u32 gl_px_idx = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel;
822
823 u8* pixel = color_buffer + dst_offset;
824 memcpy(pixel, &temp_gl_color_buffer[gl_px_idx], bytes_per_pixel);
825 }
826 }
827 }
828 }
829}
830
831void RasterizerOpenGL::CommitDepthBuffer() {
832 if (last_fb_depth_addr != 0) {
833 // TODO: Output seems correct visually, but doesn't quite match sw renderer output. One of them is wrong.
834 u8* depth_buffer = Memory::GetPhysicalPointer(last_fb_depth_addr);
835
836 if (depth_buffer != nullptr) {
837 u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format);
838
839 // OpenGL needs 4 bpp alignment for D24
840 u32 gl_bpp = bytes_per_pixel == 3 ? 4 : bytes_per_pixel;
841
842 std::unique_ptr<u8[]> temp_gl_depth_buffer(new u8[fb_depth_texture.width * fb_depth_texture.height * gl_bpp]);
843
844 state.texture_units[0].enabled_2d = true;
845 state.texture_units[0].texture_2d = fb_depth_texture.texture.handle;
846 state.Apply();
847
848 glActiveTexture(GL_TEXTURE0);
849 glGetTexImage(GL_TEXTURE_2D, 0, fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_gl_depth_buffer.get());
850
851 for (int y = 0; y < fb_depth_texture.height; ++y) {
852 for (int x = 0; x < fb_depth_texture.width; ++x) {
853 const u32 coarse_y = y & ~7;
854 u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
855 u32 gl_px_idx = x + y * fb_depth_texture.width;
856
857 switch (fb_depth_texture.format) {
858 case Pica::Regs::DepthFormat::D16:
859 Color::EncodeD16(((u16*)temp_gl_depth_buffer.get())[gl_px_idx], depth_buffer + dst_offset);
860 break;
861 case Pica::Regs::DepthFormat::D24:
862 Color::EncodeD24(((u32*)temp_gl_depth_buffer.get())[gl_px_idx], depth_buffer + dst_offset);
863 break;
864 case Pica::Regs::DepthFormat::D24S8:
865 {
866 u32 depth_stencil = ((u32*)temp_gl_depth_buffer.get())[gl_px_idx];
867 Color::EncodeD24S8((depth_stencil >> 8), depth_stencil & 0xFF, depth_buffer + dst_offset);
868 break;
869 }
870 default:
871 LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer depth format %x", fb_depth_texture.format);
872 UNIMPLEMENTED();
873 break;
874 }
875 }
876 }
877 }
878 }
879}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
new file mode 100644
index 000000000..9896f8d04
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -0,0 +1,207 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "video_core/hwrasterizer_base.h"
8
9#include "gl_state.h"
10#include "gl_rasterizer_cache.h"
11
12class RasterizerOpenGL : public HWRasterizer {
13public:
14
15 RasterizerOpenGL();
16 ~RasterizerOpenGL() override;
17
18 /// Initialize API-specific GPU objects
19 void InitObjects() override;
20
21 /// Reset the rasterizer, such as flushing all caches and updating all state
22 void Reset() override;
23
24 /// Queues the primitive formed by the given vertices for rendering
25 void AddTriangle(const Pica::VertexShader::OutputVertex& v0,
26 const Pica::VertexShader::OutputVertex& v1,
27 const Pica::VertexShader::OutputVertex& v2) override;
28
29 /// Draw the current batch of triangles
30 void DrawTriangles() override;
31
32 /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
33 void CommitFramebuffer() override;
34
35 /// Notify rasterizer that the specified PICA register has been changed
36 void NotifyPicaRegisterChanged(u32 id) override;
37
38 /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
39 void NotifyPreRead(PAddr addr, u32 size) override;
40
41 /// Notify rasterizer that a 3DS memory region has been changed
42 void NotifyFlush(PAddr addr, u32 size) override;
43
44private:
45 /// Structure used for managing texture environment states
46 struct TEVConfigUniforms {
47 GLuint enabled;
48 GLuint color_sources;
49 GLuint alpha_sources;
50 GLuint color_modifiers;
51 GLuint alpha_modifiers;
52 GLuint color_alpha_op;
53 GLuint color_alpha_multiplier;
54 GLuint const_color;
55 GLuint updates_combiner_buffer_color_alpha;
56 };
57
58 /// Structure used for storing information about color textures
59 struct TextureInfo {
60 OGLTexture texture;
61 GLsizei width;
62 GLsizei height;
63 Pica::Regs::ColorFormat format;
64 GLenum gl_format;
65 GLenum gl_type;
66 };
67
68 /// Structure used for storing information about depth textures
69 struct DepthTextureInfo {
70 OGLTexture texture;
71 GLsizei width;
72 GLsizei height;
73 Pica::Regs::DepthFormat format;
74 GLenum gl_format;
75 GLenum gl_type;
76 };
77
78 /// Structure that the hardware rendered vertices are composed of
79 struct HardwareVertex {
80 HardwareVertex(const Pica::VertexShader::OutputVertex& v) {
81 position[0] = v.pos.x.ToFloat32();
82 position[1] = v.pos.y.ToFloat32();
83 position[2] = v.pos.z.ToFloat32();
84 position[3] = v.pos.w.ToFloat32();
85 color[0] = v.color.x.ToFloat32();
86 color[1] = v.color.y.ToFloat32();
87 color[2] = v.color.z.ToFloat32();
88 color[3] = v.color.w.ToFloat32();
89 tex_coord0[0] = v.tc0.x.ToFloat32();
90 tex_coord0[1] = v.tc0.y.ToFloat32();
91 tex_coord1[0] = v.tc1.x.ToFloat32();
92 tex_coord1[1] = v.tc1.y.ToFloat32();
93 tex_coord2[0] = v.tc2.x.ToFloat32();
94 tex_coord2[1] = v.tc2.y.ToFloat32();
95 }
96
97 GLfloat position[4];
98 GLfloat color[4];
99 GLfloat tex_coord0[2];
100 GLfloat tex_coord1[2];
101 GLfloat tex_coord2[2];
102 };
103
104 /// Reconfigure the OpenGL color texture to use the given format and dimensions
105 void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height);
106
107 /// Reconfigure the OpenGL depth texture to use the given format and dimensions
108 void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height);
109
110 /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer
111 void SyncFramebuffer();
112
113 /// Syncs the cull mode to match the PICA register
114 void SyncCullMode();
115
116 /// Syncs the blend enabled status to match the PICA register
117 void SyncBlendEnabled();
118
119 /// Syncs the blend functions to match the PICA register
120 void SyncBlendFuncs();
121
122 /// Syncs the blend color to match the PICA register
123 void SyncBlendColor();
124
125 /// Syncs the alpha test states to match the PICA register
126 void SyncAlphaTest();
127
128 /// Syncs the stencil test states to match the PICA register
129 void SyncStencilTest();
130
131 /// Syncs the depth test states to match the PICA register
132 void SyncDepthTest();
133
134 /// Syncs the specified TEV stage's color and alpha sources to match the PICA register
135 void SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
136
137 /// Syncs the specified TEV stage's color and alpha modifiers to match the PICA register
138 void SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
139
140 /// Syncs the specified TEV stage's color and alpha combiner operations to match the PICA register
141 void SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
142
143 /// Syncs the specified TEV stage's constant color to match the PICA register
144 void SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
145
146 /// Syncs the specified TEV stage's color and alpha multipliers to match the PICA register
147 void SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
148
149 /// Syncs the TEV combiner color buffer to match the PICA register
150 void SyncCombinerColor();
151
152 /// Syncs the TEV combiner write flags to match the PICA register
153 void SyncCombinerWriteFlags();
154
155 /// Syncs the remaining OpenGL drawing state to match the current PICA state
156 void SyncDrawState();
157
158 /// Copies the 3DS color framebuffer into the OpenGL color framebuffer texture
159 void ReloadColorBuffer();
160
161 /// Copies the 3DS depth framebuffer into the OpenGL depth framebuffer texture
162 void ReloadDepthBuffer();
163
164 /**
165 * Save the current OpenGL color framebuffer to the current PICA framebuffer in 3DS memory
166 * Loads the OpenGL framebuffer textures into temporary buffers
167 * Then copies into the 3DS framebuffer using proper Morton order
168 */
169 void CommitColorBuffer();
170
171 /**
172 * Save the current OpenGL depth framebuffer to the current PICA framebuffer in 3DS memory
173 * Loads the OpenGL framebuffer textures into temporary buffers
174 * Then copies into the 3DS framebuffer using proper Morton order
175 */
176 void CommitDepthBuffer();
177
178 RasterizerCacheOpenGL res_cache;
179
180 std::vector<HardwareVertex> vertex_batch;
181
182 OpenGLState state;
183
184 PAddr last_fb_color_addr;
185 PAddr last_fb_depth_addr;
186
187 // Hardware rasterizer
188 TextureInfo fb_color_texture;
189 DepthTextureInfo fb_depth_texture;
190 OGLShader shader;
191 OGLVertexArray vertex_array;
192 OGLBuffer vertex_buffer;
193 OGLFramebuffer framebuffer;
194
195 // Hardware vertex shader
196 GLuint attrib_position;
197 GLuint attrib_color;
198 GLuint attrib_texcoords;
199
200 // Hardware fragment shader
201 GLuint uniform_alphatest_enabled;
202 GLuint uniform_alphatest_func;
203 GLuint uniform_alphatest_ref;
204 GLuint uniform_tex;
205 GLuint uniform_tev_combiner_buffer_color;
206 TEVConfigUniforms uniform_tev_cfgs[6];
207};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
new file mode 100644
index 000000000..6f88a8b21
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -0,0 +1,77 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/make_unique.h"
6#include "common/math_util.h"
7
8#include "core/memory.h"
9
10#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
11#include "video_core/renderer_opengl/pica_to_gl.h"
12#include "video_core/debug_utils/debug_utils.h"
13#include "video_core/math.h"
14
15RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
16 FullFlush();
17}
18
19void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) {
20 PAddr texture_addr = config.config.GetPhysicalAddress();
21
22 const auto cached_texture = texture_cache.find(texture_addr);
23
24 if (cached_texture != texture_cache.end()) {
25 state.texture_units[texture_unit].texture_2d = cached_texture->second->texture.handle;
26 state.Apply();
27 } else {
28 std::unique_ptr<CachedTexture> new_texture = Common::make_unique<CachedTexture>();
29
30 new_texture->texture.Create();
31 state.texture_units[texture_unit].texture_2d = new_texture->texture.handle;
32 state.Apply();
33
34 // TODO: Need to choose filters that correspond to PICA once register is declared
35 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
36 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
37
38 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(config.config.wrap_s));
39 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(config.config.wrap_t));
40
41 const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format);
42
43 new_texture->width = info.width;
44 new_texture->height = info.height;
45 new_texture->size = info.width * info.height * Pica::Regs::NibblesPerPixel(info.format);
46
47 u8* texture_src_data = Memory::GetPhysicalPointer(texture_addr);
48 std::unique_ptr<Math::Vec4<u8>[]> temp_texture_buffer_rgba(new Math::Vec4<u8>[info.width * info.height]);
49
50 for (int y = 0; y < info.height; ++y) {
51 for (int x = 0; x < info.width; ++x) {
52 temp_texture_buffer_rgba[x + info.width * y] = Pica::DebugUtils::LookupTexture(texture_src_data, x, info.height - 1 - y, info);
53 }
54 }
55
56 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_texture_buffer_rgba.get());
57
58 texture_cache.emplace(texture_addr, std::move(new_texture));
59 }
60}
61
62void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size) {
63 // Flush any texture that falls in the flushed region
64 // TODO: Optimize by also inserting upper bound (addr + size) of each texture into the same map and also narrow using lower_bound
65 auto cache_upper_bound = texture_cache.upper_bound(addr + size);
66 for (auto it = texture_cache.begin(); it != cache_upper_bound;) {
67 if (MathUtil::IntervalsIntersect(addr, size, it->first, it->second->size)) {
68 it = texture_cache.erase(it);
69 } else {
70 ++it;
71 }
72 }
73}
74
75void RasterizerCacheOpenGL::FullFlush() {
76 texture_cache.clear();
77}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
new file mode 100644
index 000000000..96f3a925c
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -0,0 +1,36 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "gl_state.h"
8#include "gl_resource_manager.h"
9#include "video_core/pica.h"
10
11#include <memory>
12#include <map>
13
14class RasterizerCacheOpenGL : NonCopyable {
15public:
16 ~RasterizerCacheOpenGL();
17
18 /// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached)
19 void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config);
20
21 /// Flush any cached resource that touches the flushed region
22 void NotifyFlush(PAddr addr, u32 size);
23
24 /// Flush all cached OpenGL resources tracked by this cache manager
25 void FullFlush();
26
27private:
28 struct CachedTexture {
29 OGLTexture texture;
30 GLuint width;
31 GLuint height;
32 u32 size;
33 };
34
35 std::map<PAddr, std::unique_ptr<CachedTexture>> texture_cache;
36};
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
new file mode 100644
index 000000000..8f4ae28a4
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -0,0 +1,111 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/renderer_opengl/gl_resource_manager.h"
6#include "video_core/renderer_opengl/gl_shader_util.h"
7
8// Textures
9OGLTexture::OGLTexture() : handle(0) {
10}
11
12OGLTexture::~OGLTexture() {
13 Release();
14}
15
16void OGLTexture::Create() {
17 if (handle != 0) {
18 return;
19 }
20
21 glGenTextures(1, &handle);
22}
23
24void OGLTexture::Release() {
25 glDeleteTextures(1, &handle);
26 handle = 0;
27}
28
29// Shaders
30OGLShader::OGLShader() : handle(0) {
31}
32
33OGLShader::~OGLShader() {
34 Release();
35}
36
37void OGLShader::Create(const char* vert_shader, const char* frag_shader) {
38 if (handle != 0) {
39 return;
40 }
41
42 handle = ShaderUtil::LoadShaders(vert_shader, frag_shader);
43}
44
45void OGLShader::Release() {
46 glDeleteProgram(handle);
47 handle = 0;
48}
49
50// Buffer objects
51OGLBuffer::OGLBuffer() : handle(0) {
52}
53
54OGLBuffer::~OGLBuffer() {
55 Release();
56}
57
58void OGLBuffer::Create() {
59 if (handle != 0) {
60 return;
61 }
62
63 glGenBuffers(1, &handle);
64}
65
66void OGLBuffer::Release() {
67 glDeleteBuffers(1, &handle);
68 handle = 0;
69}
70
71// Vertex array objects
72OGLVertexArray::OGLVertexArray() : handle(0) {
73}
74
75OGLVertexArray::~OGLVertexArray() {
76 Release();
77}
78
79void OGLVertexArray::Create() {
80 if (handle != 0) {
81 return;
82 }
83
84 glGenVertexArrays(1, &handle);
85}
86
87void OGLVertexArray::Release() {
88 glDeleteVertexArrays(1, &handle);
89 handle = 0;
90}
91
92// Framebuffers
93OGLFramebuffer::OGLFramebuffer() : handle(0) {
94}
95
96OGLFramebuffer::~OGLFramebuffer() {
97 Release();
98}
99
100void OGLFramebuffer::Create() {
101 if (handle != 0) {
102 return;
103 }
104
105 glGenFramebuffers(1, &handle);
106}
107
108void OGLFramebuffer::Release() {
109 glDeleteFramebuffers(1, &handle);
110 handle = 0;
111}
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
new file mode 100644
index 000000000..975720d0a
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -0,0 +1,79 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "generated/gl_3_2_core.h"
10
11class OGLTexture : public NonCopyable {
12public:
13 OGLTexture();
14 ~OGLTexture();
15
16 /// Creates a new internal OpenGL resource and stores the handle
17 void Create();
18
19 /// Deletes the internal OpenGL resource
20 void Release();
21
22 GLuint handle;
23};
24
25class OGLShader : public NonCopyable {
26public:
27 OGLShader();
28 ~OGLShader();
29
30 /// Creates a new internal OpenGL resource and stores the handle
31 void Create(const char* vert_shader, const char* frag_shader);
32
33 /// Deletes the internal OpenGL resource
34 void Release();
35
36 GLuint handle;
37};
38
39class OGLBuffer : public NonCopyable {
40public:
41 OGLBuffer();
42 ~OGLBuffer();
43
44 /// Creates a new internal OpenGL resource and stores the handle
45 void Create();
46
47 /// Deletes the internal OpenGL resource
48 void Release();
49
50 GLuint handle;
51};
52
53class OGLVertexArray : public NonCopyable {
54public:
55 OGLVertexArray();
56 ~OGLVertexArray();
57
58 /// Creates a new internal OpenGL resource and stores the handle
59 void Create();
60
61 /// Deletes the internal OpenGL resource
62 void Release();
63
64 GLuint handle;
65};
66
67class OGLFramebuffer : public NonCopyable {
68public:
69 OGLFramebuffer();
70 ~OGLFramebuffer();
71
72 /// Creates a new internal OpenGL resource and stores the handle
73 void Create();
74
75 /// Deletes the internal OpenGL resource
76 void Release();
77
78 GLuint handle;
79};
diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h
index 746a37afe..8f0941230 100644
--- a/src/video_core/renderer_opengl/gl_shaders.h
+++ b/src/video_core/renderer_opengl/gl_shaders.h
@@ -42,4 +42,292 @@ void main() {
42} 42}
43)"; 43)";
44 44
45const char g_vertex_shader_hw[] = R"(
46#version 150 core
47
48#define NUM_VTX_ATTR 7
49
50in vec4 vert_position;
51in vec4 vert_color;
52in vec2 vert_texcoords[3];
53
54out vec4 o[NUM_VTX_ATTR];
55
56void main() {
57 o[2] = vert_color;
58 o[3] = vec4(vert_texcoords[0].xy, vert_texcoords[1].xy);
59 o[5] = vec4(0.0, 0.0, vert_texcoords[2].xy);
60
61 gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
62}
63)";
64
65// TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms
66const char g_fragment_shader_hw[] = R"(
67#version 150 core
68
69#define NUM_VTX_ATTR 7
70#define NUM_TEV_STAGES 6
71
72#define SOURCE_PRIMARYCOLOR 0x0
73#define SOURCE_PRIMARYFRAGMENTCOLOR 0x1
74#define SOURCE_TEXTURE0 0x3
75#define SOURCE_TEXTURE1 0x4
76#define SOURCE_TEXTURE2 0x5
77#define SOURCE_TEXTURE3 0x6
78#define SOURCE_PREVIOUSBUFFER 0xd
79#define SOURCE_CONSTANT 0xe
80#define SOURCE_PREVIOUS 0xf
81
82#define COLORMODIFIER_SOURCECOLOR 0x0
83#define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1
84#define COLORMODIFIER_SOURCEALPHA 0x2
85#define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3
86#define COLORMODIFIER_SOURCERED 0x4
87#define COLORMODIFIER_ONEMINUSSOURCERED 0x5
88#define COLORMODIFIER_SOURCEGREEN 0x8
89#define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9
90#define COLORMODIFIER_SOURCEBLUE 0xc
91#define COLORMODIFIER_ONEMINUSSOURCEBLUE 0xd
92
93#define ALPHAMODIFIER_SOURCEALPHA 0x0
94#define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1
95#define ALPHAMODIFIER_SOURCERED 0x2
96#define ALPHAMODIFIER_ONEMINUSSOURCERED 0x3
97#define ALPHAMODIFIER_SOURCEGREEN 0x4
98#define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5
99#define ALPHAMODIFIER_SOURCEBLUE 0x6
100#define ALPHAMODIFIER_ONEMINUSSOURCEBLUE 0x7
101
102#define OPERATION_REPLACE 0
103#define OPERATION_MODULATE 1
104#define OPERATION_ADD 2
105#define OPERATION_ADDSIGNED 3
106#define OPERATION_LERP 4
107#define OPERATION_SUBTRACT 5
108#define OPERATION_MULTIPLYTHENADD 8
109#define OPERATION_ADDTHENMULTIPLY 9
110
111#define COMPAREFUNC_NEVER 0
112#define COMPAREFUNC_ALWAYS 1
113#define COMPAREFUNC_EQUAL 2
114#define COMPAREFUNC_NOTEQUAL 3
115#define COMPAREFUNC_LESSTHAN 4
116#define COMPAREFUNC_LESSTHANOREQUAL 5
117#define COMPAREFUNC_GREATERTHAN 6
118#define COMPAREFUNC_GREATERTHANOREQUAL 7
119
120in vec4 o[NUM_VTX_ATTR];
121out vec4 color;
122
123uniform bool alphatest_enabled;
124uniform int alphatest_func;
125uniform float alphatest_ref;
126
127uniform sampler2D tex[3];
128
129uniform vec4 tev_combiner_buffer_color;
130
131struct TEVConfig
132{
133 bool enabled;
134 ivec3 color_sources;
135 ivec3 alpha_sources;
136 ivec3 color_modifiers;
137 ivec3 alpha_modifiers;
138 ivec2 color_alpha_op;
139 ivec2 color_alpha_multiplier;
140 vec4 const_color;
141 bvec2 updates_combiner_buffer_color_alpha;
142};
143
144uniform TEVConfig tev_cfgs[NUM_TEV_STAGES];
145
146vec4 g_combiner_buffer;
147vec4 g_last_tex_env_out;
148vec4 g_const_color;
149
150vec4 GetSource(int source) {
151 if (source == SOURCE_PRIMARYCOLOR) {
152 return o[2];
153 } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) {
154 // HACK: Uses color value, but should really use fragment lighting output
155 return o[2];
156 } else if (source == SOURCE_TEXTURE0) {
157 return texture(tex[0], o[3].xy);
158 } else if (source == SOURCE_TEXTURE1) {
159 return texture(tex[1], o[3].zw);
160 } else if (source == SOURCE_TEXTURE2) {
161 // TODO: Unverified
162 return texture(tex[2], o[5].zw);
163 } else if (source == SOURCE_TEXTURE3) {
164 // TODO: no 4th texture?
165 } else if (source == SOURCE_PREVIOUSBUFFER) {
166 return g_combiner_buffer;
167 } else if (source == SOURCE_CONSTANT) {
168 return g_const_color;
169 } else if (source == SOURCE_PREVIOUS) {
170 return g_last_tex_env_out;
171 }
172
173 return vec4(0.0);
174}
175
176vec3 GetColorModifier(int factor, vec4 color) {
177 if (factor == COLORMODIFIER_SOURCECOLOR) {
178 return color.rgb;
179 } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) {
180 return vec3(1.0) - color.rgb;
181 } else if (factor == COLORMODIFIER_SOURCEALPHA) {
182 return color.aaa;
183 } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) {
184 return vec3(1.0) - color.aaa;
185 } else if (factor == COLORMODIFIER_SOURCERED) {
186 return color.rrr;
187 } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) {
188 return vec3(1.0) - color.rrr;
189 } else if (factor == COLORMODIFIER_SOURCEGREEN) {
190 return color.ggg;
191 } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) {
192 return vec3(1.0) - color.ggg;
193 } else if (factor == COLORMODIFIER_SOURCEBLUE) {
194 return color.bbb;
195 } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) {
196 return vec3(1.0) - color.bbb;
197 }
198
199 return vec3(0.0);
200}
201
202float GetAlphaModifier(int factor, vec4 color) {
203 if (factor == ALPHAMODIFIER_SOURCEALPHA) {
204 return color.a;
205 } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) {
206 return 1.0 - color.a;
207 } else if (factor == ALPHAMODIFIER_SOURCERED) {
208 return color.r;
209 } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) {
210 return 1.0 - color.r;
211 } else if (factor == ALPHAMODIFIER_SOURCEGREEN) {
212 return color.g;
213 } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) {
214 return 1.0 - color.g;
215 } else if (factor == ALPHAMODIFIER_SOURCEBLUE) {
216 return color.b;
217 } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) {
218 return 1.0 - color.b;
219 }
220
221 return 0.0;
222}
223
224vec3 ColorCombine(int op, vec3 color[3]) {
225 if (op == OPERATION_REPLACE) {
226 return color[0];
227 } else if (op == OPERATION_MODULATE) {
228 return color[0] * color[1];
229 } else if (op == OPERATION_ADD) {
230 return min(color[0] + color[1], 1.0);
231 } else if (op == OPERATION_ADDSIGNED) {
232 return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0);
233 } else if (op == OPERATION_LERP) {
234 return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]);
235 } else if (op == OPERATION_SUBTRACT) {
236 return max(color[0] - color[1], 0.0);
237 } else if (op == OPERATION_MULTIPLYTHENADD) {
238 return min(color[0] * color[1] + color[2], 1.0);
239 } else if (op == OPERATION_ADDTHENMULTIPLY) {
240 return min(color[0] + color[1], 1.0) * color[2];
241 }
242
243 return vec3(0.0);
244}
245
246float AlphaCombine(int op, float alpha[3]) {
247 if (op == OPERATION_REPLACE) {
248 return alpha[0];
249 } else if (op == OPERATION_MODULATE) {
250 return alpha[0] * alpha[1];
251 } else if (op == OPERATION_ADD) {
252 return min(alpha[0] + alpha[1], 1.0);
253 } else if (op == OPERATION_ADDSIGNED) {
254 return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0);
255 } else if (op == OPERATION_LERP) {
256 return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]);
257 } else if (op == OPERATION_SUBTRACT) {
258 return max(alpha[0] - alpha[1], 0.0);
259 } else if (op == OPERATION_MULTIPLYTHENADD) {
260 return min(alpha[0] * alpha[1] + alpha[2], 1.0);
261 } else if (op == OPERATION_ADDTHENMULTIPLY) {
262 return min(alpha[0] + alpha[1], 1.0) * alpha[2];
263 }
264
265 return 0.0;
266}
267
268void main(void) {
269 g_combiner_buffer = tev_combiner_buffer_color;
270
271 for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) {
272 if (tev_cfgs[tex_env_idx].enabled) {
273 g_const_color = tev_cfgs[tex_env_idx].const_color;
274
275 vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)),
276 GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)),
277 GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z)));
278 vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results);
279
280 float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)),
281 GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)),
282 GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z)));
283 float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results);
284
285 g_last_tex_env_out = vec4(min(color_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.x, 1.0), min(alpha_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.y, 1.0));
286 }
287
288 if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) {
289 g_combiner_buffer.rgb = g_last_tex_env_out.rgb;
290 }
291
292 if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) {
293 g_combiner_buffer.a = g_last_tex_env_out.a;
294 }
295 }
296
297 if (alphatest_enabled) {
298 if (alphatest_func == COMPAREFUNC_NEVER) {
299 discard;
300 } else if (alphatest_func == COMPAREFUNC_ALWAYS) {
301
302 } else if (alphatest_func == COMPAREFUNC_EQUAL) {
303 if (g_last_tex_env_out.a != alphatest_ref) {
304 discard;
305 }
306 } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) {
307 if (g_last_tex_env_out.a == alphatest_ref) {
308 discard;
309 }
310 } else if (alphatest_func == COMPAREFUNC_LESSTHAN) {
311 if (g_last_tex_env_out.a >= alphatest_ref) {
312 discard;
313 }
314 } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) {
315 if (g_last_tex_env_out.a > alphatest_ref) {
316 discard;
317 }
318 } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) {
319 if (g_last_tex_env_out.a <= alphatest_ref) {
320 discard;
321 }
322 } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) {
323 if (g_last_tex_env_out.a < alphatest_ref) {
324 discard;
325 }
326 }
327 }
328
329 color = g_last_tex_env_out;
330}
331)";
332
45} 333}
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
new file mode 100644
index 000000000..ca8a371e7
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -0,0 +1,160 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/renderer_opengl/gl_state.h"
6#include "video_core/pica.h"
7
8OpenGLState OpenGLState::cur_state;
9
10OpenGLState::OpenGLState() {
11 // These all match default OpenGL values
12 cull.enabled = false;
13 cull.mode = GL_BACK;
14
15 depth.test_enabled = false;
16 depth.test_func = GL_LESS;
17 depth.write_mask = GL_TRUE;
18
19 stencil.test_enabled = false;
20 stencil.test_func = GL_ALWAYS;
21 stencil.test_ref = 0;
22 stencil.test_mask = -1;
23 stencil.write_mask = -1;
24
25 blend.enabled = false;
26 blend.src_rgb_func = GL_ONE;
27 blend.dst_rgb_func = GL_ZERO;
28 blend.src_a_func = GL_ONE;
29 blend.dst_a_func = GL_ZERO;
30 blend.color.red = 0.0f;
31 blend.color.green = 0.0f;
32 blend.color.blue = 0.0f;
33 blend.color.alpha = 0.0f;
34
35 for (auto& texture_unit : texture_units) {
36 texture_unit.enabled_2d = false;
37 texture_unit.texture_2d = 0;
38 }
39
40 draw.framebuffer = 0;
41 draw.vertex_array = 0;
42 draw.vertex_buffer = 0;
43 draw.shader_program = 0;
44}
45
46const void OpenGLState::Apply() {
47 // Culling
48 if (cull.enabled != cur_state.cull.enabled) {
49 if (cull.enabled) {
50 glEnable(GL_CULL_FACE);
51 } else {
52 glDisable(GL_CULL_FACE);
53 }
54 }
55
56 if (cull.mode != cur_state.cull.mode) {
57 glCullFace(cull.mode);
58 }
59
60 // Depth test
61 if (depth.test_enabled != cur_state.depth.test_enabled) {
62 if (depth.test_enabled) {
63 glEnable(GL_DEPTH_TEST);
64 } else {
65 glDisable(GL_DEPTH_TEST);
66 }
67 }
68
69 if (depth.test_func != cur_state.depth.test_func) {
70 glDepthFunc(depth.test_func);
71 }
72
73 // Depth mask
74 if (depth.write_mask != cur_state.depth.write_mask) {
75 glDepthMask(depth.write_mask);
76 }
77
78 // Stencil test
79 if (stencil.test_enabled != cur_state.stencil.test_enabled) {
80 if (stencil.test_enabled) {
81 glEnable(GL_STENCIL_TEST);
82 } else {
83 glDisable(GL_STENCIL_TEST);
84 }
85 }
86
87 if (stencil.test_func != cur_state.stencil.test_func ||
88 stencil.test_ref != cur_state.stencil.test_ref ||
89 stencil.test_mask != cur_state.stencil.test_mask) {
90 glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask);
91 }
92
93 // Stencil mask
94 if (stencil.write_mask != cur_state.stencil.write_mask) {
95 glStencilMask(stencil.write_mask);
96 }
97
98 // Blending
99 if (blend.enabled != cur_state.blend.enabled) {
100 if (blend.enabled) {
101 glEnable(GL_BLEND);
102 } else {
103 glDisable(GL_BLEND);
104 }
105 }
106
107 if (blend.color.red != cur_state.blend.color.red ||
108 blend.color.green != cur_state.blend.color.green ||
109 blend.color.blue != cur_state.blend.color.blue ||
110 blend.color.alpha != cur_state.blend.color.alpha) {
111 glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha);
112 }
113
114 if (blend.src_rgb_func != cur_state.blend.src_rgb_func ||
115 blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
116 blend.src_a_func != cur_state.blend.src_a_func ||
117 blend.dst_a_func != cur_state.blend.dst_a_func) {
118 glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, blend.dst_a_func);
119 }
120
121 // Textures
122 for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) {
123 if (texture_units[texture_index].enabled_2d != texture_units[texture_index].enabled_2d) {
124 glActiveTexture(GL_TEXTURE0 + texture_index);
125
126 if (texture_units[texture_index].enabled_2d) {
127 glEnable(GL_TEXTURE_2D);
128 } else {
129 glDisable(GL_TEXTURE_2D);
130 }
131 }
132
133 if (texture_units[texture_index].texture_2d != cur_state.texture_units[texture_index].texture_2d) {
134 glActiveTexture(GL_TEXTURE0 + texture_index);
135 glBindTexture(GL_TEXTURE_2D, texture_units[texture_index].texture_2d);
136 }
137 }
138
139 // Framebuffer
140 if (draw.framebuffer != cur_state.draw.framebuffer) {
141 glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer);
142 }
143
144 // Vertex array
145 if (draw.vertex_array != cur_state.draw.vertex_array) {
146 glBindVertexArray(draw.vertex_array);
147 }
148
149 // Vertex buffer
150 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
151 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
152 }
153
154 // Shader program
155 if (draw.shader_program != cur_state.draw.shader_program) {
156 glUseProgram(draw.shader_program);
157 }
158
159 cur_state = *this;
160}
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
new file mode 100644
index 000000000..a56d31371
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -0,0 +1,70 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "generated/gl_3_2_core.h"
8
9class OpenGLState {
10public:
11 struct {
12 bool enabled; // GL_CULL_FACE
13 GLenum mode; // GL_CULL_FACE_MODE
14 } cull;
15
16 struct {
17 bool test_enabled; // GL_DEPTH_TEST
18 GLenum test_func; // GL_DEPTH_FUNC
19 GLboolean write_mask; // GL_DEPTH_WRITEMASK
20 } depth;
21
22 struct {
23 bool test_enabled; // GL_STENCIL_TEST
24 GLenum test_func; // GL_STENCIL_FUNC
25 GLint test_ref; // GL_STENCIL_REF
26 GLuint test_mask; // GL_STENCIL_VALUE_MASK
27 GLuint write_mask; // GL_STENCIL_WRITEMASK
28 } stencil;
29
30 struct {
31 bool enabled; // GL_BLEND
32 GLenum src_rgb_func; // GL_BLEND_SRC_RGB
33 GLenum dst_rgb_func; // GL_BLEND_DST_RGB
34 GLenum src_a_func; // GL_BLEND_SRC_ALPHA
35 GLenum dst_a_func; // GL_BLEND_DST_ALPHA
36
37 struct {
38 GLclampf red;
39 GLclampf green;
40 GLclampf blue;
41 GLclampf alpha;
42 } color; // GL_BLEND_COLOR
43 } blend;
44
45 // 3 texture units - one for each that is used in PICA fragment shader emulation
46 struct {
47 bool enabled_2d; // GL_TEXTURE_2D
48 GLuint texture_2d; // GL_TEXTURE_BINDING_2D
49 } texture_units[3];
50
51 struct {
52 GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
53 GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
54 GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
55 GLuint shader_program; // GL_CURRENT_PROGRAM
56 } draw;
57
58 OpenGLState();
59
60 /// Get the currently active OpenGL state
61 static const OpenGLState& GetCurState() {
62 return cur_state;
63 }
64
65 /// Apply this state as the current OpenGL state
66 const void Apply();
67
68private:
69 static OpenGLState cur_state;
70};
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
new file mode 100644
index 000000000..8369c649e
--- /dev/null
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -0,0 +1,105 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "video_core/pica.h"
10
11#include "generated/gl_3_2_core.h"
12
13namespace PicaToGL {
14
15inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) {
16 static const GLenum wrap_mode_table[] = {
17 GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge
18 0, // Unknown
19 GL_REPEAT, // WrapMode::Repeat
20 GL_MIRRORED_REPEAT // WrapMode::MirroredRepeat
21 };
22
23 // Range check table for input
24 if (mode >= ARRAY_SIZE(wrap_mode_table)) {
25 LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode);
26 UNREACHABLE();
27
28 return GL_CLAMP_TO_EDGE;
29 }
30
31 GLenum gl_mode = wrap_mode_table[mode];
32
33 // Check for dummy values indicating an unknown mode
34 if (gl_mode == 0) {
35 LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode);
36 UNIMPLEMENTED();
37
38 return GL_CLAMP_TO_EDGE;
39 }
40
41 return gl_mode;
42}
43
44inline GLenum BlendFunc(u32 factor) {
45 static const GLenum blend_func_table[] = {
46 GL_ZERO, // BlendFactor::Zero
47 GL_ONE, // BlendFactor::One
48 GL_SRC_COLOR, // BlendFactor::SourceColor
49 GL_ONE_MINUS_SRC_COLOR, // BlendFactor::OneMinusSourceColor
50 GL_DST_COLOR, // BlendFactor::DestColor
51 GL_ONE_MINUS_DST_COLOR, // BlendFactor::OneMinusDestColor
52 GL_SRC_ALPHA, // BlendFactor::SourceAlpha
53 GL_ONE_MINUS_SRC_ALPHA, // BlendFactor::OneMinusSourceAlpha
54 GL_DST_ALPHA, // BlendFactor::DestAlpha
55 GL_ONE_MINUS_DST_ALPHA, // BlendFactor::OneMinusDestAlpha
56 GL_CONSTANT_COLOR, // BlendFactor::ConstantColor
57 GL_ONE_MINUS_CONSTANT_COLOR, // BlendFactor::OneMinusConstantColor
58 GL_CONSTANT_ALPHA, // BlendFactor::ConstantAlpha
59 GL_ONE_MINUS_CONSTANT_ALPHA, // BlendFactor::OneMinusConstantAlpha
60 GL_SRC_ALPHA_SATURATE, // BlendFactor::SourceAlphaSaturate
61 };
62
63 // Range check table for input
64 if (factor >= ARRAY_SIZE(blend_func_table)) {
65 LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor);
66 UNREACHABLE();
67
68 return GL_ONE;
69 }
70
71 return blend_func_table[factor];
72}
73
74inline GLenum CompareFunc(u32 func) {
75 static const GLenum compare_func_table[] = {
76 GL_NEVER, // CompareFunc::Never
77 GL_ALWAYS, // CompareFunc::Always
78 GL_EQUAL, // CompareFunc::Equal
79 GL_NOTEQUAL, // CompareFunc::NotEqual
80 GL_LESS, // CompareFunc::LessThan
81 GL_LEQUAL, // CompareFunc::LessThanOrEqual
82 GL_GREATER, // CompareFunc::GreaterThan
83 GL_GEQUAL, // CompareFunc::GreaterThanOrEqual
84 };
85
86 // Range check table for input
87 if (func >= ARRAY_SIZE(compare_func_table)) {
88 LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func);
89 UNREACHABLE();
90
91 return GL_ALWAYS;
92 }
93
94 return compare_func_table[func];
95}
96
97inline std::array<GLfloat, 4> ColorRGBA8(const u8* bytes) {
98 return { { bytes[0] / 255.0f,
99 bytes[1] / 255.0f,
100 bytes[2] / 255.0f,
101 bytes[3] / 255.0f
102 } };
103}
104
105} // namespace
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 71ceb021b..16cf92e20 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -53,6 +53,7 @@ static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const
53 53
54/// RendererOpenGL constructor 54/// RendererOpenGL constructor
55RendererOpenGL::RendererOpenGL() { 55RendererOpenGL::RendererOpenGL() {
56 hw_rasterizer.reset(new RasterizerOpenGL());
56 resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth); 57 resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
57 resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight; 58 resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
58} 59}
@@ -63,7 +64,9 @@ RendererOpenGL::~RendererOpenGL() {
63 64
64/// Swap buffers (render frame) 65/// Swap buffers (render frame)
65void RendererOpenGL::SwapBuffers() { 66void RendererOpenGL::SwapBuffers() {
66 render_window->MakeCurrent(); 67 // Maintain the rasterizer's state as a priority
68 OpenGLState prev_state = OpenGLState::GetCurState();
69 state.Apply();
67 70
68 for(int i : {0, 1}) { 71 for(int i : {0, 1}) {
69 const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; 72 const auto& framebuffer = GPU::g_regs.framebuffer_config[i];
@@ -110,7 +113,19 @@ void RendererOpenGL::SwapBuffers() {
110 render_window->PollEvents(); 113 render_window->PollEvents();
111 render_window->SwapBuffers(); 114 render_window->SwapBuffers();
112 115
116 prev_state.Apply();
117
113 profiler.BeginFrame(); 118 profiler.BeginFrame();
119
120 bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled;
121 if (Settings::values.use_hw_renderer != hw_renderer_enabled) {
122 // TODO: Save new setting value to config file for next startup
123 Settings::values.use_hw_renderer = hw_renderer_enabled;
124
125 if (Settings::values.use_hw_renderer) {
126 hw_rasterizer->Reset();
127 }
128 }
114} 129}
115 130
116/** 131/**
@@ -139,7 +154,11 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
139 // only allows rows to have a memory alignement of 4. 154 // only allows rows to have a memory alignement of 4.
140 ASSERT(pixel_stride % 4 == 0); 155 ASSERT(pixel_stride % 4 == 0);
141 156
142 glBindTexture(GL_TEXTURE_2D, texture.handle); 157 state.texture_units[0].enabled_2d = true;
158 state.texture_units[0].texture_2d = texture.handle;
159 state.Apply();
160
161 glActiveTexture(GL_TEXTURE0);
143 glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); 162 glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride);
144 163
145 // Update existing texture 164 // Update existing texture
@@ -151,7 +170,6 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
151 texture.gl_format, texture.gl_type, framebuffer_data); 170 texture.gl_format, texture.gl_type, framebuffer_data);
152 171
153 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 172 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
154 glBindTexture(GL_TEXTURE_2D, 0);
155} 173}
156 174
157/** 175/**
@@ -161,13 +179,15 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
161 */ 179 */
162void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, 180void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
163 const TextureInfo& texture) { 181 const TextureInfo& texture) {
164 glBindTexture(GL_TEXTURE_2D, texture.handle); 182 state.texture_units[0].enabled_2d = true;
183 state.texture_units[0].texture_2d = texture.handle;
184 state.Apply();
165 185
186 glActiveTexture(GL_TEXTURE0);
166 u8 framebuffer_data[3] = { color_r, color_g, color_b }; 187 u8 framebuffer_data[3] = { color_r, color_g, color_b };
167 188
168 // Update existing texture 189 // Update existing texture
169 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data); 190 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data);
170 glBindTexture(GL_TEXTURE_2D, 0);
171} 191}
172 192
173/** 193/**
@@ -175,7 +195,6 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
175 */ 195 */
176void RendererOpenGL::InitOpenGLObjects() { 196void RendererOpenGL::InitOpenGLObjects() {
177 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); 197 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f);
178 glDisable(GL_DEPTH_TEST);
179 198
180 // Link shaders and get variable locations 199 // Link shaders and get variable locations
181 program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader); 200 program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader);
@@ -189,10 +208,12 @@ void RendererOpenGL::InitOpenGLObjects() {
189 208
190 // Generate VAO 209 // Generate VAO
191 glGenVertexArrays(1, &vertex_array_handle); 210 glGenVertexArrays(1, &vertex_array_handle);
192 glBindVertexArray(vertex_array_handle); 211
212 state.draw.vertex_array = vertex_array_handle;
213 state.draw.vertex_buffer = vertex_buffer_handle;
214 state.Apply();
193 215
194 // Attach vertex data to VAO 216 // Attach vertex data to VAO
195 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_handle);
196 glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); 217 glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
197 glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, position)); 218 glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, position));
198 glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, tex_coord)); 219 glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, tex_coord));
@@ -206,14 +227,19 @@ void RendererOpenGL::InitOpenGLObjects() {
206 // Allocation of storage is deferred until the first frame, when we 227 // Allocation of storage is deferred until the first frame, when we
207 // know the framebuffer size. 228 // know the framebuffer size.
208 229
209 glBindTexture(GL_TEXTURE_2D, texture.handle); 230 state.texture_units[0].enabled_2d = true;
231 state.texture_units[0].texture_2d = texture.handle;
232 state.Apply();
233
234 glActiveTexture(GL_TEXTURE0);
210 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); 235 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
211 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 236 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
212 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
213 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 238 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
214 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 239 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
215 } 240 }
216 glBindTexture(GL_TEXTURE_2D, 0); 241
242 hw_rasterizer->InitObjects();
217} 243}
218 244
219void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, 245void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@@ -264,7 +290,11 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
264 UNIMPLEMENTED(); 290 UNIMPLEMENTED();
265 } 291 }
266 292
267 glBindTexture(GL_TEXTURE_2D, texture.handle); 293 state.texture_units[0].enabled_2d = true;
294 state.texture_units[0].texture_2d = texture.handle;
295 state.Apply();
296
297 glActiveTexture(GL_TEXTURE0);
268 glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, 298 glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
269 texture.gl_format, texture.gl_type, nullptr); 299 texture.gl_format, texture.gl_type, nullptr);
270} 300}
@@ -280,8 +310,10 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x
280 ScreenRectVertex(x+w, y+h, 0.f, 1.f), 310 ScreenRectVertex(x+w, y+h, 0.f, 1.f),
281 }; 311 };
282 312
283 glBindTexture(GL_TEXTURE_2D, texture.handle); 313 state.texture_units[0].enabled_2d = true;
284 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_handle); 314 state.texture_units[0].texture_2d = texture.handle;
315 state.Apply();
316
285 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); 317 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
286 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 318 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
287} 319}
@@ -295,7 +327,8 @@ void RendererOpenGL::DrawScreens() {
295 glViewport(0, 0, layout.width, layout.height); 327 glViewport(0, 0, layout.width, layout.height);
296 glClear(GL_COLOR_BUFFER_BIT); 328 glClear(GL_COLOR_BUFFER_BIT);
297 329
298 glUseProgram(program_id); 330 state.draw.shader_program = program_id;
331 state.Apply();
299 332
300 // Set projection matrix 333 // Set projection matrix
301 std::array<GLfloat, 3 * 2> ortho_matrix = MakeOrthographicMatrix((float)layout.width, 334 std::array<GLfloat, 3 * 2> ortho_matrix = MakeOrthographicMatrix((float)layout.width,
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index cd782428e..2ec2e14ca 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -13,6 +13,8 @@
13#include "core/hw/gpu.h" 13#include "core/hw/gpu.h"
14 14
15#include "video_core/renderer_base.h" 15#include "video_core/renderer_base.h"
16#include "video_core/renderer_opengl/gl_state.h"
17#include "video_core/renderer_opengl/gl_rasterizer.h"
16 18
17class EmuWindow; 19class EmuWindow;
18 20
@@ -49,18 +51,18 @@ private:
49 }; 51 };
50 52
51 void InitOpenGLObjects(); 53 void InitOpenGLObjects();
52 static void ConfigureFramebufferTexture(TextureInfo& texture, 54 void ConfigureFramebufferTexture(TextureInfo& texture,
53 const GPU::Regs::FramebufferConfig& framebuffer); 55 const GPU::Regs::FramebufferConfig& framebuffer);
54 void DrawScreens(); 56 void DrawScreens();
55 void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); 57 void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h);
56 void UpdateFramerate(); 58 void UpdateFramerate();
57 59
58 // Loads framebuffer from emulated memory into the active OpenGL texture. 60 // Loads framebuffer from emulated memory into the active OpenGL texture.
59 static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, 61 void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
60 const TextureInfo& texture); 62 const TextureInfo& texture);
61 // Fills active OpenGL texture with the given RGB color. 63 // Fills active OpenGL texture with the given RGB color.
62 static void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, 64 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
63 const TextureInfo& texture); 65 const TextureInfo& texture);
64 66
65 /// Computes the viewport rectangle 67 /// Computes the viewport rectangle
66 MathUtil::Rectangle<unsigned> GetViewportExtent(); 68 MathUtil::Rectangle<unsigned> GetViewportExtent();
@@ -71,6 +73,8 @@ private:
71 int resolution_width; ///< Current resolution width 73 int resolution_width; ///< Current resolution width
72 int resolution_height; ///< Current resolution height 74 int resolution_height; ///< Current resolution height
73 75
76 OpenGLState state;
77
74 // OpenGL object IDs 78 // OpenGL object IDs
75 GLuint vertex_array_handle; 79 GLuint vertex_array_handle;
76 GLuint vertex_buffer_handle; 80 GLuint vertex_buffer_handle;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 42e3bdd5b..d4d907d5e 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -6,6 +6,7 @@
6#include "common/emu_window.h" 6#include "common/emu_window.h"
7 7
8#include "core/core.h" 8#include "core/core.h"
9#include "core/settings.h"
9 10
10#include "video_core/video_core.h" 11#include "video_core/video_core.h"
11#include "video_core/renderer_base.h" 12#include "video_core/renderer_base.h"
@@ -19,6 +20,8 @@ namespace VideoCore {
19EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window 20EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
20RendererBase* g_renderer = nullptr; ///< Renderer plugin 21RendererBase* g_renderer = nullptr; ///< Renderer plugin
21 22
23std::atomic<bool> g_hw_renderer_enabled;
24
22/// Initialize the video core 25/// Initialize the video core
23void Init(EmuWindow* emu_window) { 26void Init(EmuWindow* emu_window) {
24 g_emu_window = emu_window; 27 g_emu_window = emu_window;
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index f885bec21..3f24df7bd 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -8,6 +8,8 @@
8 8
9#include "renderer_base.h" 9#include "renderer_base.h"
10 10
11#include <atomic>
12
11//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
12// Video Core namespace 14// Video Core namespace
13 15
@@ -31,6 +33,9 @@ static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height
31extern RendererBase* g_renderer; ///< Renderer plugin 33extern RendererBase* g_renderer; ///< Renderer plugin
32extern EmuWindow* g_emu_window; ///< Emu window 34extern EmuWindow* g_emu_window; ///< Emu window
33 35
36// TODO: Wrap this in a user settings struct along with any other graphics settings (often set from qt ui)
37extern std::atomic<bool> g_hw_renderer_enabled;
38
34/// Start the video core 39/// Start the video core
35void Start(); 40void Start();
36 41