summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2015-03-10 18:08:55 -0400
committerGravatar bunnei2015-03-10 18:08:55 -0400
commitb56829df020a81248dd04688ff2b307f3444a09f (patch)
tree24a438627339b2d506e659adfa0873cdf037852a /src
parentMerge pull request #649 from lioncash/clean (diff)
parentAdded LCD registers, and implementation for color filling in OGL code. (diff)
downloadyuzu-b56829df020a81248dd04688ff2b307f3444a09f.tar.gz
yuzu-b56829df020a81248dd04688ff2b307f3444a09f.tar.xz
yuzu-b56829df020a81248dd04688ff2b307f3444a09f.zip
Merge pull request #629 from archshift/lcdfb
Implement SetLcdForceBlack and add implementation for color filling in the GPU code
Diffstat (limited to 'src')
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/gsp_gpu.cpp44
-rw-r--r--src/core/hw/gpu.cpp9
-rw-r--r--src/core/hw/gpu.h2
-rw-r--r--src/core/hw/hw.cpp38
-rw-r--r--src/core/hw/hw.h24
-rw-r--r--src/core/hw/lcd.cpp66
-rw-r--r--src/core/hw/lcd.h88
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp54
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h5
12 files changed, 282 insertions, 52 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 7b479b569..649640e72 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -45,6 +45,7 @@ static std::shared_ptr<Logger> global_logger;
45 SUB(Service, SOC) \ 45 SUB(Service, SOC) \
46 CLS(HW) \ 46 CLS(HW) \
47 SUB(HW, Memory) \ 47 SUB(HW, Memory) \
48 SUB(HW, LCD) \
48 SUB(HW, GPU) \ 49 SUB(HW, GPU) \
49 CLS(Frontend) \ 50 CLS(Frontend) \
50 CLS(Render) \ 51 CLS(Render) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 7b67b3c07..83d64145b 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -65,6 +65,7 @@ enum class Class : ClassType {
65 Service_SOC, ///< The SOC (Socket) service 65 Service_SOC, ///< The SOC (Socket) service
66 HW, ///< Low-level hardware emulation 66 HW, ///< Low-level hardware emulation
67 HW_Memory, ///< Memory-map and address translation 67 HW_Memory, ///< Memory-map and address translation
68 HW_LCD, ///< LCD register emulation
68 HW_GPU, ///< GPU control emulation 69 HW_GPU, ///< GPU control emulation
69 Frontend, ///< Emulator UI 70 Frontend, ///< Emulator UI
70 Render, ///< Emulator video output and hardware acceleration 71 Render, ///< Emulator video output and hardware acceleration
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 212da25c5..33e5be3a4 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -87,6 +87,7 @@ set(SRCS
87 hle/svc.cpp 87 hle/svc.cpp
88 hw/gpu.cpp 88 hw/gpu.cpp
89 hw/hw.cpp 89 hw/hw.cpp
90 hw/lcd.cpp
90 loader/elf.cpp 91 loader/elf.cpp
91 loader/loader.cpp 92 loader/loader.cpp
92 loader/ncch.cpp 93 loader/ncch.cpp
@@ -196,6 +197,7 @@ set(HEADERS
196 hle/svc.h 197 hle/svc.h
197 hw/gpu.h 198 hw/gpu.h
198 hw/hw.h 199 hw/hw.h
200 hw/lcd.h
199 loader/elf.h 201 loader/elf.h
200 loader/loader.h 202 loader/loader.h
201 loader/ncch.h 203 loader/ncch.h
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index c23cfa3c8..cff585698 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -7,14 +7,20 @@
7#include "core/mem_map.h" 7#include "core/mem_map.h"
8#include "core/hle/kernel/event.h" 8#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/shared_memory.h" 9#include "core/hle/kernel/shared_memory.h"
10#include "core/hle/result.h"
10#include "gsp_gpu.h" 11#include "gsp_gpu.h"
12#include "core/hw/hw.h"
11#include "core/hw/gpu.h" 13#include "core/hw/gpu.h"
14#include "core/hw/lcd.h"
12 15
13#include "video_core/gpu_debugger.h" 16#include "video_core/gpu_debugger.h"
14 17
15// Main graphics debugger object - TODO: Here is probably not the best place for this 18// Main graphics debugger object - TODO: Here is probably not the best place for this
16GraphicsDebugger g_debugger; 19GraphicsDebugger g_debugger;
17 20
21// Beginning address of HW regs
22const static u32 REGS_BEGIN = 0x1EB00000;
23
18//////////////////////////////////////////////////////////////////////////////////////////////////// 24////////////////////////////////////////////////////////////////////////////////////////////////////
19// Namespace GSP_GPU 25// Namespace GSP_GPU
20 26
@@ -85,7 +91,7 @@ static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
85 return; 91 return;
86 92
87 while (size_in_bytes > 0) { 93 while (size_in_bytes > 0) {
88 GPU::Write<u32>(base_address + 0x1EB00000, *data); 94 HW::Write<u32>(base_address + REGS_BEGIN, *data);
89 95
90 size_in_bytes -= 4; 96 size_in_bytes -= 4;
91 ++data; 97 ++data;
@@ -128,15 +134,15 @@ static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32*
128 return; 134 return;
129 135
130 while (size_in_bytes > 0) { 136 while (size_in_bytes > 0) {
131 const u32 reg_address = base_address + 0x1EB00000; 137 const u32 reg_address = base_address + REGS_BEGIN;
132 138
133 u32 reg_value; 139 u32 reg_value;
134 GPU::Read<u32>(reg_value, reg_address); 140 HW::Read<u32>(reg_value, reg_address);
135 141
136 // Update the current value of the register only for set mask bits 142 // Update the current value of the register only for set mask bits
137 reg_value = (reg_value & ~*masks) | (*data | *masks); 143 reg_value = (reg_value & ~*masks) | (*data | *masks);
138 144
139 GPU::Write<u32>(reg_address, reg_value); 145 HW::Write<u32>(reg_address, reg_value);
140 146
141 size_in_bytes -= 4; 147 size_in_bytes -= 4;
142 ++data; 148 ++data;
@@ -188,7 +194,7 @@ static void ReadHWRegs(Service::Interface* self) {
188 u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]); 194 u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
189 195
190 while (size > 0) { 196 while (size > 0) {
191 GPU::Read<u32>(*dst, reg_addr + 0x1EB00000); 197 HW::Read<u32>(*dst, reg_addr + REGS_BEGIN);
192 198
193 size -= 4; 199 size -= 4;
194 ++dst; 200 ++dst;
@@ -427,6 +433,32 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
427 } 433 }
428} 434}
429 435
436/**
437 * GSP_GPU::SetLcdForceBlack service function
438 *
439 * Enable or disable REG_LCDCOLORFILL with the color black.
440 *
441 * Inputs:
442 * 1: Black color fill flag (0 = don't fill, !0 = fill)
443 * Outputs:
444 * 1: Result code
445 */
446static void SetLcdForceBlack(Service::Interface* self) {
447 u32* cmd_buff = Kernel::GetCommandBuffer();
448
449 bool enable_black = cmd_buff[1] != 0;
450 LCD::Regs::ColorFill data = {0};
451
452 // Since data is already zeroed, there is no need to explicitly set
453 // the color to black (all zero).
454 data.is_enabled = enable_black;
455
456 LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_top), data.raw); // Top LCD
457 LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_bottom), data.raw); // Bottom LCD
458
459 cmd_buff[1] = RESULT_SUCCESS.raw;
460}
461
430/// This triggers handling of the GX command written to the command buffer in shared memory. 462/// This triggers handling of the GX command written to the command buffer in shared memory.
431static void TriggerCmdReqQueue(Service::Interface* self) { 463static void TriggerCmdReqQueue(Service::Interface* self) {
432 // Iterate through each thread's command queue... 464 // Iterate through each thread's command queue...
@@ -460,7 +492,7 @@ const Interface::FunctionInfo FunctionTable[] = {
460 {0x00080082, FlushDataCache, "FlushDataCache"}, 492 {0x00080082, FlushDataCache, "FlushDataCache"},
461 {0x00090082, nullptr, "InvalidateDataCache"}, 493 {0x00090082, nullptr, "InvalidateDataCache"},
462 {0x000A0044, nullptr, "RegisterInterruptEvents"}, 494 {0x000A0044, nullptr, "RegisterInterruptEvents"},
463 {0x000B0040, nullptr, "SetLcdForceBlack"}, 495 {0x000B0040, SetLcdForceBlack, "SetLcdForceBlack"},
464 {0x000C0000, TriggerCmdReqQueue, "TriggerCmdReqQueue"}, 496 {0x000C0000, TriggerCmdReqQueue, "TriggerCmdReqQueue"},
465 {0x000D0140, nullptr, "SetDisplayTransfer"}, 497 {0x000D0140, nullptr, "SetDisplayTransfer"},
466 {0x000E0180, nullptr, "SetTextureCopy"}, 498 {0x000E0180, nullptr, "SetTextureCopy"},
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index b7102b874..30318fc06 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -15,12 +15,13 @@
15#include "core/hle/service/gsp_gpu.h" 15#include "core/hle/service/gsp_gpu.h"
16#include "core/hle/service/dsp_dsp.h" 16#include "core/hle/service/dsp_dsp.h"
17 17
18#include "core/hw/hw.h"
18#include "core/hw/gpu.h" 19#include "core/hw/gpu.h"
19 20
20#include "video_core/command_processor.h" 21#include "video_core/command_processor.h"
21#include "video_core/utils.h" 22#include "video_core/utils.h"
22#include "video_core/video_core.h" 23#include "video_core/video_core.h"
23#include <video_core/color.h> 24#include "video_core/color.h"
24 25
25namespace GPU { 26namespace GPU {
26 27
@@ -40,7 +41,7 @@ static bool last_skip_frame = false;
40 41
41template <typename T> 42template <typename T>
42inline void Read(T &var, const u32 raw_addr) { 43inline void Read(T &var, const u32 raw_addr) {
43 u32 addr = raw_addr - 0x1EF00000; 44 u32 addr = raw_addr - HW::VADDR_GPU;
44 u32 index = addr / 4; 45 u32 index = addr / 4;
45 46
46 // Reads other than u32 are untested, so I'd rather have them abort than silently fail 47 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
@@ -54,7 +55,7 @@ inline void Read(T &var, const u32 raw_addr) {
54 55
55template <typename T> 56template <typename T>
56inline void Write(u32 addr, const T data) { 57inline void Write(u32 addr, const T data) {
57 addr -= 0x1EF00000; 58 addr -= HW::VADDR_GPU;
58 u32 index = addr / 4; 59 u32 index = addr / 4;
59 60
60 // Writes other than u32 are untested, so I'd rather have them abort than silently fail 61 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
@@ -313,8 +314,6 @@ void Init() {
313 framebuffer_top.address_right2 = 0x182B9800; 314 framebuffer_top.address_right2 = 0x182B9800;
314 framebuffer_sub.address_left1 = 0x1848F000; 315 framebuffer_sub.address_left1 = 0x1848F000;
315 framebuffer_sub.address_left2 = 0x184C7800; 316 framebuffer_sub.address_left2 = 0x184C7800;
316 //framebuffer_sub.address_right1 = unknown;
317 //framebuffer_sub.address_right2 = unknown;
318 317
319 framebuffer_top.width = 240; 318 framebuffer_top.width = 240;
320 framebuffer_top.height = 400; 319 framebuffer_top.height = 400;
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 3e81f03ef..5b7f0a4e9 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -246,6 +246,8 @@ struct Regs {
246 return content[index]; 246 return content[index];
247 } 247 }
248 248
249#undef ASSERT_MEMBER_SIZE
250
249private: 251private:
250 /* 252 /*
251 * Most physical addresses which GPU registers refer to are 8-byte aligned. 253 * Most physical addresses which GPU registers refer to are 8-byte aligned.
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index a63ba6eeb..bed50af50 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -6,43 +6,19 @@
6 6
7#include "core/hw/hw.h" 7#include "core/hw/hw.h"
8#include "core/hw/gpu.h" 8#include "core/hw/gpu.h"
9#include "core/hw/lcd.h"
9 10
10namespace HW { 11namespace HW {
11 12
12enum {
13 VADDR_HASH = 0x1EC01000,
14 VADDR_CSND = 0x1EC03000,
15 VADDR_DSP = 0x1EC40000,
16 VADDR_PDN = 0x1EC41000,
17 VADDR_CODEC = 0x1EC41000,
18 VADDR_SPI = 0x1EC42000,
19 VADDR_SPI_2 = 0x1EC43000, // Only used under TWL_FIRM?
20 VADDR_I2C = 0x1EC44000,
21 VADDR_CODEC_2 = 0x1EC45000,
22 VADDR_HID = 0x1EC46000,
23 VADDR_PAD = 0x1EC46000,
24 VADDR_PTM = 0x1EC46000,
25 VADDR_GPIO = 0x1EC47000,
26 VADDR_I2C_2 = 0x1EC48000,
27 VADDR_SPI_3 = 0x1EC60000,
28 VADDR_I2C_3 = 0x1EC61000,
29 VADDR_MIC = 0x1EC62000,
30 VADDR_PXI = 0x1EC63000, // 0xFFFD2000
31 //VADDR_NTRCARD
32 VADDR_CDMA = 0xFFFDA000, // CoreLink DMA-330? Info
33 VADDR_DSP_2 = 0x1ED03000,
34 VADDR_HASH_2 = 0x1EE01000,
35 VADDR_GPU = 0x1EF00000,
36};
37
38template <typename T> 13template <typename T>
39inline void Read(T &var, const u32 addr) { 14inline void Read(T &var, const u32 addr) {
40 switch (addr & 0xFFFFF000) { 15 switch (addr & 0xFFFFF000) {
41
42 case VADDR_GPU: 16 case VADDR_GPU:
43 GPU::Read(var, addr); 17 GPU::Read(var, addr);
44 break; 18 break;
45 19 case VADDR_LCD:
20 LCD::Write(var, addr);
21 break;
46 default: 22 default:
47 LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); 23 LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
48 } 24 }
@@ -51,11 +27,12 @@ inline void Read(T &var, const u32 addr) {
51template <typename T> 27template <typename T>
52inline void Write(u32 addr, const T data) { 28inline void Write(u32 addr, const T data) {
53 switch (addr & 0xFFFFF000) { 29 switch (addr & 0xFFFFF000) {
54
55 case VADDR_GPU: 30 case VADDR_GPU:
56 GPU::Write(addr, data); 31 GPU::Write(addr, data);
57 break; 32 break;
58 33 case VADDR_LCD:
34 LCD::Write(addr, data);
35 break;
59 default: 36 default:
60 LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr); 37 LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
61 } 38 }
@@ -80,6 +57,7 @@ void Update() {
80/// Initialize hardware 57/// Initialize hardware
81void Init() { 58void Init() {
82 GPU::Init(); 59 GPU::Init();
60 LCD::Init();
83 LOG_DEBUG(HW, "initialized OK"); 61 LOG_DEBUG(HW, "initialized OK");
84} 62}
85 63
diff --git a/src/core/hw/hw.h b/src/core/hw/hw.h
index 991c0a07d..d65608910 100644
--- a/src/core/hw/hw.h
+++ b/src/core/hw/hw.h
@@ -8,6 +8,30 @@
8 8
9namespace HW { 9namespace HW {
10 10
11/// Beginnings of IO register regions, in the user VA space.
12enum : u32 {
13 VADDR_HASH = 0x1EC01000,
14 VADDR_CSND = 0x1EC03000,
15 VADDR_DSP = 0x1EC40000,
16 VADDR_PDN = 0x1EC41000,
17 VADDR_CODEC = 0x1EC41000,
18 VADDR_SPI = 0x1EC42000,
19 VADDR_SPI_2 = 0x1EC43000, // Only used under TWL_FIRM?
20 VADDR_I2C = 0x1EC44000,
21 VADDR_CODEC_2 = 0x1EC45000,
22 VADDR_HID = 0x1EC46000,
23 VADDR_GPIO = 0x1EC47000,
24 VADDR_I2C_2 = 0x1EC48000,
25 VADDR_SPI_3 = 0x1EC60000,
26 VADDR_I2C_3 = 0x1EC61000,
27 VADDR_MIC = 0x1EC62000,
28 VADDR_PXI = 0x1EC63000,
29 VADDR_LCD = 0x1ED02000,
30 VADDR_DSP_2 = 0x1ED03000,
31 VADDR_HASH_2 = 0x1EE01000,
32 VADDR_GPU = 0x1EF00000,
33};
34
11template <typename T> 35template <typename T>
12void Read(T &var, const u32 addr); 36void Read(T &var, const u32 addr);
13 37
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp
new file mode 100644
index 000000000..7986f3ddb
--- /dev/null
+++ b/src/core/hw/lcd.cpp
@@ -0,0 +1,66 @@
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/common_types.h"
6
7#include "core/arm/arm_interface.h"
8#include "core/hle/hle.h"
9#include "core/hw/hw.h"
10#include "core/hw/lcd.h"
11
12namespace LCD {
13
14Regs g_regs;
15
16template <typename T>
17inline void Read(T &var, const u32 raw_addr) {
18 u32 addr = raw_addr - HW::VADDR_LCD;
19 u32 index = addr / 4;
20
21 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
22 if (index >= 0x400 || !std::is_same<T, u32>::value) {
23 LOG_ERROR(HW_LCD, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
24 return;
25 }
26
27 var = g_regs[index];
28}
29
30template <typename T>
31inline void Write(u32 addr, const T data) {
32 addr -= HW::VADDR_LCD;
33 u32 index = addr / 4;
34
35 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
36 if (index >= 0x400 || !std::is_same<T, u32>::value) {
37 LOG_ERROR(HW_LCD, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
38 return;
39 }
40
41 g_regs[index] = static_cast<u32>(data);
42}
43
44// Explicitly instantiate template functions because we aren't defining this in the header:
45
46template void Read<u64>(u64 &var, const u32 addr);
47template void Read<u32>(u32 &var, const u32 addr);
48template void Read<u16>(u16 &var, const u32 addr);
49template void Read<u8>(u8 &var, const u32 addr);
50
51template void Write<u64>(u32 addr, const u64 data);
52template void Write<u32>(u32 addr, const u32 data);
53template void Write<u16>(u32 addr, const u16 data);
54template void Write<u8>(u32 addr, const u8 data);
55
56/// Initialize hardware
57void Init() {
58 LOG_DEBUG(HW_LCD, "initialized OK");
59}
60
61/// Shutdown hardware
62void Shutdown() {
63 LOG_DEBUG(HW_LCD, "shutdown OK");
64}
65
66} // namespace
diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h
new file mode 100644
index 000000000..43893a625
--- /dev/null
+++ b/src/core/hw/lcd.h
@@ -0,0 +1,88 @@
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 <cstddef>
8
9#include "common/common_types.h"
10#include "common/bit_field.h"
11
12#define LCD_REG_INDEX(field_name) (offsetof(LCD::Regs, field_name) / sizeof(u32))
13
14namespace LCD {
15
16struct Regs {
17
18 union ColorFill {
19 u32 raw;
20
21 BitField<0, 8, u32> color_r;
22 BitField<8, 8, u32> color_g;
23 BitField<16, 8, u32> color_b;
24 BitField<24, 1, u32> is_enabled;
25 };
26
27 INSERT_PADDING_WORDS(0x81);
28 ColorFill color_fill_top;
29 INSERT_PADDING_WORDS(0xE);
30 u32 backlight_top;
31
32 INSERT_PADDING_WORDS(0x1F0);
33
34 ColorFill color_fill_bottom;
35 INSERT_PADDING_WORDS(0xE);
36 u32 backlight_bottom;
37 INSERT_PADDING_WORDS(0x16F);
38
39 static inline size_t NumIds() {
40 return sizeof(Regs) / sizeof(u32);
41 }
42
43 u32& operator [] (int index) const {
44 u32* content = (u32*)this;
45 return content[index];
46 }
47
48 u32& operator [] (int index) {
49 u32* content = (u32*)this;
50 return content[index];
51 }
52
53#undef ASSERT_MEMBER_SIZE
54
55};
56static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout");
57
58// TODO: MSVC does not support using offsetof() on non-static data members even though this
59// is technically allowed since C++11. This macro should be enabled once MSVC adds
60// support for that.
61#ifndef _MSC_VER
62#define ASSERT_REG_POSITION(field_name, position) \
63 static_assert(offsetof(Regs, field_name) == position * 4, \
64 "Field "#field_name" has invalid position")
65
66ASSERT_REG_POSITION(color_fill_top, 0x81);
67ASSERT_REG_POSITION(backlight_top, 0x90);
68ASSERT_REG_POSITION(color_fill_bottom, 0x281);
69ASSERT_REG_POSITION(backlight_bottom, 0x290);
70
71#undef ASSERT_REG_POSITION
72#endif // !defined(_MSC_VER)
73
74extern Regs g_regs;
75
76template <typename T>
77void Read(T &var, const u32 addr);
78
79template <typename T>
80void Write(u32 addr, const T data);
81
82/// Initialize hardware
83void Init();
84
85/// Shutdown hardware
86void Shutdown();
87
88} // namespace
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 95ab96340..4273a177f 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -3,6 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hw/gpu.h" 5#include "core/hw/gpu.h"
6#include "core/hw/hw.h"
7#include "core/hw/lcd.h"
6#include "core/mem_map.h" 8#include "core/mem_map.h"
7 9
8#include "common/emu_window.h" 10#include "common/emu_window.h"
@@ -64,16 +66,33 @@ void RendererOpenGL::SwapBuffers() {
64 for(int i : {0, 1}) { 66 for(int i : {0, 1}) {
65 const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; 67 const auto& framebuffer = GPU::g_regs.framebuffer_config[i];
66 68
67 if (textures[i].width != (GLsizei)framebuffer.width || 69 // Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04
68 textures[i].height != (GLsizei)framebuffer.height || 70 u32 lcd_color_addr = (i == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom);
69 textures[i].format != framebuffer.color_format) { 71 lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr;
70 // Reallocate texture if the framebuffer size has changed. 72 LCD::Regs::ColorFill color_fill = {0};
71 // This is expected to not happen very often and hence should not be a 73 LCD::Read(color_fill.raw, lcd_color_addr);
72 // performance problem. 74
73 ConfigureFramebufferTexture(textures[i], framebuffer); 75 if (color_fill.is_enabled) {
76 LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, textures[i]);
77
78 // Resize the texture in case the framebuffer size has changed
79 textures[i].width = 1;
80 textures[i].height = 1;
81 } else {
82 if (textures[i].width != (GLsizei)framebuffer.width ||
83 textures[i].height != (GLsizei)framebuffer.height ||
84 textures[i].format != framebuffer.color_format) {
85 // Reallocate texture if the framebuffer size has changed.
86 // This is expected to not happen very often and hence should not be a
87 // performance problem.
88 ConfigureFramebufferTexture(textures[i], framebuffer);
89 }
90 LoadFBToActiveGLTexture(framebuffer, textures[i]);
91
92 // Resize the texture in case the framebuffer size has changed
93 textures[i].width = framebuffer.width;
94 textures[i].height = framebuffer.height;
74 } 95 }
75
76 LoadFBToActiveGLTexture(GPU::g_regs.framebuffer_config[i], textures[i]);
77 } 96 }
78 97
79 DrawScreens(); 98 DrawScreens();
@@ -127,10 +146,25 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
127 // TODO: Applications could theoretically crash Citra here by specifying too large 146 // TODO: Applications could theoretically crash Citra here by specifying too large
128 // framebuffer sizes. We should make sure that this cannot happen. 147 // framebuffer sizes. We should make sure that this cannot happen.
129 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, 148 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height,
130 texture.gl_format, texture.gl_type, framebuffer_data); 149 texture.gl_format, texture.gl_type, framebuffer_data);
131 150
132 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 151 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
152 glBindTexture(GL_TEXTURE_2D, 0);
153}
133 154
155/**
156 * Fills active OpenGL texture with the given RGB color.
157 * Since the color is solid, the texture can be 1x1 but will stretch across whatever it's rendered on.
158 * This has the added benefit of being *really fast*.
159 */
160void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
161 const TextureInfo& texture) {
162 glBindTexture(GL_TEXTURE_2D, texture.handle);
163
164 u8 framebuffer_data[3] = { color_r, color_g, color_b };
165
166 // Update existing texture
167 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data);
134 glBindTexture(GL_TEXTURE_2D, 0); 168 glBindTexture(GL_TEXTURE_2D, 0);
135} 169}
136 170
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index bcabab557..cd782428e 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -58,6 +58,9 @@ private:
58 // Loads framebuffer from emulated memory into the active OpenGL texture. 58 // Loads framebuffer from emulated memory into the active OpenGL texture.
59 static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, 59 static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
60 const TextureInfo& texture); 60 const TextureInfo& texture);
61 // Fills active OpenGL texture with the given RGB color.
62 static void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
63 const TextureInfo& texture);
61 64
62 /// Computes the viewport rectangle 65 /// Computes the viewport rectangle
63 MathUtil::Rectangle<unsigned> GetViewportExtent(); 66 MathUtil::Rectangle<unsigned> GetViewportExtent();
@@ -72,7 +75,7 @@ private:
72 GLuint vertex_array_handle; 75 GLuint vertex_array_handle;
73 GLuint vertex_buffer_handle; 76 GLuint vertex_buffer_handle;
74 GLuint program_id; 77 GLuint program_id;
75 std::array<TextureInfo, 2> textures; 78 std::array<TextureInfo, 2> textures; ///< Textures for top and bottom screens respectively
76 // Shader uniform location indices 79 // Shader uniform location indices
77 GLuint uniform_modelview_matrix; 80 GLuint uniform_modelview_matrix;
78 GLuint uniform_color_texture; 81 GLuint uniform_color_texture;