diff options
| author | 2015-05-18 21:21:33 -0700 | |
|---|---|---|
| committer | 2015-05-22 15:51:18 -0700 | |
| commit | 05dc633a8c35221ce8d6abe6ddf027f8b0bab6c2 (patch) | |
| tree | d080c1efd3b928bda551cb9eee304547e66a4351 | |
| parent | INI hw/sw renderer toggle (diff) | |
| download | yuzu-05dc633a8c35221ce8d6abe6ddf027f8b0bab6c2.tar.gz yuzu-05dc633a8c35221ce8d6abe6ddf027f8b0bab6c2.tar.xz yuzu-05dc633a8c35221ce8d6abe6ddf027f8b0bab6c2.zip | |
OpenGL renderer
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 |
| 22 | int main(int argc, char **argv) { | 24 | int 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 | ||
| 49 | GMainWindow::GMainWindow() : emu_thread(nullptr) | 51 | GMainWindow::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 | ||
| 309 | void GMainWindow::SetHardwareRendererEnabled(bool enabled) { | ||
| 310 | VideoCore::g_hw_renderer_enabled = enabled; | ||
| 311 | } | ||
| 303 | 312 | ||
| 304 | void GMainWindow::ToggleWindowMode() { | 313 | void 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 | ||
| 74 | private: | 75 | private: |
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 &Hotkeys ...</string> | 132 | <string>Configure &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 @@ | |||
| 11 | namespace MathUtil | 11 | namespace MathUtil |
| 12 | { | 12 | { |
| 13 | 13 | ||
| 14 | inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, unsigned length1) { | ||
| 15 | return (std::max(start0, start1) <= std::min(start0 + length0, start1 + length1)); | ||
| 16 | } | ||
| 17 | |||
| 14 | template<typename T> | 18 | template<typename T> |
| 15 | inline T Clamp(const T val, const T& min, const T& max) | 19 | inline 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 |
| 20 | GraphicsDebugger g_debugger; | 21 | GraphicsDebugger 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 @@ | |||
| 1 | set(SRCS | 1 | set(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 | |||
| 15 | set(HEADERS | 19 | set(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 | |||
| 10 | class HWRasterizer { | ||
| 11 | public: | ||
| 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 | |||
| 9 | class RendererBase : NonCopyable { | 11 | class RendererBase : NonCopyable { |
| 10 | public: | 12 | public: |
| 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 | |||
| 51 | protected: | 55 | protected: |
| 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 | |||
| 20 | static 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 | |||
| 31 | RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr(0) { } | ||
| 32 | RasterizerOpenGL::~RasterizerOpenGL() { } | ||
| 33 | |||
| 34 | void 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 | |||
| 130 | void 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 | |||
| 187 | void 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 | |||
| 195 | void 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 | |||
| 207 | void RasterizerOpenGL::CommitFramebuffer() { | ||
| 208 | CommitColorBuffer(); | ||
| 209 | CommitDepthBuffer(); | ||
| 210 | } | ||
| 211 | |||
| 212 | void 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 | |||
| 362 | void 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 | |||
| 382 | void 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 | |||
| 405 | void 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 | |||
| 462 | void 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 | |||
| 503 | void 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 | |||
| 574 | void 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 | |||
| 597 | void RasterizerOpenGL::SyncBlendEnabled() { | ||
| 598 | state.blend.enabled = Pica::registers.output_merger.alphablend_enable; | ||
| 599 | } | ||
| 600 | |||
| 601 | void 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 | |||
| 608 | void 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 | |||
| 616 | void 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 | |||
| 622 | void RasterizerOpenGL::SyncStencilTest() { | ||
| 623 | // TODO: Implement stencil test, mask, and op | ||
| 624 | } | ||
| 625 | |||
| 626 | void 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 | |||
| 632 | void 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 | |||
| 644 | void 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 | |||
| 656 | void 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 | |||
| 660 | void 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 | |||
| 665 | void 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 | |||
| 669 | void 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 | |||
| 674 | void 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 | |||
| 683 | void 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 | |||
| 718 | void 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 | |||
| 749 | void 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 | |||
| 800 | void 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 | |||
| 831 | void 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 | |||
| 12 | class RasterizerOpenGL : public HWRasterizer { | ||
| 13 | public: | ||
| 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 | |||
| 44 | private: | ||
| 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 | |||
| 15 | RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { | ||
| 16 | FullFlush(); | ||
| 17 | } | ||
| 18 | |||
| 19 | void 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 | |||
| 62 | void 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 | |||
| 75 | void 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 | |||
| 14 | class RasterizerCacheOpenGL : NonCopyable { | ||
| 15 | public: | ||
| 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 | |||
| 27 | private: | ||
| 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 | ||
| 9 | OGLTexture::OGLTexture() : handle(0) { | ||
| 10 | } | ||
| 11 | |||
| 12 | OGLTexture::~OGLTexture() { | ||
| 13 | Release(); | ||
| 14 | } | ||
| 15 | |||
| 16 | void OGLTexture::Create() { | ||
| 17 | if (handle != 0) { | ||
| 18 | return; | ||
| 19 | } | ||
| 20 | |||
| 21 | glGenTextures(1, &handle); | ||
| 22 | } | ||
| 23 | |||
| 24 | void OGLTexture::Release() { | ||
| 25 | glDeleteTextures(1, &handle); | ||
| 26 | handle = 0; | ||
| 27 | } | ||
| 28 | |||
| 29 | // Shaders | ||
| 30 | OGLShader::OGLShader() : handle(0) { | ||
| 31 | } | ||
| 32 | |||
| 33 | OGLShader::~OGLShader() { | ||
| 34 | Release(); | ||
| 35 | } | ||
| 36 | |||
| 37 | void 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 | |||
| 45 | void OGLShader::Release() { | ||
| 46 | glDeleteProgram(handle); | ||
| 47 | handle = 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | // Buffer objects | ||
| 51 | OGLBuffer::OGLBuffer() : handle(0) { | ||
| 52 | } | ||
| 53 | |||
| 54 | OGLBuffer::~OGLBuffer() { | ||
| 55 | Release(); | ||
| 56 | } | ||
| 57 | |||
| 58 | void OGLBuffer::Create() { | ||
| 59 | if (handle != 0) { | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | |||
| 63 | glGenBuffers(1, &handle); | ||
| 64 | } | ||
| 65 | |||
| 66 | void OGLBuffer::Release() { | ||
| 67 | glDeleteBuffers(1, &handle); | ||
| 68 | handle = 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | // Vertex array objects | ||
| 72 | OGLVertexArray::OGLVertexArray() : handle(0) { | ||
| 73 | } | ||
| 74 | |||
| 75 | OGLVertexArray::~OGLVertexArray() { | ||
| 76 | Release(); | ||
| 77 | } | ||
| 78 | |||
| 79 | void OGLVertexArray::Create() { | ||
| 80 | if (handle != 0) { | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | |||
| 84 | glGenVertexArrays(1, &handle); | ||
| 85 | } | ||
| 86 | |||
| 87 | void OGLVertexArray::Release() { | ||
| 88 | glDeleteVertexArrays(1, &handle); | ||
| 89 | handle = 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | // Framebuffers | ||
| 93 | OGLFramebuffer::OGLFramebuffer() : handle(0) { | ||
| 94 | } | ||
| 95 | |||
| 96 | OGLFramebuffer::~OGLFramebuffer() { | ||
| 97 | Release(); | ||
| 98 | } | ||
| 99 | |||
| 100 | void OGLFramebuffer::Create() { | ||
| 101 | if (handle != 0) { | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | glGenFramebuffers(1, &handle); | ||
| 106 | } | ||
| 107 | |||
| 108 | void 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 | |||
| 11 | class OGLTexture : public NonCopyable { | ||
| 12 | public: | ||
| 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 | |||
| 25 | class OGLShader : public NonCopyable { | ||
| 26 | public: | ||
| 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 | |||
| 39 | class OGLBuffer : public NonCopyable { | ||
| 40 | public: | ||
| 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 | |||
| 53 | class OGLVertexArray : public NonCopyable { | ||
| 54 | public: | ||
| 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 | |||
| 67 | class OGLFramebuffer : public NonCopyable { | ||
| 68 | public: | ||
| 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 | ||
| 45 | const char g_vertex_shader_hw[] = R"( | ||
| 46 | #version 150 core | ||
| 47 | |||
| 48 | #define NUM_VTX_ATTR 7 | ||
| 49 | |||
| 50 | in vec4 vert_position; | ||
| 51 | in vec4 vert_color; | ||
| 52 | in vec2 vert_texcoords[3]; | ||
| 53 | |||
| 54 | out vec4 o[NUM_VTX_ATTR]; | ||
| 55 | |||
| 56 | void 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 | ||
| 66 | const 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 | |||
| 120 | in vec4 o[NUM_VTX_ATTR]; | ||
| 121 | out vec4 color; | ||
| 122 | |||
| 123 | uniform bool alphatest_enabled; | ||
| 124 | uniform int alphatest_func; | ||
| 125 | uniform float alphatest_ref; | ||
| 126 | |||
| 127 | uniform sampler2D tex[3]; | ||
| 128 | |||
| 129 | uniform vec4 tev_combiner_buffer_color; | ||
| 130 | |||
| 131 | struct 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 | |||
| 144 | uniform TEVConfig tev_cfgs[NUM_TEV_STAGES]; | ||
| 145 | |||
| 146 | vec4 g_combiner_buffer; | ||
| 147 | vec4 g_last_tex_env_out; | ||
| 148 | vec4 g_const_color; | ||
| 149 | |||
| 150 | vec4 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 | |||
| 176 | vec3 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 | |||
| 202 | float 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 | |||
| 224 | vec3 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 | |||
| 246 | float 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 | |||
| 268 | void 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 | |||
| 8 | OpenGLState OpenGLState::cur_state; | ||
| 9 | |||
| 10 | OpenGLState::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 | |||
| 46 | const 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 | |||
| 9 | class OpenGLState { | ||
| 10 | public: | ||
| 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 | |||
| 68 | private: | ||
| 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 | |||
| 13 | namespace PicaToGL { | ||
| 14 | |||
| 15 | inline 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 | |||
| 44 | inline 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 | |||
| 74 | inline 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 | |||
| 97 | inline 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 |
| 55 | RendererOpenGL::RendererOpenGL() { | 55 | RendererOpenGL::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) |
| 65 | void RendererOpenGL::SwapBuffers() { | 66 | void 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 | */ |
| 162 | void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, | 180 | void 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 | */ |
| 176 | void RendererOpenGL::InitOpenGLObjects() { | 196 | void 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 | ||
| 219 | void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | 245 | void 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 | ||
| 17 | class EmuWindow; | 19 | class 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 { | |||
| 19 | EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window | 20 | EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window |
| 20 | RendererBase* g_renderer = nullptr; ///< Renderer plugin | 21 | RendererBase* g_renderer = nullptr; ///< Renderer plugin |
| 21 | 22 | ||
| 23 | std::atomic<bool> g_hw_renderer_enabled; | ||
| 24 | |||
| 22 | /// Initialize the video core | 25 | /// Initialize the video core |
| 23 | void Init(EmuWindow* emu_window) { | 26 | void 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 | |||
| 31 | extern RendererBase* g_renderer; ///< Renderer plugin | 33 | extern RendererBase* g_renderer; ///< Renderer plugin |
| 32 | extern EmuWindow* g_emu_window; ///< Emu window | 34 | extern 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) | ||
| 37 | extern std::atomic<bool> g_hw_renderer_enabled; | ||
| 38 | |||
| 34 | /// Start the video core | 39 | /// Start the video core |
| 35 | void Start(); | 40 | void Start(); |
| 36 | 41 | ||