summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra_qt/CMakeLists.txt6
-rw-r--r--src/citra_qt/Info.plist40
-rw-r--r--src/citra_qt/bootmanager.cpp31
-rw-r--r--src/citra_qt/bootmanager.h2
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp8
-rw-r--r--src/citra_qt/game_list.cpp2
-rw-r--r--src/citra_qt/main.cpp19
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp.cpp12
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp.h2
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpdouble.cpp76
-rw-r--r--src/core/file_sys/archive_backend.h6
-rw-r--r--src/core/file_sys/disk_archive.cpp5
-rw-r--r--src/core/file_sys/disk_archive.h1
-rw-r--r--src/core/file_sys/ivfc_archive.cpp5
-rw-r--r--src/core/file_sys/ivfc_archive.h1
-rw-r--r--src/core/hle/kernel/thread.cpp2
-rw-r--r--src/core/hle/service/csnd_snd.cpp57
-rw-r--r--src/core/hle/service/csnd_snd.h13
-rw-r--r--src/core/hle/service/fs/archive.cpp7
-rw-r--r--src/core/hle/service/fs/archive.h7
-rw-r--r--src/core/hle/service/fs/fs_user.cpp29
-rw-r--r--src/core/hle/service/ptm/ptm.cpp12
-rw-r--r--src/core/hle/service/ptm/ptm.h8
-rw-r--r--src/core/hle/service/ptm/ptm_u.cpp2
-rw-r--r--src/core/hw/y2r.cpp2
-rw-r--r--src/core/loader/3dsx.cpp4
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/ncch.cpp8
-rw-r--r--src/core/loader/ncch.h36
-rw-r--r--src/core/memory.cpp4
-rw-r--r--src/video_core/CMakeLists.txt3
-rw-r--r--src/video_core/command_processor.cpp2
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp6
-rw-r--r--src/video_core/pica.h4
-rw-r--r--src/video_core/rasterizer.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp319
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h157
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp388
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h27
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h20
-rw-r--r--src/video_core/renderer_opengl/gl_shaders.h337
-rw-r--r--src/video_core/renderer_opengl/gl_state.h1
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp39
45 files changed, 998 insertions, 730 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 51a574629..bbf6ae001 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -24,6 +24,7 @@ set(SRCS
24 hotkeys.cpp 24 hotkeys.cpp
25 main.cpp 25 main.cpp
26 citra-qt.rc 26 citra-qt.rc
27 Info.plist
27 ) 28 )
28 29
29set(HEADERS 30set(HEADERS
@@ -71,7 +72,10 @@ else()
71endif() 72endif()
72 73
73if (APPLE) 74if (APPLE)
74 add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS}) 75 set(MACOSX_ICON "../../dist/citra.icns")
76 set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
77 add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${MACOSX_ICON})
78 set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
75else() 79else()
76 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) 80 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
77endif() 81endif()
diff --git a/src/citra_qt/Info.plist b/src/citra_qt/Info.plist
new file mode 100644
index 000000000..4c89e128b
--- /dev/null
+++ b/src/citra_qt/Info.plist
@@ -0,0 +1,40 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3<plist version="1.0">
4<dict>
5 <key>CFBundleDevelopmentRegion</key>
6 <string>English</string>
7 <key>CFBundleExecutable</key>
8 <string>$(EXECUTABLE_NAME)</string>
9 <key>CFBundleGetInfoString</key>
10 <string></string>
11 <key>CFBundleIconFile</key>
12 <string>citra.icns</string>
13 <key>CFBundleIdentifier</key>
14 <string>com.citra-emu.citra</string>
15 <key>CFBundleInfoDictionaryVersion</key>
16 <string>6.0</string>
17 <key>CFBundleLongVersionString</key>
18 <string></string>
19 <key>CFBundleName</key>
20 <string>Citra</string>
21 <key>CFBundlePackageType</key>
22 <string>APPL</string>
23 <key>CFBundleShortVersionString</key>
24 <string></string>
25 <key>CFBundleSignature</key>
26 <string>????</string>
27 <key>CFBundleVersion</key>
28 <string></string>
29 <key>CSResourcesFileMapped</key>
30 <true/>
31 <key>LSRequiresCarbon</key>
32 <true/>
33 <key>NSHumanReadableCopyright</key>
34 <string></string>
35 <key>NSPrincipalClass</key>
36 <string>NSApplication</string>
37 <key>NSHighResolutionCapable</key>
38 <string>True</string>
39</dict>
40</plist>
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index b19b367e1..8e60b9cad 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -181,16 +181,9 @@ void GRenderWindow::PollEvents() {
181void GRenderWindow::OnFramebufferSizeChanged() 181void GRenderWindow::OnFramebufferSizeChanged()
182{ 182{
183 // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size 183 // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size
184#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) 184 qreal pixelRatio = windowPixelRatio();
185 // windowHandle() might not be accessible until the window is displayed to screen. 185 unsigned width = child->QPaintDevice::width() * pixelRatio;
186 auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0; 186 unsigned height = child->QPaintDevice::height() * pixelRatio;
187
188 unsigned width = child->QPaintDevice::width() * pixel_ratio;
189 unsigned height = child->QPaintDevice::height() * pixel_ratio;
190#else
191 unsigned width = child->QPaintDevice::width();
192 unsigned height = child->QPaintDevice::height();
193#endif
194 187
195 NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); 188 NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
196} 189}
@@ -223,6 +216,16 @@ QByteArray GRenderWindow::saveGeometry()
223 return geometry; 216 return geometry;
224} 217}
225 218
219qreal GRenderWindow::windowPixelRatio()
220{
221#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
222 // windowHandle() might not be accessible until the window is displayed to screen.
223 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
224#else
225 return 1.0f;
226#endif
227}
228
226void GRenderWindow::closeEvent(QCloseEvent* event) { 229void GRenderWindow::closeEvent(QCloseEvent* event) {
227 emit Closed(); 230 emit Closed();
228 QWidget::closeEvent(event); 231 QWidget::closeEvent(event);
@@ -243,14 +246,18 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event)
243 if (event->button() == Qt::LeftButton) 246 if (event->button() == Qt::LeftButton)
244 { 247 {
245 auto pos = event->pos(); 248 auto pos = event->pos();
246 this->TouchPressed(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y())); 249 qreal pixelRatio = windowPixelRatio();
250 this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio),
251 static_cast<unsigned>(pos.y() * pixelRatio));
247 } 252 }
248} 253}
249 254
250void GRenderWindow::mouseMoveEvent(QMouseEvent *event) 255void GRenderWindow::mouseMoveEvent(QMouseEvent *event)
251{ 256{
252 auto pos = event->pos(); 257 auto pos = event->pos();
253 this->TouchMoved(static_cast<unsigned>(std::max(pos.x(), 0)), static_cast<unsigned>(std::max(pos.y(), 0))); 258 qreal pixelRatio = windowPixelRatio();
259 this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u),
260 std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
254} 261}
255 262
256void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) 263void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 0a9d263b8..0dcf3e5eb 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -111,6 +111,8 @@ public:
111 void restoreGeometry(const QByteArray& geometry); // overridden 111 void restoreGeometry(const QByteArray& geometry); // overridden
112 QByteArray saveGeometry(); // overridden 112 QByteArray saveGeometry(); // overridden
113 113
114 qreal windowPixelRatio();
115
114 void closeEvent(QCloseEvent* event) override; 116 void closeEvent(QCloseEvent* event) override;
115 117
116 void keyPressEvent(QKeyEvent* event) override; 118 void keyPressEvent(QKeyEvent* event) override;
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
index f915d2bab..a5a5fe6b0 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -294,16 +294,16 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
294 { 294 {
295 // Highlight current instruction 295 // Highlight current instruction
296 int current_record_index = par->cycle_index->value(); 296 int current_record_index = par->cycle_index->value();
297 if (current_record_index < par->debug_data.records.size()) { 297 if (current_record_index < static_cast<int>(par->debug_data.records.size())) {
298 const auto& current_record = par->debug_data.records[current_record_index]; 298 const auto& current_record = par->debug_data.records[current_record_index];
299 if (index.row() == current_record.instruction_offset) { 299 if (index.row() == static_cast<int>(current_record.instruction_offset)) {
300 return QColor(255, 255, 63); 300 return QColor(255, 255, 63);
301 } 301 }
302 } 302 }
303 303
304 // Use a grey background for instructions which have no debug data associated to them 304 // Use a grey background for instructions which have no debug data associated to them
305 for (const auto& record : par->debug_data.records) 305 for (const auto& record : par->debug_data.records)
306 if (index.row() == record.instruction_offset) 306 if (index.row() == static_cast<int>(record.instruction_offset))
307 return QVariant(); 307 return QVariant();
308 308
309 return QBrush(QColor(192, 192, 192)); 309 return QBrush(QColor(192, 192, 192));
@@ -494,7 +494,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
494 debug_data = Pica::Shader::ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup); 494 debug_data = Pica::Shader::ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup);
495 495
496 // Reload widget state 496 // Reload widget state
497 for (unsigned int attr = 0; attr < num_attributes; ++attr) { 497 for (int attr = 0; attr < num_attributes; ++attr) {
498 unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr); 498 unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr);
499 input_data_mapping[source_attr]->setText(QString("-> v%1").arg(attr)); 499 input_data_mapping[source_attr]->setText(QString("-> v%1").arg(attr));
500 input_data_container[source_attr]->setVisible(true); 500 input_data_container[source_attr]->setVisible(true);
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index dade3c212..e925f08a7 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -80,7 +80,7 @@ void GameList::DonePopulating()
80void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) 80void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
81{ 81{
82 if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) { 82 if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) {
83 LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLatin1().data()); 83 LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data());
84 return; 84 return;
85 } 85 }
86 86
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index e5ed01a11..d6c27f0df 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -247,7 +247,7 @@ void GMainWindow::OnDisplayTitleBars(bool show)
247} 247}
248 248
249void GMainWindow::BootGame(const std::string& filename) { 249void GMainWindow::BootGame(const std::string& filename) {
250 LOG_INFO(Frontend, "Citra starting...\n"); 250 LOG_INFO(Frontend, "Citra starting...");
251 251
252 // Shutdown previous session if the emu thread is still active... 252 // Shutdown previous session if the emu thread is still active...
253 if (emu_thread != nullptr) 253 if (emu_thread != nullptr)
@@ -362,7 +362,7 @@ void GMainWindow::UpdateRecentFiles() {
362} 362}
363 363
364void GMainWindow::OnGameListLoadFile(QString game_path) { 364void GMainWindow::OnGameListLoadFile(QString game_path) {
365 BootGame(game_path.toLatin1().data()); 365 BootGame(game_path.toLocal8Bit().data());
366} 366}
367 367
368void GMainWindow::OnMenuLoadFile() { 368void GMainWindow::OnMenuLoadFile() {
@@ -374,7 +374,7 @@ void GMainWindow::OnMenuLoadFile() {
374 settings.setValue("romsPath", QFileInfo(filename).path()); 374 settings.setValue("romsPath", QFileInfo(filename).path());
375 StoreRecentFile(filename); 375 StoreRecentFile(filename);
376 376
377 BootGame(filename.toLatin1().data()); 377 BootGame(filename.toLocal8Bit().data());
378 } 378 }
379} 379}
380 380
@@ -386,7 +386,7 @@ void GMainWindow::OnMenuLoadSymbolMap() {
386 if (!filename.isEmpty()) { 386 if (!filename.isEmpty()) {
387 settings.setValue("symbolsPath", QFileInfo(filename).path()); 387 settings.setValue("symbolsPath", QFileInfo(filename).path());
388 388
389 LoadSymbolMap(filename.toLatin1().data()); 389 LoadSymbolMap(filename.toLocal8Bit().data());
390 } 390 }
391} 391}
392 392
@@ -407,7 +407,7 @@ void GMainWindow::OnMenuRecentFile() {
407 QString filename = action->data().toString(); 407 QString filename = action->data().toString();
408 QFileInfo file_info(filename); 408 QFileInfo file_info(filename);
409 if (file_info.exists()) { 409 if (file_info.exists()) {
410 BootGame(filename.toLatin1().data()); 410 BootGame(filename.toLocal8Bit().data());
411 StoreRecentFile(filename); // Put the filename on top of the list 411 StoreRecentFile(filename); // Put the filename on top of the list
412 } else { 412 } else {
413 // Display an error message and remove the file from the list. 413 // Display an error message and remove the file from the list.
@@ -450,6 +450,10 @@ void GMainWindow::OnOpenHotkeysDialog() {
450 450
451void GMainWindow::SetHardwareRendererEnabled(bool enabled) { 451void GMainWindow::SetHardwareRendererEnabled(bool enabled) {
452 VideoCore::g_hw_renderer_enabled = enabled; 452 VideoCore::g_hw_renderer_enabled = enabled;
453
454 Config config;
455 Settings::values.use_hw_renderer = enabled;
456 config.Save();
453} 457}
454 458
455void GMainWindow::SetGdbstubEnabled(bool enabled) { 459void GMainWindow::SetGdbstubEnabled(bool enabled) {
@@ -458,6 +462,10 @@ void GMainWindow::SetGdbstubEnabled(bool enabled) {
458 462
459void GMainWindow::SetShaderJITEnabled(bool enabled) { 463void GMainWindow::SetShaderJITEnabled(bool enabled) {
460 VideoCore::g_shader_jit_enabled = enabled; 464 VideoCore::g_shader_jit_enabled = enabled;
465
466 Config config;
467 Settings::values.use_shader_jit = enabled;
468 config.Save();
461} 469}
462 470
463void GMainWindow::ToggleWindowMode() { 471void GMainWindow::ToggleWindowMode() {
@@ -469,6 +477,7 @@ void GMainWindow::ToggleWindowMode() {
469 if (emulation_running) { 477 if (emulation_running) {
470 render_window->setVisible(true); 478 render_window->setVisible(true);
471 render_window->setFocus(); 479 render_window->setFocus();
480 game_list->hide();
472 } 481 }
473 482
474 } else { 483 } else {
diff --git a/src/core/arm/skyeye_common/vfp/vfp.cpp b/src/core/arm/skyeye_common/vfp/vfp.cpp
index 0537135e2..a27a7e194 100644
--- a/src/core/arm/skyeye_common/vfp/vfp.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfp.cpp
@@ -113,26 +113,26 @@ void VMOVR(ARMul_State* state, u32 single, u32 d, u32 m)
113/* Miscellaneous functions */ 113/* Miscellaneous functions */
114s32 vfp_get_float(ARMul_State* state, unsigned int reg) 114s32 vfp_get_float(ARMul_State* state, unsigned int reg)
115{ 115{
116 LOG_TRACE(Core_ARM11, "VFP get float: s%d=[%08x]\n", reg, state->ExtReg[reg]); 116 LOG_TRACE(Core_ARM11, "VFP get float: s%d=[%08x]", reg, state->ExtReg[reg]);
117 return state->ExtReg[reg]; 117 return state->ExtReg[reg];
118} 118}
119 119
120void vfp_put_float(ARMul_State* state, s32 val, unsigned int reg) 120void vfp_put_float(ARMul_State* state, s32 val, unsigned int reg)
121{ 121{
122 LOG_TRACE(Core_ARM11, "VFP put float: s%d <= [%08x]\n", reg, val); 122 LOG_TRACE(Core_ARM11, "VFP put float: s%d <= [%08x]", reg, val);
123 state->ExtReg[reg] = val; 123 state->ExtReg[reg] = val;
124} 124}
125 125
126u64 vfp_get_double(ARMul_State* state, unsigned int reg) 126u64 vfp_get_double(ARMul_State* state, unsigned int reg)
127{ 127{
128 u64 result = ((u64) state->ExtReg[reg*2+1])<<32 | state->ExtReg[reg*2]; 128 u64 result = ((u64) state->ExtReg[reg*2+1])<<32 | state->ExtReg[reg*2];
129 LOG_TRACE(Core_ARM11, "VFP get double: s[%d-%d]=[%016llx]\n", reg * 2 + 1, reg * 2, result); 129 LOG_TRACE(Core_ARM11, "VFP get double: s[%d-%d]=[%016llx]", reg * 2 + 1, reg * 2, result);
130 return result; 130 return result;
131} 131}
132 132
133void vfp_put_double(ARMul_State* state, u64 val, unsigned int reg) 133void vfp_put_double(ARMul_State* state, u64 val, unsigned int reg)
134{ 134{
135 LOG_TRACE(Core_ARM11, "VFP put double: s[%d-%d] <= [%08x-%08x]\n", reg * 2 + 1, reg * 2, (u32)(val >> 32), (u32)(val & 0xffffffff)); 135 LOG_TRACE(Core_ARM11, "VFP put double: s[%d-%d] <= [%08x-%08x]", reg * 2 + 1, reg * 2, (u32)(val >> 32), (u32)(val & 0xffffffff));
136 state->ExtReg[reg*2] = (u32) (val & 0xffffffff); 136 state->ExtReg[reg*2] = (u32) (val & 0xffffffff);
137 state->ExtReg[reg*2+1] = (u32) (val>>32); 137 state->ExtReg[reg*2+1] = (u32) (val>>32);
138} 138}
@@ -142,10 +142,10 @@ void vfp_put_double(ARMul_State* state, u64 val, unsigned int reg)
142 */ 142 */
143void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr) 143void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr)
144{ 144{
145 LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x\n", exceptions); 145 LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x", exceptions);
146 146
147 if (exceptions == VFP_EXCEPTION_ERROR) { 147 if (exceptions == VFP_EXCEPTION_ERROR) {
148 LOG_CRITICAL(Core_ARM11, "unhandled bounce %x\n", inst); 148 LOG_CRITICAL(Core_ARM11, "unhandled bounce %x", inst);
149 Crash(); 149 Crash();
150 } 150 }
151 151
diff --git a/src/core/arm/skyeye_common/vfp/vfp.h b/src/core/arm/skyeye_common/vfp/vfp.h
index 88908da9f..60a63e6de 100644
--- a/src/core/arm/skyeye_common/vfp/vfp.h
+++ b/src/core/arm/skyeye_common/vfp/vfp.h
@@ -22,7 +22,7 @@
22 22
23#include "core/arm/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */ 23#include "core/arm/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
24 24
25#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested\n", __FUNCTION__); 25#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested", __FUNCTION__);
26#define CHECK_VFP_ENABLED 26#define CHECK_VFP_ENABLED
27#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]); 27#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]);
28 28
diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
index 857e6ce45..45914d479 100644
--- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
@@ -65,7 +65,7 @@ static struct vfp_double vfp_double_default_qnan = {
65 65
66static void vfp_double_dump(const char *str, struct vfp_double *d) 66static void vfp_double_dump(const char *str, struct vfp_double *d)
67{ 67{
68 LOG_TRACE(Core_ARM11, "VFP: %s: sign=%d exponent=%d significand=%016llx\n", 68 LOG_TRACE(Core_ARM11, "VFP: %s: sign=%d exponent=%d significand=%016llx",
69 str, d->sign != 0, d->exponent, d->significand); 69 str, d->sign != 0, d->exponent, d->significand);
70} 70}
71 71
@@ -155,7 +155,7 @@ u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd,
155 } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0)) 155 } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
156 incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1; 156 incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
157 157
158 LOG_TRACE(Core_ARM11, "VFP: rounding increment = 0x%08llx\n", incr); 158 LOG_TRACE(Core_ARM11, "VFP: rounding increment = 0x%08llx", incr);
159 159
160 /* 160 /*
161 * Is our rounding going to overflow? 161 * Is our rounding going to overflow?
@@ -210,7 +210,7 @@ pack:
210 vfp_double_dump("pack: final", vd); 210 vfp_double_dump("pack: final", vd);
211 { 211 {
212 s64 d = vfp_double_pack(vd); 212 s64 d = vfp_double_pack(vd);
213 LOG_TRACE(Core_ARM11, "VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func, 213 LOG_TRACE(Core_ARM11, "VFP: %s: d(d%d)=%016llx exceptions=%08x", func,
214 dd, d, exceptions); 214 dd, d, exceptions);
215 vfp_put_double(state, d, dd); 215 vfp_put_double(state, d, dd);
216 } 216 }
@@ -267,28 +267,28 @@ vfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn,
267 */ 267 */
268static u32 vfp_double_fabs(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 268static u32 vfp_double_fabs(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
269{ 269{
270 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 270 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
271 vfp_put_double(state, vfp_double_packed_abs(vfp_get_double(state, dm)), dd); 271 vfp_put_double(state, vfp_double_packed_abs(vfp_get_double(state, dm)), dd);
272 return 0; 272 return 0;
273} 273}
274 274
275static u32 vfp_double_fcpy(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 275static u32 vfp_double_fcpy(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
276{ 276{
277 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 277 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
278 vfp_put_double(state, vfp_get_double(state, dm), dd); 278 vfp_put_double(state, vfp_get_double(state, dm), dd);
279 return 0; 279 return 0;
280} 280}
281 281
282static u32 vfp_double_fneg(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 282static u32 vfp_double_fneg(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
283{ 283{
284 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 284 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
285 vfp_put_double(state, vfp_double_packed_negate(vfp_get_double(state, dm)), dd); 285 vfp_put_double(state, vfp_double_packed_negate(vfp_get_double(state, dm)), dd);
286 return 0; 286 return 0;
287} 287}
288 288
289static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 289static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
290{ 290{
291 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 291 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
292 vfp_double vdm, vdd, *vdp; 292 vfp_double vdm, vdd, *vdp;
293 int ret, tm; 293 int ret, tm;
294 294
@@ -383,7 +383,7 @@ static u32 vfp_compare(ARMul_State* state, int dd, int signal_on_qnan, int dm, u
383 s64 d, m; 383 s64 d, m;
384 u32 ret = 0; 384 u32 ret = 0;
385 385
386 LOG_TRACE(Core_ARM11, "In %s, state=0x%p, fpscr=0x%x\n", __FUNCTION__, state, fpscr); 386 LOG_TRACE(Core_ARM11, "In %s, state=0x%p, fpscr=0x%x", __FUNCTION__, state, fpscr);
387 m = vfp_get_double(state, dm); 387 m = vfp_get_double(state, dm);
388 if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) { 388 if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) {
389 ret |= FPSCR_CFLAG | FPSCR_VFLAG; 389 ret |= FPSCR_CFLAG | FPSCR_VFLAG;
@@ -438,32 +438,32 @@ static u32 vfp_compare(ARMul_State* state, int dd, int signal_on_qnan, int dm, u
438 ret |= FPSCR_CFLAG; 438 ret |= FPSCR_CFLAG;
439 } 439 }
440 } 440 }
441 LOG_TRACE(Core_ARM11, "In %s, state=0x%p, ret=0x%x\n", __FUNCTION__, state, ret); 441 LOG_TRACE(Core_ARM11, "In %s, state=0x%p, ret=0x%x", __FUNCTION__, state, ret);
442 442
443 return ret; 443 return ret;
444} 444}
445 445
446static u32 vfp_double_fcmp(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 446static u32 vfp_double_fcmp(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
447{ 447{
448 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 448 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
449 return vfp_compare(state, dd, 0, dm, fpscr); 449 return vfp_compare(state, dd, 0, dm, fpscr);
450} 450}
451 451
452static u32 vfp_double_fcmpe(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 452static u32 vfp_double_fcmpe(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
453{ 453{
454 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 454 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
455 return vfp_compare(state, dd, 1, dm, fpscr); 455 return vfp_compare(state, dd, 1, dm, fpscr);
456} 456}
457 457
458static u32 vfp_double_fcmpz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 458static u32 vfp_double_fcmpz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
459{ 459{
460 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 460 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
461 return vfp_compare(state, dd, 0, VFP_REG_ZERO, fpscr); 461 return vfp_compare(state, dd, 0, VFP_REG_ZERO, fpscr);
462} 462}
463 463
464static u32 vfp_double_fcmpez(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 464static u32 vfp_double_fcmpez(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
465{ 465{
466 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 466 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
467 return vfp_compare(state, dd, 1, VFP_REG_ZERO, fpscr); 467 return vfp_compare(state, dd, 1, VFP_REG_ZERO, fpscr);
468} 468}
469 469
@@ -474,7 +474,7 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32
474 int tm; 474 int tm;
475 u32 exceptions = 0; 475 u32 exceptions = 0;
476 476
477 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 477 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
478 vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); 478 vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
479 479
480 tm = vfp_double_type(&vdm); 480 tm = vfp_double_type(&vdm);
@@ -516,7 +516,7 @@ static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32
516 struct vfp_double vdm; 516 struct vfp_double vdm;
517 u32 m = vfp_get_float(state, dm); 517 u32 m = vfp_get_float(state, dm);
518 518
519 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 519 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
520 vdm.sign = 0; 520 vdm.sign = 0;
521 vdm.exponent = 1023 + 63 - 1; 521 vdm.exponent = 1023 + 63 - 1;
522 vdm.significand = (u64)m; 522 vdm.significand = (u64)m;
@@ -529,7 +529,7 @@ static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32
529 struct vfp_double vdm; 529 struct vfp_double vdm;
530 u32 m = vfp_get_float(state, dm); 530 u32 m = vfp_get_float(state, dm);
531 531
532 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 532 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
533 vdm.sign = (m & 0x80000000) >> 16; 533 vdm.sign = (m & 0x80000000) >> 16;
534 vdm.exponent = 1023 + 63 - 1; 534 vdm.exponent = 1023 + 63 - 1;
535 vdm.significand = vdm.sign ? (~m + 1) : m; 535 vdm.significand = vdm.sign ? (~m + 1) : m;
@@ -544,7 +544,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
544 int rmode = fpscr & FPSCR_RMODE_MASK; 544 int rmode = fpscr & FPSCR_RMODE_MASK;
545 int tm; 545 int tm;
546 546
547 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 547 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
548 vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); 548 vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
549 549
550 /* 550 /*
@@ -605,7 +605,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
605 } 605 }
606 } 606 }
607 607
608 LOG_TRACE(Core_ARM11, "VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 608 LOG_TRACE(Core_ARM11, "VFP: ftoui: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
609 609
610 vfp_put_float(state, d, sd); 610 vfp_put_float(state, d, sd);
611 611
@@ -614,7 +614,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
614 614
615static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr) 615static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
616{ 616{
617 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 617 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
618 return vfp_double_ftoui(state, sd, unused, dm, FPSCR_ROUND_TOZERO); 618 return vfp_double_ftoui(state, sd, unused, dm, FPSCR_ROUND_TOZERO);
619} 619}
620 620
@@ -625,7 +625,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
625 int rmode = fpscr & FPSCR_RMODE_MASK; 625 int rmode = fpscr & FPSCR_RMODE_MASK;
626 int tm; 626 int tm;
627 627
628 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 628 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
629 vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); 629 vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
630 vfp_double_dump("VDM", &vdm); 630 vfp_double_dump("VDM", &vdm);
631 631
@@ -682,7 +682,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
682 } 682 }
683 } 683 }
684 684
685 LOG_TRACE(Core_ARM11, "VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 685 LOG_TRACE(Core_ARM11, "VFP: ftosi: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
686 686
687 vfp_put_float(state, (s32)d, sd); 687 vfp_put_float(state, (s32)d, sd);
688 688
@@ -691,7 +691,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
691 691
692static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) 692static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
693{ 693{
694 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 694 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
695 return vfp_double_ftosi(state, dd, unused, dm, FPSCR_ROUND_TOZERO); 695 return vfp_double_ftosi(state, dd, unused, dm, FPSCR_ROUND_TOZERO);
696} 696}
697 697
@@ -775,7 +775,7 @@ u32 vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,struct vfp_dou
775 775
776 if (vdn->significand & (1ULL << 63) || 776 if (vdn->significand & (1ULL << 63) ||
777 vdm->significand & (1ULL << 63)) { 777 vdm->significand & (1ULL << 63)) {
778 LOG_INFO(Core_ARM11, "VFP: bad FP values in %s\n", __func__); 778 LOG_INFO(Core_ARM11, "VFP: bad FP values in %s", __func__);
779 vfp_double_dump("VDN", vdn); 779 vfp_double_dump("VDN", vdn);
780 vfp_double_dump("VDM", vdm); 780 vfp_double_dump("VDM", vdm);
781 } 781 }
@@ -843,7 +843,7 @@ vfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn,
843 */ 843 */
844 if (vdn->exponent < vdm->exponent) { 844 if (vdn->exponent < vdm->exponent) {
845 std::swap(vdm, vdn); 845 std::swap(vdm, vdn);
846 LOG_TRACE(Core_ARM11, "VFP: swapping M <-> N\n"); 846 LOG_TRACE(Core_ARM11, "VFP: swapping M <-> N");
847 } 847 }
848 848
849 vdd->sign = vdn->sign ^ vdm->sign; 849 vdd->sign = vdn->sign ^ vdm->sign;
@@ -927,7 +927,7 @@ vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 f
927 */ 927 */
928static u32 vfp_double_fmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr) 928static u32 vfp_double_fmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
929{ 929{
930 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 930 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
931 return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, 0, "fmac"); 931 return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, 0, "fmac");
932} 932}
933 933
@@ -936,7 +936,7 @@ static u32 vfp_double_fmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
936 */ 936 */
937static u32 vfp_double_fnmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr) 937static u32 vfp_double_fnmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
938{ 938{
939 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 939 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
940 return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac"); 940 return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac");
941} 941}
942 942
@@ -945,7 +945,7 @@ static u32 vfp_double_fnmac(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
945 */ 945 */
946static u32 vfp_double_fmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr) 946static u32 vfp_double_fmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
947{ 947{
948 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 948 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
949 return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc"); 949 return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc");
950} 950}
951 951
@@ -954,7 +954,7 @@ static u32 vfp_double_fmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
954 */ 954 */
955static u32 vfp_double_fnmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr) 955static u32 vfp_double_fnmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
956{ 956{
957 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 957 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
958 return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc"); 958 return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
959} 959}
960 960
@@ -966,7 +966,7 @@ static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
966 struct vfp_double vdd, vdn, vdm; 966 struct vfp_double vdd, vdn, vdm;
967 u32 exceptions; 967 u32 exceptions;
968 968
969 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 969 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
970 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); 970 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
971 if (vdn.exponent == 0 && vdn.significand) 971 if (vdn.exponent == 0 && vdn.significand)
972 vfp_double_normalise_denormal(&vdn); 972 vfp_double_normalise_denormal(&vdn);
@@ -987,7 +987,7 @@ static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
987 struct vfp_double vdd, vdn, vdm; 987 struct vfp_double vdd, vdn, vdm;
988 u32 exceptions; 988 u32 exceptions;
989 989
990 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 990 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
991 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); 991 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
992 if (vdn.exponent == 0 && vdn.significand) 992 if (vdn.exponent == 0 && vdn.significand)
993 vfp_double_normalise_denormal(&vdn); 993 vfp_double_normalise_denormal(&vdn);
@@ -1010,7 +1010,7 @@ static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
1010 struct vfp_double vdd, vdn, vdm; 1010 struct vfp_double vdd, vdn, vdm;
1011 u32 exceptions; 1011 u32 exceptions;
1012 1012
1013 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 1013 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
1014 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); 1014 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
1015 if (vdn.exponent == 0 && vdn.significand) 1015 if (vdn.exponent == 0 && vdn.significand)
1016 vfp_double_normalise_denormal(&vdn); 1016 vfp_double_normalise_denormal(&vdn);
@@ -1032,7 +1032,7 @@ static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
1032 struct vfp_double vdd, vdn, vdm; 1032 struct vfp_double vdd, vdn, vdm;
1033 u32 exceptions; 1033 u32 exceptions;
1034 1034
1035 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 1035 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
1036 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); 1036 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
1037 if (vdn.exponent == 0 && vdn.significand) 1037 if (vdn.exponent == 0 && vdn.significand)
1038 vfp_double_normalise_denormal(&vdn); 1038 vfp_double_normalise_denormal(&vdn);
@@ -1060,7 +1060,7 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
1060 u32 exceptions = 0; 1060 u32 exceptions = 0;
1061 int tm, tn; 1061 int tm, tn;
1062 1062
1063 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 1063 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
1064 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); 1064 vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
1065 vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); 1065 vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
1066 1066
@@ -1185,7 +1185,7 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
1185 unsigned int vecitr, veclen, vecstride; 1185 unsigned int vecitr, veclen, vecstride;
1186 struct op *fop; 1186 struct op *fop;
1187 1187
1188 LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__); 1188 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
1189 vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK)); 1189 vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK));
1190 1190
1191 fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)]; 1191 fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];
@@ -1216,7 +1216,7 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
1216 else 1216 else
1217 veclen = fpscr & FPSCR_LENGTH_MASK; 1217 veclen = fpscr & FPSCR_LENGTH_MASK;
1218 1218
1219 LOG_TRACE(Core_ARM11, "VFP: vecstride=%u veclen=%u\n", vecstride, 1219 LOG_TRACE(Core_ARM11, "VFP: vecstride=%u veclen=%u", vecstride,
1220 (veclen >> FPSCR_LENGTH_BIT) + 1); 1220 (veclen >> FPSCR_LENGTH_BIT) + 1);
1221 1221
1222 if (!fop->fn) { 1222 if (!fop->fn) {
@@ -1230,16 +1230,16 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
1230 1230
1231 type = (fop->flags & OP_SD) ? 's' : 'd'; 1231 type = (fop->flags & OP_SD) ? 's' : 'd';
1232 if (op == FOP_EXT) 1232 if (op == FOP_EXT)
1233 LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = op[%u] (d%u)\n", 1233 LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = op[%u] (d%u)",
1234 vecitr >> FPSCR_LENGTH_BIT, 1234 vecitr >> FPSCR_LENGTH_BIT,
1235 type, dest, dn, dm); 1235 type, dest, dn, dm);
1236 else 1236 else
1237 LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n", 1237 LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)",
1238 vecitr >> FPSCR_LENGTH_BIT, 1238 vecitr >> FPSCR_LENGTH_BIT,
1239 type, dest, dn, FOP_TO_IDX(op), dm); 1239 type, dest, dn, FOP_TO_IDX(op), dm);
1240 1240
1241 except = fop->fn(state, dest, dn, dm, fpscr); 1241 except = fop->fn(state, dest, dn, dm, fpscr);
1242 LOG_TRACE(Core_ARM11, "VFP: itr%d: exceptions=%08x\n", 1242 LOG_TRACE(Core_ARM11, "VFP: itr%d: exceptions=%08x",
1243 vecitr >> FPSCR_LENGTH_BIT, except); 1243 vecitr >> FPSCR_LENGTH_BIT, except);
1244 1244
1245 exceptions |= except; 1245 exceptions |= except;
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
index c6a1be79d..e7a59a1ed 100644
--- a/src/core/file_sys/archive_backend.h
+++ b/src/core/file_sys/archive_backend.h
@@ -131,6 +131,12 @@ public:
131 * @return Opened directory, or nullptr 131 * @return Opened directory, or nullptr
132 */ 132 */
133 virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; 133 virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
134
135 /**
136 * Get the free space
137 * @return The number of free bytes in the archive
138 */
139 virtual u64 GetFreeBytes() const = 0;
134}; 140};
135 141
136class ArchiveFactory : NonCopyable { 142class ArchiveFactory : NonCopyable {
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index e9ecd2b1c..0ba502200 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -74,6 +74,11 @@ std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) c
74 return std::move(directory); 74 return std::move(directory);
75} 75}
76 76
77u64 DiskArchive::GetFreeBytes() const {
78 // TODO: Stubbed to return 1GiB
79 return 1024 * 1024 * 1024;
80}
81
77//////////////////////////////////////////////////////////////////////////////////////////////////// 82////////////////////////////////////////////////////////////////////////////////////////////////////
78 83
79DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode) { 84DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode) {
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
index aaac65b17..ef9a98057 100644
--- a/src/core/file_sys/disk_archive.h
+++ b/src/core/file_sys/disk_archive.h
@@ -41,6 +41,7 @@ public:
41 bool CreateDirectory(const Path& path) const override; 41 bool CreateDirectory(const Path& path) const override;
42 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; 42 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
43 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; 43 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
44 u64 GetFreeBytes() const override;
44 45
45protected: 46protected:
46 friend class DiskFile; 47 friend class DiskFile;
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp
index 441ca9b53..2efc31a8c 100644
--- a/src/core/file_sys/ivfc_archive.cpp
+++ b/src/core/file_sys/ivfc_archive.cpp
@@ -59,6 +59,11 @@ std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) c
59 return Common::make_unique<IVFCDirectory>(); 59 return Common::make_unique<IVFCDirectory>();
60} 60}
61 61
62u64 IVFCArchive::GetFreeBytes() const {
63 LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive");
64 return 0;
65}
66
62//////////////////////////////////////////////////////////////////////////////////////////////////// 67////////////////////////////////////////////////////////////////////////////////////////////////////
63 68
64size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { 69size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h
index c15a6c4ae..f3fd82de4 100644
--- a/src/core/file_sys/ivfc_archive.h
+++ b/src/core/file_sys/ivfc_archive.h
@@ -42,6 +42,7 @@ public:
42 bool CreateDirectory(const Path& path) const override; 42 bool CreateDirectory(const Path& path) const override;
43 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; 43 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
44 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; 44 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
45 u64 GetFreeBytes() const override;
45 46
46protected: 47protected:
47 std::shared_ptr<FileUtil::IOFile> romfs_file; 48 std::shared_ptr<FileUtil::IOFile> romfs_file;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c10126513..00fa995f6 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -220,7 +220,7 @@ static void SwitchContext(Thread* new_thread) {
220 220
221 // Clean up the thread's wait_objects, they'll be restored if needed during 221 // Clean up the thread's wait_objects, they'll be restored if needed during
222 // the svcWaitSynchronization call 222 // the svcWaitSynchronization call
223 for (int i = 0; i < new_thread->wait_objects.size(); ++i) { 223 for (size_t i = 0; i < new_thread->wait_objects.size(); ++i) {
224 SharedPtr<WaitObject> object = new_thread->wait_objects[i]; 224 SharedPtr<WaitObject> object = new_thread->wait_objects[i];
225 object->RemoveWaitingThread(new_thread); 225 object->RemoveWaitingThread(new_thread);
226 } 226 }
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 6a1d961ac..ce2877f57 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.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/hle/hle.h" 5#include "core/hle/hle.h"
6#include "core/hle/kernel/mutex.h"
7#include "core/hle/kernel/shared_memory.h"
6#include "core/hle/service/csnd_snd.h" 8#include "core/hle/service/csnd_snd.h"
7 9
8//////////////////////////////////////////////////////////////////////////////////////////////////// 10////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,11 +13,11 @@
11namespace CSND_SND { 13namespace CSND_SND {
12 14
13const Interface::FunctionInfo FunctionTable[] = { 15const Interface::FunctionInfo FunctionTable[] = {
14 {0x00010140, nullptr, "Initialize"}, 16 {0x00010140, Initialize, "Initialize"},
15 {0x00020000, nullptr, "Shutdown"}, 17 {0x00020000, Shutdown, "Shutdown"},
16 {0x00030040, nullptr, "ExecuteType0Commands"}, 18 {0x00030040, ExecuteType0Commands, "ExecuteType0Commands"},
17 {0x00040080, nullptr, "ExecuteType1Commands"}, 19 {0x00040080, nullptr, "ExecuteType1Commands"},
18 {0x00050000, nullptr, "AcquireSoundChannels"}, 20 {0x00050000, AcquireSoundChannels, "AcquireSoundChannels"},
19 {0x00060000, nullptr, "ReleaseSoundChannels"}, 21 {0x00060000, nullptr, "ReleaseSoundChannels"},
20 {0x00070000, nullptr, "AcquireCaptureDevice"}, 22 {0x00070000, nullptr, "AcquireCaptureDevice"},
21 {0x00080040, nullptr, "ReleaseCaptureDevice"}, 23 {0x00080040, nullptr, "ReleaseCaptureDevice"},
@@ -31,4 +33,51 @@ Interface::Interface() {
31 Register(FunctionTable); 33 Register(FunctionTable);
32} 34}
33 35
36static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
37static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr;
38
39void Initialize(Service::Interface* self) {
40 u32* cmd_buff = Kernel::GetCommandBuffer();
41
42 shared_memory = Kernel::SharedMemory::Create(cmd_buff[1],
43 Kernel::MemoryPermission::ReadWrite,
44 Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem");
45
46 mutex = Kernel::Mutex::Create(false);
47
48 cmd_buff[1] = 0;
49 cmd_buff[2] = 0x4000000;
50 cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom();
51 cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom();
52}
53
54void ExecuteType0Commands(Service::Interface* self) {
55 u32* cmd_buff = Kernel::GetCommandBuffer();
56
57 if (shared_memory != nullptr) {
58 struct Type0Command* command = reinterpret_cast<struct Type0Command*>(
59 shared_memory->GetPointer(cmd_buff[1]));
60 if (command == nullptr) {
61 cmd_buff[1] = 1;
62 }else{
63 LOG_WARNING(Service, "(STUBBED) CSND_SND::ExecuteType0Commands");
64 command->finished |= 1;
65 cmd_buff[1] = 0;
66 }
67 }else{
68 cmd_buff[1] = 1;
69 }
70}
71
72void AcquireSoundChannels(Service::Interface* self) {
73 u32* cmd_buff = Kernel::GetCommandBuffer();
74 cmd_buff[1] = 0;
75 cmd_buff[2] = 0xFFFFFF00;
76}
77
78void Shutdown(Service::Interface* self) {
79 shared_memory = nullptr;
80 mutex = nullptr;
81}
82
34} // namespace 83} // namespace
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h
index a84752473..e861f3327 100644
--- a/src/core/hle/service/csnd_snd.h
+++ b/src/core/hle/service/csnd_snd.h
@@ -20,4 +20,17 @@ public:
20 } 20 }
21}; 21};
22 22
23struct Type0Command {
24 // command id and next command offset
25 u32 command_id;
26 u32 finished;
27 u32 flags;
28 u8 parameters[20];
29};
30
31void Initialize(Service::Interface* self);
32void ExecuteType0Commands(Service::Interface* self);
33void AcquireSoundChannels(Service::Interface* self);
34void Shutdown(Service::Interface* self);
35
23} // namespace 36} // namespace
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 6c0df67c3..d64b3656a 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -403,6 +403,13 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
403 return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory)); 403 return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory));
404} 404}
405 405
406ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
407 ArchiveBackend* archive = GetArchive(archive_handle);
408 if (archive == nullptr)
409 return ERR_INVALID_HANDLE;
410 return MakeResult<u64>(archive->GetFreeBytes());
411}
412
406ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) { 413ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) {
407 auto archive_itr = id_code_map.find(id_code); 414 auto archive_itr = id_code_map.find(id_code);
408 if (archive_itr == id_code_map.end()) { 415 if (archive_itr == id_code_map.end()) {
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 6f7048710..952deb4d4 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -167,6 +167,13 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
167 const FileSys::Path& path); 167 const FileSys::Path& path);
168 168
169/** 169/**
170 * Get the free space in an Archive
171 * @param archive_handle Handle to an open Archive object
172 * @return The number of free bytes in the archive
173 */
174ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle);
175
176/**
170 * Erases the contents of the physical folder that contains the archive 177 * Erases the contents of the physical folder that contains the archive
171 * identified by the specified id code and path 178 * identified by the specified id code and path
172 * @param id_code The id of the archive to format 179 * @param id_code The id of the archive to format
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index ae52083f9..b3fa89302 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -497,6 +497,33 @@ static void FormatThisUserSaveData(Service::Interface* self) {
497} 497}
498 498
499/** 499/**
500 * FS_User::GetFreeBytes service function
501 * Inputs:
502 * 0: 0x08120080
503 * 1: Archive handle low word
504 * 2: Archive handle high word
505 * Outputs:
506 * 1: Result of function, 0 on success, otherwise error code
507 * 2: Free byte count low word
508 * 3: Free byte count high word
509 */
510static void GetFreeBytes(Service::Interface* self) {
511 u32* cmd_buff = Kernel::GetCommandBuffer();
512
513 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
514 ResultVal<u64> bytes_res = GetFreeBytesInArchive(archive_handle);
515
516 cmd_buff[1] = bytes_res.Code().raw;
517 if (bytes_res.Succeeded()) {
518 cmd_buff[2] = (u32)*bytes_res;
519 cmd_buff[3] = *bytes_res >> 32;
520 } else {
521 cmd_buff[2] = 0;
522 cmd_buff[3] = 0;
523 }
524}
525
526/**
500 * FS_User::CreateExtSaveData service function 527 * FS_User::CreateExtSaveData service function
501 * Inputs: 528 * Inputs:
502 * 0 : 0x08510242 529 * 0 : 0x08510242
@@ -700,7 +727,7 @@ const Interface::FunctionInfo FunctionTable[] = {
700 {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"}, 727 {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
701 {0x08100200, nullptr, "CreateSystemSaveData"}, 728 {0x08100200, nullptr, "CreateSystemSaveData"},
702 {0x08110040, nullptr, "DeleteSystemSaveData"}, 729 {0x08110040, nullptr, "DeleteSystemSaveData"},
703 {0x08120080, nullptr, "GetFreeBytes"}, 730 {0x08120080, GetFreeBytes, "GetFreeBytes"},
704 {0x08130000, nullptr, "GetCardType"}, 731 {0x08130000, nullptr, "GetCardType"},
705 {0x08140000, nullptr, "GetSdmcArchiveResource"}, 732 {0x08140000, nullptr, "GetSdmcArchiveResource"},
706 {0x08150000, nullptr, "GetNandArchiveResource"}, 733 {0x08150000, nullptr, "GetNandArchiveResource"},
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 2c7d49c9f..22c1093ff 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -68,6 +68,18 @@ void GetBatteryChargeState(Service::Interface* self) {
68 LOG_WARNING(Service_PTM, "(STUBBED) called"); 68 LOG_WARNING(Service_PTM, "(STUBBED) called");
69} 69}
70 70
71void GetTotalStepCount(Service::Interface* self) {
72 u32* cmd_buff = Kernel::GetCommandBuffer();
73
74 // TODO: This function is only a stub,
75 // it returns 0 as the total step count
76
77 cmd_buff[1] = RESULT_SUCCESS.raw;
78 cmd_buff[2] = 0;
79
80 LOG_WARNING(Service_PTM, "(STUBBED) called");
81}
82
71void IsLegacyPowerOff(Service::Interface* self) { 83void IsLegacyPowerOff(Service::Interface* self) {
72 u32* cmd_buff = Kernel::GetCommandBuffer(); 84 u32* cmd_buff = Kernel::GetCommandBuffer();
73 85
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
index b690003cb..f2e76441f 100644
--- a/src/core/hle/service/ptm/ptm.h
+++ b/src/core/hle/service/ptm/ptm.h
@@ -72,6 +72,14 @@ void GetBatteryLevel(Interface* self);
72void GetBatteryChargeState(Interface* self); 72void GetBatteryChargeState(Interface* self);
73 73
74/** 74/**
75 * PTM::GetTotalStepCount service function
76 * Outputs:
77 * 1 : Result of function, 0 on success, otherwise error code
78 * 2 : Output of function, * = total step count
79 */
80void GetTotalStepCount(Interface* self);
81
82/**
75 * PTM::IsLegacyPowerOff service function 83 * PTM::IsLegacyPowerOff service function
76 * Outputs: 84 * Outputs:
77 * 1: Result code, 0 on success, otherwise error code 85 * 1: Result code, 0 on success, otherwise error code
diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp
index 3f5e9c7c1..09dc38c3e 100644
--- a/src/core/hle/service/ptm/ptm_u.cpp
+++ b/src/core/hle/service/ptm/ptm_u.cpp
@@ -23,7 +23,7 @@ const Interface::FunctionInfo FunctionTable[] = {
23 {0x00090000, nullptr, "GetPedometerState"}, 23 {0x00090000, nullptr, "GetPedometerState"},
24 {0x000A0042, nullptr, "GetStepHistoryEntry"}, 24 {0x000A0042, nullptr, "GetStepHistoryEntry"},
25 {0x000B00C2, nullptr, "GetStepHistory"}, 25 {0x000B00C2, nullptr, "GetStepHistory"},
26 {0x000C0000, nullptr, "GetTotalStepCount"}, 26 {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
27 {0x000D0040, nullptr, "SetPedometerRecordingMode"}, 27 {0x000D0040, nullptr, "SetPedometerRecordingMode"},
28 {0x000E0000, nullptr, "GetPedometerRecordingMode"}, 28 {0x000E0000, nullptr, "GetPedometerRecordingMode"},
29 {0x000F0084, nullptr, "GetStepHistoryAll"}, 29 {0x000F0084, nullptr, "GetStepHistoryAll"},
diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp
index 15f96ced8..48c45564f 100644
--- a/src/core/hw/y2r.cpp
+++ b/src/core/hw/y2r.cpp
@@ -324,7 +324,7 @@ void PerformConversion(ConversionConfiguration& cvt) {
324 324
325 u32* output_buffer = reinterpret_cast<u32*>(data_buffer.get()); 325 u32* output_buffer = reinterpret_cast<u32*>(data_buffer.get());
326 326
327 for (int i = 0; i < num_tiles; ++i) { 327 for (size_t i = 0; i < num_tiles; ++i) {
328 int image_strip_width = 0; 328 int image_strip_width = 0;
329 int output_stride = 0; 329 int output_stride = 0;
330 330
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 111b6a409..8eed6a50a 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -181,14 +181,14 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
181 181
182 for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) { 182 for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
183 const auto& table = reloc_table[current_inprogress]; 183 const auto& table = reloc_table[current_inprogress];
184 LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", current_segment_reloc_table, 184 LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)", current_segment_reloc_table,
185 (u32)table.skip, (u32)table.patch); 185 (u32)table.skip, (u32)table.patch);
186 pos += table.skip; 186 pos += table.skip;
187 s32 num_patches = table.patch; 187 s32 num_patches = table.patch;
188 while (0 < num_patches && pos < end_pos) { 188 while (0 < num_patches && pos < end_pos) {
189 u32 in_addr = (u8*)pos - program_image.data(); 189 u32 in_addr = (u8*)pos - program_image.data();
190 u32 addr = TranslateAddr(*pos, &loadinfo, offsets); 190 u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
191 LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n", 191 LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)",
192 base_addr + in_addr, addr, current_segment_reloc_table, *pos); 192 base_addr + in_addr, addr, current_segment_reloc_table, *pos);
193 switch (current_segment_reloc_table) { 193 switch (current_segment_reloc_table) {
194 case 0: 194 case 0:
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 8de95dacf..a7f2715ba 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -71,6 +71,7 @@ enum class ResultStatus {
71 ErrorNotUsed, 71 ErrorNotUsed,
72 ErrorAlreadyLoaded, 72 ErrorAlreadyLoaded,
73 ErrorMemoryAllocationFailed, 73 ErrorMemoryAllocationFailed,
74 ErrorEncrypted,
74}; 75};
75 76
76static inline u32 MakeMagic(char a, char b, char c, char d) { 77static inline u32 MakeMagic(char a, char b, char c, char d) {
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 094d74100..68b3f546e 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -128,9 +128,8 @@ ResultStatus AppLoader_NCCH::LoadExec() {
128 if (ResultStatus::Success == ReadCode(code)) { 128 if (ResultStatus::Success == ReadCode(code)) {
129 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( 129 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
130 (const char*)exheader_header.codeset_info.name, 8); 130 (const char*)exheader_header.codeset_info.name, 8);
131 u64 program_id = *reinterpret_cast<u64_le const*>(&ncch_header.program_id[0]);
132 131
133 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id); 132 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, ncch_header.program_id);
134 133
135 codeset->code.offset = 0; 134 codeset->code.offset = 0;
136 codeset->code.addr = exheader_header.codeset_info.text.address; 135 codeset->code.addr = exheader_header.codeset_info.text.address;
@@ -266,6 +265,11 @@ ResultStatus AppLoader_NCCH::Load() {
266 LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority); 265 LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority);
267 LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category); 266 LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category);
268 267
268 if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
269 LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
270 return ResultStatus::ErrorEncrypted;
271 }
272
269 // Read ExeFS... 273 // Read ExeFS...
270 274
271 exefs_offset = ncch_header.exefs_offset * kBlockSize; 275 exefs_offset = ncch_header.exefs_offset * kBlockSize;
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index d875e4cf3..ca6772a78 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -17,31 +17,31 @@
17 17
18struct NCCH_Header { 18struct NCCH_Header {
19 u8 signature[0x100]; 19 u8 signature[0x100];
20 u32 magic; 20 u32_le magic;
21 u32 content_size; 21 u32_le content_size;
22 u8 partition_id[8]; 22 u8 partition_id[8];
23 u16 maker_code; 23 u16_le maker_code;
24 u16 version; 24 u16_le version;
25 u8 reserved_0[4]; 25 u8 reserved_0[4];
26 u8 program_id[8]; 26 u64_le program_id;
27 u8 reserved_1[0x10]; 27 u8 reserved_1[0x10];
28 u8 logo_region_hash[0x20]; 28 u8 logo_region_hash[0x20];
29 u8 product_code[0x10]; 29 u8 product_code[0x10];
30 u8 extended_header_hash[0x20]; 30 u8 extended_header_hash[0x20];
31 u32 extended_header_size; 31 u32_le extended_header_size;
32 u8 reserved_2[4]; 32 u8 reserved_2[4];
33 u8 flags[8]; 33 u8 flags[8];
34 u32 plain_region_offset; 34 u32_le plain_region_offset;
35 u32 plain_region_size; 35 u32_le plain_region_size;
36 u32 logo_region_offset; 36 u32_le logo_region_offset;
37 u32 logo_region_size; 37 u32_le logo_region_size;
38 u32 exefs_offset; 38 u32_le exefs_offset;
39 u32 exefs_size; 39 u32_le exefs_size;
40 u32 exefs_hash_region_size; 40 u32_le exefs_hash_region_size;
41 u8 reserved_3[4]; 41 u8 reserved_3[4];
42 u32 romfs_offset; 42 u32_le romfs_offset;
43 u32 romfs_size; 43 u32_le romfs_size;
44 u32 romfs_hash_region_size; 44 u32_le romfs_hash_region_size;
45 u8 reserved_4[4]; 45 u8 reserved_4[4];
46 u8 exefs_super_block_hash[0x20]; 46 u8 exefs_super_block_hash[0x20];
47 u8 romfs_super_block_hash[0x20]; 47 u8 romfs_super_block_hash[0x20];
@@ -109,8 +109,8 @@ struct ExHeader_StorageInfo {
109}; 109};
110 110
111struct ExHeader_ARM11_SystemLocalCaps { 111struct ExHeader_ARM11_SystemLocalCaps {
112 u8 program_id[8]; 112 u64_le program_id;
113 u32 core_version; 113 u32_le core_version;
114 u8 reserved_flags[2]; 114 u8 reserved_flags[2];
115 union { 115 union {
116 u8 flags0; 116 u8 flags0;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index b80795e0c..fc79c3ee9 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -26,9 +26,9 @@ enum class PageType {
26}; 26};
27 27
28/** 28/**
29 * A (reasonably) fast way of allowing switchable and remmapable process address spaces. It loosely 29 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
30 * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and 30 * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
31 * fetching requirements when acessing. In the usual case of an access to regular memory, it only 31 * fetching requirements when accessing. In the usual case of an access to regular memory, it only
32 * requires an indexed fetch and a check for NULL. 32 * requires an indexed fetch and a check for NULL.
33 */ 33 */
34struct PageTable { 34struct PageTable {
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 8c9d76ab4..2a924f4ad 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,6 +1,7 @@
1set(SRCS 1set(SRCS
2 renderer_opengl/gl_rasterizer.cpp 2 renderer_opengl/gl_rasterizer.cpp
3 renderer_opengl/gl_rasterizer_cache.cpp 3 renderer_opengl/gl_rasterizer_cache.cpp
4 renderer_opengl/gl_shader_gen.cpp
4 renderer_opengl/gl_shader_util.cpp 5 renderer_opengl/gl_shader_util.cpp
5 renderer_opengl/gl_state.cpp 6 renderer_opengl/gl_state.cpp
6 renderer_opengl/renderer_opengl.cpp 7 renderer_opengl/renderer_opengl.cpp
@@ -21,8 +22,8 @@ set(HEADERS
21 renderer_opengl/gl_rasterizer.h 22 renderer_opengl/gl_rasterizer.h
22 renderer_opengl/gl_rasterizer_cache.h 23 renderer_opengl/gl_rasterizer_cache.h
23 renderer_opengl/gl_resource_manager.h 24 renderer_opengl/gl_resource_manager.h
25 renderer_opengl/gl_shader_gen.h
24 renderer_opengl/gl_shader_util.h 26 renderer_opengl/gl_shader_util.h
25 renderer_opengl/gl_shaders.h
26 renderer_opengl/gl_state.h 27 renderer_opengl/gl_state.h
27 renderer_opengl/pica_to_gl.h 28 renderer_opengl/pica_to_gl.h
28 renderer_opengl/renderer_opengl.h 29 renderer_opengl/renderer_opengl.h
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 47afd8938..bd1b09a4b 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -157,6 +157,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
157 157
158 // TODO: What happens if a loader overwrites a previous one's data? 158 // TODO: What happens if a loader overwrites a previous one's data?
159 for (unsigned component = 0; component < loader_config.component_count; ++component) { 159 for (unsigned component = 0; component < loader_config.component_count; ++component) {
160 if (component >= 12)
161 LOG_ERROR(HW_GPU, "Overflow in the vertex attribute loader %u trying to load component %u", loader, component);
160 u32 attribute_index = loader_config.GetComponent(component); 162 u32 attribute_index = loader_config.GetComponent(component);
161 vertex_attribute_sources[attribute_index] = load_address; 163 vertex_attribute_sources[attribute_index] = load_address;
162 vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count); 164 vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index aa1f1484c..f1cfa9361 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -641,7 +641,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
641 // Initialize write structure 641 // Initialize write structure
642 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 642 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
643 if (png_ptr == nullptr) { 643 if (png_ptr == nullptr) {
644 LOG_ERROR(Debug_GPU, "Could not allocate write struct\n"); 644 LOG_ERROR(Debug_GPU, "Could not allocate write struct");
645 goto finalise; 645 goto finalise;
646 646
647 } 647 }
@@ -649,13 +649,13 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
649 // Initialize info structure 649 // Initialize info structure
650 info_ptr = png_create_info_struct(png_ptr); 650 info_ptr = png_create_info_struct(png_ptr);
651 if (info_ptr == nullptr) { 651 if (info_ptr == nullptr) {
652 LOG_ERROR(Debug_GPU, "Could not allocate info struct\n"); 652 LOG_ERROR(Debug_GPU, "Could not allocate info struct");
653 goto finalise; 653 goto finalise;
654 } 654 }
655 655
656 // Setup Exception handling 656 // Setup Exception handling
657 if (setjmp(png_jmpbuf(png_ptr))) { 657 if (setjmp(png_jmpbuf(png_ptr))) {
658 LOG_ERROR(Debug_GPU, "Error during png creation\n"); 658 LOG_ERROR(Debug_GPU, "Error during png creation");
659 goto finalise; 659 goto finalise;
660 } 660 }
661 661
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index ff81b409d..2f1b2dec4 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -317,6 +317,7 @@ struct Regs {
317 }; 317 };
318 318
319 union { 319 union {
320 u32 sources_raw;
320 BitField< 0, 4, Source> color_source1; 321 BitField< 0, 4, Source> color_source1;
321 BitField< 4, 4, Source> color_source2; 322 BitField< 4, 4, Source> color_source2;
322 BitField< 8, 4, Source> color_source3; 323 BitField< 8, 4, Source> color_source3;
@@ -326,6 +327,7 @@ struct Regs {
326 }; 327 };
327 328
328 union { 329 union {
330 u32 modifiers_raw;
329 BitField< 0, 4, ColorModifier> color_modifier1; 331 BitField< 0, 4, ColorModifier> color_modifier1;
330 BitField< 4, 4, ColorModifier> color_modifier2; 332 BitField< 4, 4, ColorModifier> color_modifier2;
331 BitField< 8, 4, ColorModifier> color_modifier3; 333 BitField< 8, 4, ColorModifier> color_modifier3;
@@ -335,6 +337,7 @@ struct Regs {
335 }; 337 };
336 338
337 union { 339 union {
340 u32 ops_raw;
338 BitField< 0, 4, Operation> color_op; 341 BitField< 0, 4, Operation> color_op;
339 BitField<16, 4, Operation> alpha_op; 342 BitField<16, 4, Operation> alpha_op;
340 }; 343 };
@@ -348,6 +351,7 @@ struct Regs {
348 }; 351 };
349 352
350 union { 353 union {
354 u32 scales_raw;
351 BitField< 0, 2, u32> color_scale; 355 BitField< 0, 2, u32> color_scale;
352 BitField<16, 2, u32> alpha_scale; 356 BitField<16, 2, u32> alpha_scale;
353 }; 357 };
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index 7abf60292..226fad783 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -462,7 +462,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
462 } 462 }
463 463
464 default: 464 default:
465 LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x\n", (int)mode); 465 LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x", (int)mode);
466 UNIMPLEMENTED(); 466 UNIMPLEMENTED();
467 return 0; 467 return 0;
468 } 468 }
@@ -541,7 +541,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
541 return combiner_output; 541 return combiner_output;
542 542
543 default: 543 default:
544 LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); 544 LOG_ERROR(HW_GPU, "Unknown color combiner source %d", (int)source);
545 UNIMPLEMENTED(); 545 UNIMPLEMENTED();
546 return {0, 0, 0, 0}; 546 return {0, 0, 0, 0};
547 } 547 }
@@ -679,7 +679,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
679 return { (u8)result, (u8)result, (u8)result }; 679 return { (u8)result, (u8)result, (u8)result };
680 } 680 }
681 default: 681 default:
682 LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); 682 LOG_ERROR(HW_GPU, "Unknown color combiner operation %d", (int)op);
683 UNIMPLEMENTED(); 683 UNIMPLEMENTED();
684 return {0, 0, 0}; 684 return {0, 0, 0};
685 } 685 }
@@ -716,7 +716,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
716 return (std::min(255, (input[0] + input[1])) * input[2]) / 255; 716 return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
717 717
718 default: 718 default:
719 LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op); 719 LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d", (int)op);
720 UNIMPLEMENTED(); 720 UNIMPLEMENTED();
721 return 0; 721 return 0;
722 } 722 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 50eb157a5..d1def2f3b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -8,6 +8,8 @@
8#include <glad/glad.h> 8#include <glad/glad.h>
9 9
10#include "common/color.h" 10#include "common/color.h"
11#include "common/file_util.h"
12#include "common/make_unique.h"
11#include "common/math_util.h" 13#include "common/math_util.h"
12#include "common/microprofile.h" 14#include "common/microprofile.h"
13#include "common/profiler.h" 15#include "common/profiler.h"
@@ -19,7 +21,7 @@
19#include "video_core/pica.h" 21#include "video_core/pica.h"
20#include "video_core/utils.h" 22#include "video_core/utils.h"
21#include "video_core/renderer_opengl/gl_rasterizer.h" 23#include "video_core/renderer_opengl/gl_rasterizer.h"
22#include "video_core/renderer_opengl/gl_shaders.h" 24#include "video_core/renderer_opengl/gl_shader_gen.h"
23#include "video_core/renderer_opengl/gl_shader_util.h" 25#include "video_core/renderer_opengl/gl_shader_util.h"
24#include "video_core/renderer_opengl/pica_to_gl.h" 26#include "video_core/renderer_opengl/pica_to_gl.h"
25 27
@@ -38,38 +40,8 @@ RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr
38RasterizerOpenGL::~RasterizerOpenGL() { } 40RasterizerOpenGL::~RasterizerOpenGL() { }
39 41
40void RasterizerOpenGL::InitObjects() { 42void RasterizerOpenGL::InitObjects() {
41 // Create the hardware shader program and get attrib/uniform locations
42 shader.Create(GLShaders::g_vertex_shader_hw, GLShaders::g_fragment_shader_hw);
43 attrib_position = glGetAttribLocation(shader.handle, "vert_position");
44 attrib_color = glGetAttribLocation(shader.handle, "vert_color");
45 attrib_texcoords = glGetAttribLocation(shader.handle, "vert_texcoords");
46
47 uniform_alphatest_enabled = glGetUniformLocation(shader.handle, "alphatest_enabled");
48 uniform_alphatest_func = glGetUniformLocation(shader.handle, "alphatest_func");
49 uniform_alphatest_ref = glGetUniformLocation(shader.handle, "alphatest_ref");
50
51 uniform_tex = glGetUniformLocation(shader.handle, "tex");
52
53 uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.handle, "tev_combiner_buffer_color");
54
55 const auto tev_stages = Pica::g_state.regs.GetTevStages();
56 for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
57 auto& uniform_tev_cfg = uniform_tev_cfgs[tev_stage_index];
58
59 std::string tev_ref_str = "tev_cfgs[" + std::to_string(tev_stage_index) + "]";
60 uniform_tev_cfg.enabled = glGetUniformLocation(shader.handle, (tev_ref_str + ".enabled").c_str());
61 uniform_tev_cfg.color_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_sources").c_str());
62 uniform_tev_cfg.alpha_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_sources").c_str());
63 uniform_tev_cfg.color_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_modifiers").c_str());
64 uniform_tev_cfg.alpha_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_modifiers").c_str());
65 uniform_tev_cfg.color_alpha_op = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_op").c_str());
66 uniform_tev_cfg.color_alpha_multiplier = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_multiplier").c_str());
67 uniform_tev_cfg.const_color = glGetUniformLocation(shader.handle, (tev_ref_str + ".const_color").c_str());
68 uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str());
69 }
70
71 // Create sampler objects 43 // Create sampler objects
72 for (int i = 0; i < texture_samplers.size(); ++i) { 44 for (size_t i = 0; i < texture_samplers.size(); ++i) {
73 texture_samplers[i].Create(); 45 texture_samplers[i].Create();
74 state.texture_units[i].sampler = texture_samplers[i].sampler.handle; 46 state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
75 } 47 }
@@ -78,29 +50,25 @@ void RasterizerOpenGL::InitObjects() {
78 vertex_buffer.Create(); 50 vertex_buffer.Create();
79 vertex_array.Create(); 51 vertex_array.Create();
80 52
81 // Update OpenGL state
82 state.draw.vertex_array = vertex_array.handle; 53 state.draw.vertex_array = vertex_array.handle;
83 state.draw.vertex_buffer = vertex_buffer.handle; 54 state.draw.vertex_buffer = vertex_buffer.handle;
84 state.draw.shader_program = shader.handle;
85
86 state.Apply(); 55 state.Apply();
87 56
88 // Set the texture samplers to correspond to different texture units
89 glUniform1i(uniform_tex, 0);
90 glUniform1i(uniform_tex + 1, 1);
91 glUniform1i(uniform_tex + 2, 2);
92
93 // Set vertex attributes 57 // Set vertex attributes
94 glVertexAttribPointer(attrib_position, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); 58 glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
95 glVertexAttribPointer(attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); 59 glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION);
96 glVertexAttribPointer(attrib_texcoords, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); 60
97 glVertexAttribPointer(attrib_texcoords + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); 61 glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
98 glVertexAttribPointer(attrib_texcoords + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); 62 glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR);
99 glEnableVertexAttribArray(attrib_position); 63
100 glEnableVertexAttribArray(attrib_color); 64 glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
101 glEnableVertexAttribArray(attrib_texcoords); 65 glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
102 glEnableVertexAttribArray(attrib_texcoords + 1); 66 glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
103 glEnableVertexAttribArray(attrib_texcoords + 2); 67 glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD0);
68 glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1);
69 glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2);
70
71 SetShader();
104 72
105 // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation 73 // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
106 fb_color_texture.texture.Create(); 74 fb_color_texture.texture.Create();
@@ -150,61 +118,15 @@ void RasterizerOpenGL::InitObjects() {
150} 118}
151 119
152void RasterizerOpenGL::Reset() { 120void RasterizerOpenGL::Reset() {
153 const auto& regs = Pica::g_state.regs;
154
155 SyncCullMode(); 121 SyncCullMode();
156 SyncBlendEnabled(); 122 SyncBlendEnabled();
157 SyncBlendFuncs(); 123 SyncBlendFuncs();
158 SyncBlendColor(); 124 SyncBlendColor();
159 SyncAlphaTest();
160 SyncLogicOp(); 125 SyncLogicOp();
161 SyncStencilTest(); 126 SyncStencilTest();
162 SyncDepthTest(); 127 SyncDepthTest();
163 128
164 // TEV stage 0 129 SetShader();
165 SyncTevSources(0, regs.tev_stage0);
166 SyncTevModifiers(0, regs.tev_stage0);
167 SyncTevOps(0, regs.tev_stage0);
168 SyncTevColor(0, regs.tev_stage0);
169 SyncTevMultipliers(0, regs.tev_stage0);
170
171 // TEV stage 1
172 SyncTevSources(1, regs.tev_stage1);
173 SyncTevModifiers(1, regs.tev_stage1);
174 SyncTevOps(1, regs.tev_stage1);
175 SyncTevColor(1, regs.tev_stage1);
176 SyncTevMultipliers(1, regs.tev_stage1);
177
178 // TEV stage 2
179 SyncTevSources(2, regs.tev_stage2);
180 SyncTevModifiers(2, regs.tev_stage2);
181 SyncTevOps(2, regs.tev_stage2);
182 SyncTevColor(2, regs.tev_stage2);
183 SyncTevMultipliers(2, regs.tev_stage2);
184
185 // TEV stage 3
186 SyncTevSources(3, regs.tev_stage3);
187 SyncTevModifiers(3, regs.tev_stage3);
188 SyncTevOps(3, regs.tev_stage3);
189 SyncTevColor(3, regs.tev_stage3);
190 SyncTevMultipliers(3, regs.tev_stage3);
191
192 // TEV stage 4
193 SyncTevSources(4, regs.tev_stage4);
194 SyncTevModifiers(4, regs.tev_stage4);
195 SyncTevOps(4, regs.tev_stage4);
196 SyncTevColor(4, regs.tev_stage4);
197 SyncTevMultipliers(4, regs.tev_stage4);
198
199 // TEV stage 5
200 SyncTevSources(5, regs.tev_stage5);
201 SyncTevModifiers(5, regs.tev_stage5);
202 SyncTevOps(5, regs.tev_stage5);
203 SyncTevColor(5, regs.tev_stage5);
204 SyncTevMultipliers(5, regs.tev_stage5);
205
206 SyncCombinerColor();
207 SyncCombinerWriteFlags();
208 130
209 res_cache.FullFlush(); 131 res_cache.FullFlush();
210} 132}
@@ -221,6 +143,11 @@ void RasterizerOpenGL::DrawTriangles() {
221 SyncFramebuffer(); 143 SyncFramebuffer();
222 SyncDrawState(); 144 SyncDrawState();
223 145
146 if (state.draw.shader_dirty) {
147 SetShader();
148 state.draw.shader_dirty = false;
149 }
150
224 glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW); 151 glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW);
225 glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); 152 glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
226 153
@@ -272,6 +199,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
272 // Alpha test 199 // Alpha test
273 case PICA_REG_INDEX(output_merger.alpha_test): 200 case PICA_REG_INDEX(output_merger.alpha_test):
274 SyncAlphaTest(); 201 SyncAlphaTest();
202 state.draw.shader_dirty = true;
275 break; 203 break;
276 204
277 // Stencil test 205 // Stencil test
@@ -290,117 +218,57 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
290 SyncLogicOp(); 218 SyncLogicOp();
291 break; 219 break;
292 220
293 // TEV stage 0 221 // TEV stages
294 case PICA_REG_INDEX(tev_stage0.color_source1): 222 case PICA_REG_INDEX(tev_stage0.color_source1):
295 SyncTevSources(0, regs.tev_stage0);
296 break;
297 case PICA_REG_INDEX(tev_stage0.color_modifier1): 223 case PICA_REG_INDEX(tev_stage0.color_modifier1):
298 SyncTevModifiers(0, regs.tev_stage0);
299 break;
300 case PICA_REG_INDEX(tev_stage0.color_op): 224 case PICA_REG_INDEX(tev_stage0.color_op):
301 SyncTevOps(0, regs.tev_stage0);
302 break;
303 case PICA_REG_INDEX(tev_stage0.const_r):
304 SyncTevColor(0, regs.tev_stage0);
305 break;
306 case PICA_REG_INDEX(tev_stage0.color_scale): 225 case PICA_REG_INDEX(tev_stage0.color_scale):
307 SyncTevMultipliers(0, regs.tev_stage0);
308 break;
309
310 // TEV stage 1
311 case PICA_REG_INDEX(tev_stage1.color_source1): 226 case PICA_REG_INDEX(tev_stage1.color_source1):
312 SyncTevSources(1, regs.tev_stage1);
313 break;
314 case PICA_REG_INDEX(tev_stage1.color_modifier1): 227 case PICA_REG_INDEX(tev_stage1.color_modifier1):
315 SyncTevModifiers(1, regs.tev_stage1);
316 break;
317 case PICA_REG_INDEX(tev_stage1.color_op): 228 case PICA_REG_INDEX(tev_stage1.color_op):
318 SyncTevOps(1, regs.tev_stage1);
319 break;
320 case PICA_REG_INDEX(tev_stage1.const_r):
321 SyncTevColor(1, regs.tev_stage1);
322 break;
323 case PICA_REG_INDEX(tev_stage1.color_scale): 229 case PICA_REG_INDEX(tev_stage1.color_scale):
324 SyncTevMultipliers(1, regs.tev_stage1);
325 break;
326
327 // TEV stage 2
328 case PICA_REG_INDEX(tev_stage2.color_source1): 230 case PICA_REG_INDEX(tev_stage2.color_source1):
329 SyncTevSources(2, regs.tev_stage2);
330 break;
331 case PICA_REG_INDEX(tev_stage2.color_modifier1): 231 case PICA_REG_INDEX(tev_stage2.color_modifier1):
332 SyncTevModifiers(2, regs.tev_stage2);
333 break;
334 case PICA_REG_INDEX(tev_stage2.color_op): 232 case PICA_REG_INDEX(tev_stage2.color_op):
335 SyncTevOps(2, regs.tev_stage2);
336 break;
337 case PICA_REG_INDEX(tev_stage2.const_r):
338 SyncTevColor(2, regs.tev_stage2);
339 break;
340 case PICA_REG_INDEX(tev_stage2.color_scale): 233 case PICA_REG_INDEX(tev_stage2.color_scale):
341 SyncTevMultipliers(2, regs.tev_stage2);
342 break;
343
344 // TEV stage 3
345 case PICA_REG_INDEX(tev_stage3.color_source1): 234 case PICA_REG_INDEX(tev_stage3.color_source1):
346 SyncTevSources(3, regs.tev_stage3);
347 break;
348 case PICA_REG_INDEX(tev_stage3.color_modifier1): 235 case PICA_REG_INDEX(tev_stage3.color_modifier1):
349 SyncTevModifiers(3, regs.tev_stage3);
350 break;
351 case PICA_REG_INDEX(tev_stage3.color_op): 236 case PICA_REG_INDEX(tev_stage3.color_op):
352 SyncTevOps(3, regs.tev_stage3);
353 break;
354 case PICA_REG_INDEX(tev_stage3.const_r):
355 SyncTevColor(3, regs.tev_stage3);
356 break;
357 case PICA_REG_INDEX(tev_stage3.color_scale): 237 case PICA_REG_INDEX(tev_stage3.color_scale):
358 SyncTevMultipliers(3, regs.tev_stage3);
359 break;
360
361 // TEV stage 4
362 case PICA_REG_INDEX(tev_stage4.color_source1): 238 case PICA_REG_INDEX(tev_stage4.color_source1):
363 SyncTevSources(4, regs.tev_stage4);
364 break;
365 case PICA_REG_INDEX(tev_stage4.color_modifier1): 239 case PICA_REG_INDEX(tev_stage4.color_modifier1):
366 SyncTevModifiers(4, regs.tev_stage4);
367 break;
368 case PICA_REG_INDEX(tev_stage4.color_op): 240 case PICA_REG_INDEX(tev_stage4.color_op):
369 SyncTevOps(4, regs.tev_stage4); 241 case PICA_REG_INDEX(tev_stage4.color_scale):
242 case PICA_REG_INDEX(tev_stage5.color_source1):
243 case PICA_REG_INDEX(tev_stage5.color_modifier1):
244 case PICA_REG_INDEX(tev_stage5.color_op):
245 case PICA_REG_INDEX(tev_stage5.color_scale):
246 case PICA_REG_INDEX(tev_combiner_buffer_input):
247 state.draw.shader_dirty = true;
370 break; 248 break;
371 case PICA_REG_INDEX(tev_stage4.const_r): 249 case PICA_REG_INDEX(tev_stage0.const_r):
372 SyncTevColor(4, regs.tev_stage4); 250 SyncTevConstColor(0, regs.tev_stage0);
373 break; 251 break;
374 case PICA_REG_INDEX(tev_stage4.color_scale): 252 case PICA_REG_INDEX(tev_stage1.const_r):
375 SyncTevMultipliers(4, regs.tev_stage4); 253 SyncTevConstColor(1, regs.tev_stage1);
376 break; 254 break;
377 255 case PICA_REG_INDEX(tev_stage2.const_r):
378 // TEV stage 5 256 SyncTevConstColor(2, regs.tev_stage2);
379 case PICA_REG_INDEX(tev_stage5.color_source1):
380 SyncTevSources(5, regs.tev_stage5);
381 break; 257 break;
382 case PICA_REG_INDEX(tev_stage5.color_modifier1): 258 case PICA_REG_INDEX(tev_stage3.const_r):
383 SyncTevModifiers(5, regs.tev_stage5); 259 SyncTevConstColor(3, regs.tev_stage3);
384 break; 260 break;
385 case PICA_REG_INDEX(tev_stage5.color_op): 261 case PICA_REG_INDEX(tev_stage4.const_r):
386 SyncTevOps(5, regs.tev_stage5); 262 SyncTevConstColor(4, regs.tev_stage4);
387 break; 263 break;
388 case PICA_REG_INDEX(tev_stage5.const_r): 264 case PICA_REG_INDEX(tev_stage5.const_r):
389 SyncTevColor(5, regs.tev_stage5); 265 SyncTevConstColor(5, regs.tev_stage5);
390 break;
391 case PICA_REG_INDEX(tev_stage5.color_scale):
392 SyncTevMultipliers(5, regs.tev_stage5);
393 break; 266 break;
394 267
395 // TEV combiner buffer color 268 // TEV combiner buffer color
396 case PICA_REG_INDEX(tev_combiner_buffer_color): 269 case PICA_REG_INDEX(tev_combiner_buffer_color):
397 SyncCombinerColor(); 270 SyncCombinerColor();
398 break; 271 break;
399
400 // TEV combiner buffer write flags
401 case PICA_REG_INDEX(tev_combiner_buffer_input):
402 SyncCombinerWriteFlags();
403 break;
404 } 272 }
405} 273}
406 274
@@ -592,6 +460,41 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::
592 state.Apply(); 460 state.Apply();
593} 461}
594 462
463void RasterizerOpenGL::SetShader() {
464 PicaShaderConfig config = PicaShaderConfig::CurrentConfig();
465 std::unique_ptr<PicaShader> shader = Common::make_unique<PicaShader>();
466
467 // Find (or generate) the GLSL shader for the current TEV state
468 auto cached_shader = shader_cache.find(config);
469 if (cached_shader != shader_cache.end()) {
470 current_shader = cached_shader->second.get();
471
472 state.draw.shader_program = current_shader->shader.handle;
473 state.Apply();
474 } else {
475 LOG_DEBUG(Render_OpenGL, "Creating new shader");
476
477 shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str());
478
479 state.draw.shader_program = shader->shader.handle;
480 state.Apply();
481
482 // Set the texture samplers to correspond to different texture units
483 glUniform1i(PicaShader::Uniform::Texture0, 0);
484 glUniform1i(PicaShader::Uniform::Texture1, 1);
485 glUniform1i(PicaShader::Uniform::Texture2, 2);
486
487 current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
488 }
489
490 // Update uniforms
491 SyncAlphaTest();
492 SyncCombinerColor();
493 auto& tev_stages = Pica::g_state.regs.GetTevStages();
494 for (int index = 0; index < tev_stages.size(); ++index)
495 SyncTevConstColor(index, tev_stages[index]);
496}
497
595void RasterizerOpenGL::SyncFramebuffer() { 498void RasterizerOpenGL::SyncFramebuffer() {
596 const auto& regs = Pica::g_state.regs; 499 const auto& regs = Pica::g_state.regs;
597 500
@@ -601,8 +504,8 @@ void RasterizerOpenGL::SyncFramebuffer() {
601 PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress(); 504 PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
602 Pica::Regs::DepthFormat new_fb_depth_format = regs.framebuffer.depth_format; 505 Pica::Regs::DepthFormat new_fb_depth_format = regs.framebuffer.depth_format;
603 506
604 bool fb_size_changed = fb_color_texture.width != regs.framebuffer.GetWidth() || 507 bool fb_size_changed = fb_color_texture.width != static_cast<GLsizei>(regs.framebuffer.GetWidth()) ||
605 fb_color_texture.height != regs.framebuffer.GetHeight(); 508 fb_color_texture.height != static_cast<GLsizei>(regs.framebuffer.GetHeight());
606 509
607 bool color_fb_prop_changed = fb_color_texture.format != new_fb_color_format || 510 bool color_fb_prop_changed = fb_color_texture.format != new_fb_color_format ||
608 fb_size_changed; 511 fb_size_changed;
@@ -712,9 +615,7 @@ void RasterizerOpenGL::SyncBlendColor() {
712 615
713void RasterizerOpenGL::SyncAlphaTest() { 616void RasterizerOpenGL::SyncAlphaTest() {
714 const auto& regs = Pica::g_state.regs; 617 const auto& regs = Pica::g_state.regs;
715 glUniform1i(uniform_alphatest_enabled, regs.output_merger.alpha_test.enable); 618 glUniform1i(PicaShader::Uniform::AlphaTestRef, regs.output_merger.alpha_test.ref);
716 glUniform1i(uniform_alphatest_func, (GLint)regs.output_merger.alpha_test.func.Value());
717 glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f);
718} 619}
719 620
720void RasterizerOpenGL::SyncLogicOp() { 621void RasterizerOpenGL::SyncLogicOp() {
@@ -744,56 +645,14 @@ void RasterizerOpenGL::SyncDepthTest() {
744 state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE; 645 state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE;
745} 646}
746 647
747void RasterizerOpenGL::SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
748 GLint color_srcs[3] = { (GLint)config.color_source1.Value(),
749 (GLint)config.color_source2.Value(),
750 (GLint)config.color_source3.Value() };
751 GLint alpha_srcs[3] = { (GLint)config.alpha_source1.Value(),
752 (GLint)config.alpha_source2.Value(),
753 (GLint)config.alpha_source3.Value() };
754
755 glUniform3iv(uniform_tev_cfgs[stage_index].color_sources, 1, color_srcs);
756 glUniform3iv(uniform_tev_cfgs[stage_index].alpha_sources, 1, alpha_srcs);
757}
758
759void RasterizerOpenGL::SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
760 GLint color_mods[3] = { (GLint)config.color_modifier1.Value(),
761 (GLint)config.color_modifier2.Value(),
762 (GLint)config.color_modifier3.Value() };
763 GLint alpha_mods[3] = { (GLint)config.alpha_modifier1.Value(),
764 (GLint)config.alpha_modifier2.Value(),
765 (GLint)config.alpha_modifier3.Value() };
766
767 glUniform3iv(uniform_tev_cfgs[stage_index].color_modifiers, 1, color_mods);
768 glUniform3iv(uniform_tev_cfgs[stage_index].alpha_modifiers, 1, alpha_mods);
769}
770
771void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
772 glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_op, (GLint)config.color_op.Value(), (GLint)config.alpha_op.Value());
773}
774
775void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
776 auto const_color = PicaToGL::ColorRGBA8(config.const_color);
777 glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data());
778}
779
780void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
781 glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_multiplier, config.GetColorMultiplier(), config.GetAlphaMultiplier());
782}
783
784void RasterizerOpenGL::SyncCombinerColor() { 648void RasterizerOpenGL::SyncCombinerColor() {
785 auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); 649 auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
786 glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data()); 650 glUniform4fv(PicaShader::Uniform::TevCombinerBufferColor, 1, combiner_color.data());
787} 651}
788 652
789void RasterizerOpenGL::SyncCombinerWriteFlags() { 653void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevStageConfig& tev_stage) {
790 const auto& regs = Pica::g_state.regs; 654 auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color);
791 const auto tev_stages = regs.GetTevStages(); 655 glUniform4fv(PicaShader::Uniform::TevConstColors + stage_index, 1, const_color.data());
792 for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
793 glUniform2i(uniform_tev_cfgs[tev_stage_index].updates_combiner_buffer_color_alpha,
794 regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index),
795 regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index));
796 }
797} 656}
798 657
799void RasterizerOpenGL::SyncDrawState() { 658void RasterizerOpenGL::SyncDrawState() {
@@ -824,12 +683,6 @@ void RasterizerOpenGL::SyncDrawState() {
824 } 683 }
825 } 684 }
826 685
827 // Skip processing TEV stages that simply pass the previous stage results through
828 const auto tev_stages = regs.GetTevStages();
829 for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
830 glUniform1i(uniform_tev_cfgs[tev_stage_index].enabled, !IsPassThroughTevStage(tev_stages[tev_stage_index]));
831 }
832
833 state.Apply(); 686 state.Apply();
834} 687}
835 688
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 1fe307846..872cae7da 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -4,15 +4,104 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstddef>
8#include <cstring>
9#include <memory>
7#include <vector> 10#include <vector>
11#include <unordered_map>
8 12
9#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/hash.h"
10 15
16#include "video_core/pica.h"
11#include "video_core/hwrasterizer_base.h" 17#include "video_core/hwrasterizer_base.h"
12#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 18#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
13#include "video_core/renderer_opengl/gl_state.h" 19#include "video_core/renderer_opengl/gl_state.h"
14#include "video_core/shader/shader_interpreter.h" 20#include "video_core/shader/shader_interpreter.h"
15 21
22/**
23 * This struct contains all state used to generate the GLSL shader program that emulates the current
24 * Pica register configuration. This struct is used as a cache key for generated GLSL shader
25 * programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by
26 * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
27 * Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
28 * two separate shaders sharing the same key.
29 */
30struct PicaShaderConfig {
31 /// Construct a PicaShaderConfig with the current Pica register configuration.
32 static PicaShaderConfig CurrentConfig() {
33 PicaShaderConfig res;
34 const auto& regs = Pica::g_state.regs;
35
36 res.alpha_test_func = regs.output_merger.alpha_test.enable ?
37 regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always;
38
39 // Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling
40 // the GetTevStages() function) because BitField explicitly disables copies.
41
42 res.tev_stages[0].sources_raw = regs.tev_stage0.sources_raw;
43 res.tev_stages[1].sources_raw = regs.tev_stage1.sources_raw;
44 res.tev_stages[2].sources_raw = regs.tev_stage2.sources_raw;
45 res.tev_stages[3].sources_raw = regs.tev_stage3.sources_raw;
46 res.tev_stages[4].sources_raw = regs.tev_stage4.sources_raw;
47 res.tev_stages[5].sources_raw = regs.tev_stage5.sources_raw;
48
49 res.tev_stages[0].modifiers_raw = regs.tev_stage0.modifiers_raw;
50 res.tev_stages[1].modifiers_raw = regs.tev_stage1.modifiers_raw;
51 res.tev_stages[2].modifiers_raw = regs.tev_stage2.modifiers_raw;
52 res.tev_stages[3].modifiers_raw = regs.tev_stage3.modifiers_raw;
53 res.tev_stages[4].modifiers_raw = regs.tev_stage4.modifiers_raw;
54 res.tev_stages[5].modifiers_raw = regs.tev_stage5.modifiers_raw;
55
56 res.tev_stages[0].ops_raw = regs.tev_stage0.ops_raw;
57 res.tev_stages[1].ops_raw = regs.tev_stage1.ops_raw;
58 res.tev_stages[2].ops_raw = regs.tev_stage2.ops_raw;
59 res.tev_stages[3].ops_raw = regs.tev_stage3.ops_raw;
60 res.tev_stages[4].ops_raw = regs.tev_stage4.ops_raw;
61 res.tev_stages[5].ops_raw = regs.tev_stage5.ops_raw;
62
63 res.tev_stages[0].scales_raw = regs.tev_stage0.scales_raw;
64 res.tev_stages[1].scales_raw = regs.tev_stage1.scales_raw;
65 res.tev_stages[2].scales_raw = regs.tev_stage2.scales_raw;
66 res.tev_stages[3].scales_raw = regs.tev_stage3.scales_raw;
67 res.tev_stages[4].scales_raw = regs.tev_stage4.scales_raw;
68 res.tev_stages[5].scales_raw = regs.tev_stage5.scales_raw;
69
70 res.combiner_buffer_input =
71 regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
72 regs.tev_combiner_buffer_input.update_mask_a.Value() << 4;
73
74 return res;
75 }
76
77 bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
78 return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index));
79 }
80
81 bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
82 return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index));
83 }
84
85 bool operator ==(const PicaShaderConfig& o) const {
86 return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0;
87 };
88
89 Pica::Regs::CompareFunc alpha_test_func;
90 std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
91 u8 combiner_buffer_input;
92};
93
94namespace std {
95
96template <>
97struct hash<PicaShaderConfig> {
98 size_t operator()(const PicaShaderConfig& k) const {
99 return Common::ComputeHash64(&k, sizeof(PicaShaderConfig));
100 }
101};
102
103} // namespace std
104
16class RasterizerOpenGL : public HWRasterizer { 105class RasterizerOpenGL : public HWRasterizer {
17public: 106public:
18 107
@@ -45,20 +134,24 @@ public:
45 /// Notify rasterizer that a 3DS memory region has been changed 134 /// Notify rasterizer that a 3DS memory region has been changed
46 void NotifyFlush(PAddr addr, u32 size) override; 135 void NotifyFlush(PAddr addr, u32 size) override;
47 136
48private: 137 /// OpenGL shader generated for a given Pica register state
49 /// Structure used for managing texture environment states 138 struct PicaShader {
50 struct TEVConfigUniforms { 139 /// OpenGL shader resource
51 GLuint enabled; 140 OGLShader shader;
52 GLuint color_sources; 141
53 GLuint alpha_sources; 142 /// Fragment shader uniforms
54 GLuint color_modifiers; 143 enum Uniform : GLuint {
55 GLuint alpha_modifiers; 144 AlphaTestRef = 0,
56 GLuint color_alpha_op; 145 TevConstColors = 1,
57 GLuint color_alpha_multiplier; 146 Texture0 = 7,
58 GLuint const_color; 147 Texture1 = 8,
59 GLuint updates_combiner_buffer_color_alpha; 148 Texture2 = 9,
149 TevCombinerBufferColor = 10,
150 };
60 }; 151 };
61 152
153private:
154
62 /// Structure used for storing information about color textures 155 /// Structure used for storing information about color textures
63 struct TextureInfo { 156 struct TextureInfo {
64 OGLTexture texture; 157 OGLTexture texture;
@@ -129,6 +222,9 @@ private:
129 /// Reconfigure the OpenGL depth texture to use the given format and dimensions 222 /// Reconfigure the OpenGL depth texture to use the given format and dimensions
130 void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height); 223 void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height);
131 224
225 /// Sets the OpenGL shader in accordance with the current PICA register state
226 void SetShader();
227
132 /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer 228 /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer
133 void SyncFramebuffer(); 229 void SyncFramebuffer();
134 230
@@ -156,27 +252,12 @@ private:
156 /// Syncs the depth test states to match the PICA register 252 /// Syncs the depth test states to match the PICA register
157 void SyncDepthTest(); 253 void SyncDepthTest();
158 254
159 /// Syncs the specified TEV stage's color and alpha sources to match the PICA register 255 /// Syncs the TEV constant color to match the PICA register
160 void SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config); 256 void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage);
161
162 /// Syncs the specified TEV stage's color and alpha modifiers to match the PICA register
163 void SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
164
165 /// Syncs the specified TEV stage's color and alpha combiner operations to match the PICA register
166 void SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
167
168 /// Syncs the specified TEV stage's constant color to match the PICA register
169 void SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
170
171 /// Syncs the specified TEV stage's color and alpha multipliers to match the PICA register
172 void SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
173 257
174 /// Syncs the TEV combiner color buffer to match the PICA register 258 /// Syncs the TEV combiner color buffer to match the PICA register
175 void SyncCombinerColor(); 259 void SyncCombinerColor();
176 260
177 /// Syncs the TEV combiner write flags to match the PICA register
178 void SyncCombinerWriteFlags();
179
180 /// Syncs the remaining OpenGL drawing state to match the current PICA state 261 /// Syncs the remaining OpenGL drawing state to match the current PICA state
181 void SyncDrawState(); 262 void SyncDrawState();
182 263
@@ -213,21 +294,11 @@ private:
213 std::array<SamplerInfo, 3> texture_samplers; 294 std::array<SamplerInfo, 3> texture_samplers;
214 TextureInfo fb_color_texture; 295 TextureInfo fb_color_texture;
215 DepthTextureInfo fb_depth_texture; 296 DepthTextureInfo fb_depth_texture;
216 OGLShader shader; 297
298 std::unordered_map<PicaShaderConfig, std::unique_ptr<PicaShader>> shader_cache;
299 const PicaShader* current_shader = nullptr;
300
217 OGLVertexArray vertex_array; 301 OGLVertexArray vertex_array;
218 OGLBuffer vertex_buffer; 302 OGLBuffer vertex_buffer;
219 OGLFramebuffer framebuffer; 303 OGLFramebuffer framebuffer;
220
221 // Hardware vertex shader
222 GLuint attrib_position;
223 GLuint attrib_color;
224 GLuint attrib_texcoords;
225
226 // Hardware fragment shader
227 GLuint uniform_alphatest_enabled;
228 GLuint uniform_alphatest_func;
229 GLuint uniform_alphatest_ref;
230 GLuint uniform_tex;
231 GLuint uniform_tev_combiner_buffer_color;
232 TEVConfigUniforms uniform_tev_cfgs[6];
233}; 304};
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 65034d40d..eb128966c 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -71,7 +71,7 @@ public:
71 /// Creates a new internal OpenGL resource and stores the handle 71 /// Creates a new internal OpenGL resource and stores the handle
72 void Create(const char* vert_shader, const char* frag_shader) { 72 void Create(const char* vert_shader, const char* frag_shader) {
73 if (handle != 0) return; 73 if (handle != 0) return;
74 handle = ShaderUtil::LoadShaders(vert_shader, frag_shader); 74 handle = GLShader::LoadProgram(vert_shader, frag_shader);
75 } 75 }
76 76
77 /// Deletes the internal OpenGL resource 77 /// Deletes the internal OpenGL resource
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
new file mode 100644
index 000000000..d19d15e75
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -0,0 +1,388 @@
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/pica.h"
6#include "video_core/renderer_opengl/gl_rasterizer.h"
7#include "video_core/renderer_opengl/gl_shader_gen.h"
8
9using Pica::Regs;
10using TevStageConfig = Regs::TevStageConfig;
11
12namespace GLShader {
13
14/// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code)
15static bool IsPassThroughTevStage(const TevStageConfig& stage) {
16 return (stage.color_op == TevStageConfig::Operation::Replace &&
17 stage.alpha_op == TevStageConfig::Operation::Replace &&
18 stage.color_source1 == TevStageConfig::Source::Previous &&
19 stage.alpha_source1 == TevStageConfig::Source::Previous &&
20 stage.color_modifier1 == TevStageConfig::ColorModifier::SourceColor &&
21 stage.alpha_modifier1 == TevStageConfig::AlphaModifier::SourceAlpha &&
22 stage.GetColorMultiplier() == 1 &&
23 stage.GetAlphaMultiplier() == 1);
24}
25
26/// Writes the specified TEV stage source component(s)
27static void AppendSource(std::string& out, TevStageConfig::Source source,
28 const std::string& index_name) {
29 using Source = TevStageConfig::Source;
30 switch (source) {
31 case Source::PrimaryColor:
32 out += "primary_color";
33 break;
34 case Source::PrimaryFragmentColor:
35 // HACK: Until we implement fragment lighting, use primary_color
36 out += "primary_color";
37 break;
38 case Source::SecondaryFragmentColor:
39 // HACK: Until we implement fragment lighting, use zero
40 out += "vec4(0.0)";
41 break;
42 case Source::Texture0:
43 out += "texture(tex[0], texcoord[0])";
44 break;
45 case Source::Texture1:
46 out += "texture(tex[1], texcoord[1])";
47 break;
48 case Source::Texture2:
49 out += "texture(tex[2], texcoord[2])";
50 break;
51 case Source::PreviousBuffer:
52 out += "combiner_buffer";
53 break;
54 case Source::Constant:
55 ((out += "const_color[") += index_name) += ']';
56 break;
57 case Source::Previous:
58 out += "last_tex_env_out";
59 break;
60 default:
61 out += "vec4(0.0)";
62 LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source);
63 break;
64 }
65}
66
67/// Writes the color components to use for the specified TEV stage color modifier
68static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier modifier,
69 TevStageConfig::Source source, const std::string& index_name) {
70 using ColorModifier = TevStageConfig::ColorModifier;
71 switch (modifier) {
72 case ColorModifier::SourceColor:
73 AppendSource(out, source, index_name);
74 out += ".rgb";
75 break;
76 case ColorModifier::OneMinusSourceColor:
77 out += "vec3(1.0) - ";
78 AppendSource(out, source, index_name);
79 out += ".rgb";
80 break;
81 case ColorModifier::SourceAlpha:
82 AppendSource(out, source, index_name);
83 out += ".aaa";
84 break;
85 case ColorModifier::OneMinusSourceAlpha:
86 out += "vec3(1.0) - ";
87 AppendSource(out, source, index_name);
88 out += ".aaa";
89 break;
90 case ColorModifier::SourceRed:
91 AppendSource(out, source, index_name);
92 out += ".rrr";
93 break;
94 case ColorModifier::OneMinusSourceRed:
95 out += "vec3(1.0) - ";
96 AppendSource(out, source, index_name);
97 out += ".rrr";
98 break;
99 case ColorModifier::SourceGreen:
100 AppendSource(out, source, index_name);
101 out += ".ggg";
102 break;
103 case ColorModifier::OneMinusSourceGreen:
104 out += "vec3(1.0) - ";
105 AppendSource(out, source, index_name);
106 out += ".ggg";
107 break;
108 case ColorModifier::SourceBlue:
109 AppendSource(out, source, index_name);
110 out += ".bbb";
111 break;
112 case ColorModifier::OneMinusSourceBlue:
113 out += "vec3(1.0) - ";
114 AppendSource(out, source, index_name);
115 out += ".bbb";
116 break;
117 default:
118 out += "vec3(0.0)";
119 LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier);
120 break;
121 }
122}
123
124/// Writes the alpha component to use for the specified TEV stage alpha modifier
125static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier modifier,
126 TevStageConfig::Source source, const std::string& index_name) {
127 using AlphaModifier = TevStageConfig::AlphaModifier;
128 switch (modifier) {
129 case AlphaModifier::SourceAlpha:
130 AppendSource(out, source, index_name);
131 out += ".a";
132 break;
133 case AlphaModifier::OneMinusSourceAlpha:
134 out += "1.0 - ";
135 AppendSource(out, source, index_name);
136 out += ".a";
137 break;
138 case AlphaModifier::SourceRed:
139 AppendSource(out, source, index_name);
140 out += ".r";
141 break;
142 case AlphaModifier::OneMinusSourceRed:
143 out += "1.0 - ";
144 AppendSource(out, source, index_name);
145 out += ".r";
146 break;
147 case AlphaModifier::SourceGreen:
148 AppendSource(out, source, index_name);
149 out += ".g";
150 break;
151 case AlphaModifier::OneMinusSourceGreen:
152 out += "1.0 - ";
153 AppendSource(out, source, index_name);
154 out += ".g";
155 break;
156 case AlphaModifier::SourceBlue:
157 AppendSource(out, source, index_name);
158 out += ".b";
159 break;
160 case AlphaModifier::OneMinusSourceBlue:
161 out += "1.0 - ";
162 AppendSource(out, source, index_name);
163 out += ".b";
164 break;
165 default:
166 out += "0.0";
167 LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier);
168 break;
169 }
170}
171
172/// Writes the combiner function for the color components for the specified TEV stage operation
173static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation,
174 const std::string& variable_name) {
175 out += "clamp(";
176 using Operation = TevStageConfig::Operation;
177 switch (operation) {
178 case Operation::Replace:
179 out += variable_name + "[0]";
180 break;
181 case Operation::Modulate:
182 out += variable_name + "[0] * " + variable_name + "[1]";
183 break;
184 case Operation::Add:
185 out += variable_name + "[0] + " + variable_name + "[1]";
186 break;
187 case Operation::AddSigned:
188 out += variable_name + "[0] + " + variable_name + "[1] - vec3(0.5)";
189 break;
190 case Operation::Lerp:
191 // TODO(bunnei): Verify if HW actually does this per-component, otherwise we can just use builtin lerp
192 out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])";
193 break;
194 case Operation::Subtract:
195 out += variable_name + "[0] - " + variable_name + "[1]";
196 break;
197 case Operation::MultiplyThenAdd:
198 out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]";
199 break;
200 case Operation::AddThenMultiply:
201 out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]";
202 break;
203 default:
204 out += "vec3(0.0)";
205 LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation);
206 break;
207 }
208 out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0
209}
210
211/// Writes the combiner function for the alpha component for the specified TEV stage operation
212static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation,
213 const std::string& variable_name) {
214 out += "clamp(";
215 using Operation = TevStageConfig::Operation;
216 switch (operation) {
217 case Operation::Replace:
218 out += variable_name + "[0]";
219 break;
220 case Operation::Modulate:
221 out += variable_name + "[0] * " + variable_name + "[1]";
222 break;
223 case Operation::Add:
224 out += variable_name + "[0] + " + variable_name + "[1]";
225 break;
226 case Operation::AddSigned:
227 out += variable_name + "[0] + " + variable_name + "[1] - 0.5";
228 break;
229 case Operation::Lerp:
230 out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])";
231 break;
232 case Operation::Subtract:
233 out += variable_name + "[0] - " + variable_name + "[1]";
234 break;
235 case Operation::MultiplyThenAdd:
236 out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]";
237 break;
238 case Operation::AddThenMultiply:
239 out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]";
240 break;
241 default:
242 out += "0.0";
243 LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation);
244 break;
245 }
246 out += ", 0.0, 1.0)";
247}
248
249/// Writes the if-statement condition used to evaluate alpha testing
250static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) {
251 using CompareFunc = Regs::CompareFunc;
252 switch (func) {
253 case CompareFunc::Never:
254 out += "true";
255 break;
256 case CompareFunc::Always:
257 out += "false";
258 break;
259 case CompareFunc::Equal:
260 case CompareFunc::NotEqual:
261 case CompareFunc::LessThan:
262 case CompareFunc::LessThanOrEqual:
263 case CompareFunc::GreaterThan:
264 case CompareFunc::GreaterThanOrEqual:
265 {
266 static const char* op[] = { "!=", "==", ">=", ">", "<=", "<", };
267 unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal;
268 out += "int(last_tex_env_out.a * 255.0f) " + std::string(op[index]) + " alphatest_ref";
269 break;
270 }
271
272 default:
273 out += "false";
274 LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func);
275 break;
276 }
277}
278
279/// Writes the code to emulate the specified TEV stage
280static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
281 auto& stage = config.tev_stages[index];
282 if (!IsPassThroughTevStage(stage)) {
283 std::string index_name = std::to_string(index);
284
285 out += "vec3 color_results_" + index_name + "[3] = vec3[3](";
286 AppendColorModifier(out, stage.color_modifier1, stage.color_source1, index_name);
287 out += ", ";
288 AppendColorModifier(out, stage.color_modifier2, stage.color_source2, index_name);
289 out += ", ";
290 AppendColorModifier(out, stage.color_modifier3, stage.color_source3, index_name);
291 out += ");\n";
292
293 out += "vec3 color_output_" + index_name + " = ";
294 AppendColorCombiner(out, stage.color_op, "color_results_" + index_name);
295 out += ";\n";
296
297 out += "float alpha_results_" + index_name + "[3] = float[3](";
298 AppendAlphaModifier(out, stage.alpha_modifier1, stage.alpha_source1, index_name);
299 out += ", ";
300 AppendAlphaModifier(out, stage.alpha_modifier2, stage.alpha_source2, index_name);
301 out += ", ";
302 AppendAlphaModifier(out, stage.alpha_modifier3, stage.alpha_source3, index_name);
303 out += ");\n";
304
305 out += "float alpha_output_" + index_name + " = ";
306 AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name);
307 out += ";\n";
308
309 out += "last_tex_env_out = vec4("
310 "clamp(color_output_" + index_name + " * " + std::to_string(stage.GetColorMultiplier()) + ".0, vec3(0.0), vec3(1.0)),"
311 "clamp(alpha_output_" + index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + ".0, 0.0, 1.0));\n";
312 }
313
314 if (config.TevStageUpdatesCombinerBufferColor(index))
315 out += "combiner_buffer.rgb = last_tex_env_out.rgb;\n";
316
317 if (config.TevStageUpdatesCombinerBufferAlpha(index))
318 out += "combiner_buffer.a = last_tex_env_out.a;\n";
319}
320
321std::string GenerateFragmentShader(const PicaShaderConfig& config) {
322 std::string out = R"(
323#version 330
324#extension GL_ARB_explicit_uniform_location : require
325
326#define NUM_TEV_STAGES 6
327
328in vec4 primary_color;
329in vec2 texcoord[3];
330
331out vec4 color;
332)";
333
334 using Uniform = RasterizerOpenGL::PicaShader::Uniform;
335 out += "layout(location = " + std::to_string((int)Uniform::AlphaTestRef) + ") uniform int alphatest_ref;\n";
336 out += "layout(location = " + std::to_string((int)Uniform::TevConstColors) + ") uniform vec4 const_color[NUM_TEV_STAGES];\n";
337 out += "layout(location = " + std::to_string((int)Uniform::Texture0) + ") uniform sampler2D tex[3];\n";
338 out += "layout(location = " + std::to_string((int)Uniform::TevCombinerBufferColor) + ") uniform vec4 tev_combiner_buffer_color;\n";
339
340 out += "void main() {\n";
341 out += "vec4 combiner_buffer = tev_combiner_buffer_color;\n";
342 out += "vec4 last_tex_env_out = vec4(0.0);\n";
343
344 // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
345 if (config.alpha_test_func == Regs::CompareFunc::Never) {
346 out += "discard; }";
347 return out;
348 }
349
350 for (size_t index = 0; index < config.tev_stages.size(); ++index)
351 WriteTevStage(out, config, (unsigned)index);
352
353 if (config.alpha_test_func != Regs::CompareFunc::Always) {
354 out += "if (";
355 AppendAlphaTestCondition(out, config.alpha_test_func);
356 out += ") discard;\n";
357 }
358
359 out += "color = last_tex_env_out;\n}";
360
361 return out;
362}
363
364std::string GenerateVertexShader() {
365 std::string out = "#version 330\n";
366 out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n";
367 out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n";
368 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n";
369 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n";
370 out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n";
371
372 out += R"(
373out vec4 primary_color;
374out vec2 texcoord[3];
375
376void main() {
377 primary_color = vert_color;
378 texcoord[0] = vert_texcoord0;
379 texcoord[1] = vert_texcoord1;
380 texcoord[2] = vert_texcoord2;
381 gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
382}
383)";
384
385 return out;
386}
387
388} // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
new file mode 100644
index 000000000..0ca9d2879
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -0,0 +1,27 @@
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 <string>
8
9#include "video_core/renderer_opengl/gl_rasterizer.h"
10
11namespace GLShader {
12
13/**
14 * Generates the GLSL vertex shader program source code for the current Pica state
15 * @returns String of the shader source code
16 */
17std::string GenerateVertexShader();
18
19/**
20 * Generates the GLSL fragment shader program source code for the current Pica state
21 * @param config ShaderCacheKey object generated for the current Pica state, used for the shader
22 * configuration (NOTE: Use state in this struct only, not the Pica registers!)
23 * @returns String of the shader source code
24 */
25std::string GenerateFragmentShader(const PicaShaderConfig& config);
26
27} // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 4cf246c06..e3f7a5868 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -8,9 +8,9 @@
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "video_core/renderer_opengl/gl_shader_util.h" 9#include "video_core/renderer_opengl/gl_shader_util.h"
10 10
11namespace ShaderUtil { 11namespace GLShader {
12 12
13GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { 13GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
14 14
15 // Create the shaders 15 // Create the shaders
16 GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); 16 GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
@@ -65,6 +65,7 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
65 GLuint program_id = glCreateProgram(); 65 GLuint program_id = glCreateProgram();
66 glAttachShader(program_id, vertex_shader_id); 66 glAttachShader(program_id, vertex_shader_id);
67 glAttachShader(program_id, fragment_shader_id); 67 glAttachShader(program_id, fragment_shader_id);
68
68 glLinkProgram(program_id); 69 glLinkProgram(program_id);
69 70
70 // Check the program 71 // Check the program
@@ -87,4 +88,4 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
87 return program_id; 88 return program_id;
88} 89}
89 90
90} 91} // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index c9d7cc380..046aae14f 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -6,8 +6,22 @@
6 6
7#include <glad/glad.h> 7#include <glad/glad.h>
8 8
9namespace ShaderUtil { 9namespace GLShader {
10 10
11GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); 11enum Attributes {
12 ATTRIBUTE_POSITION,
13 ATTRIBUTE_COLOR,
14 ATTRIBUTE_TEXCOORD0,
15 ATTRIBUTE_TEXCOORD1,
16 ATTRIBUTE_TEXCOORD2,
17};
12 18
13} 19/**
20 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
21 * @param vertex_shader String of the GLSL vertex shader program
22 * @param fragment_shader String of the GLSL fragment shader program
23 * @returns Handle of the newly created OpenGL shader object
24 */
25GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader);
26
27} // namespace
diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h
deleted file mode 100644
index a8cb2f595..000000000
--- a/src/video_core/renderer_opengl/gl_shaders.h
+++ /dev/null
@@ -1,337 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace GLShaders {
8
9const char g_vertex_shader[] = R"(
10#version 150 core
11
12in vec2 vert_position;
13in vec2 vert_tex_coord;
14out vec2 frag_tex_coord;
15
16// This is a truncated 3x3 matrix for 2D transformations:
17// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
18// The third column performs translation.
19// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
20// implicitly be [0, 0, 1]
21uniform mat3x2 modelview_matrix;
22
23void main() {
24 // Multiply input position by the rotscale part of the matrix and then manually translate by
25 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
26 // to `vec3(vert_position.xy, 1.0)`
27 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
28 frag_tex_coord = vert_tex_coord;
29}
30)";
31
32const char g_fragment_shader[] = R"(
33#version 150 core
34
35in vec2 frag_tex_coord;
36out vec4 color;
37
38uniform sampler2D color_texture;
39
40void main() {
41 color = texture(color_texture, frag_tex_coord);
42}
43)";
44
45const char g_vertex_shader_hw[] = R"(
46#version 150 core
47
48#define NUM_VTX_ATTR 7
49
50in vec4 vert_position;
51in vec4 vert_color;
52in vec2 vert_texcoords[3];
53
54out vec4 o[NUM_VTX_ATTR];
55
56void main() {
57 o[2] = vert_color;
58 o[3] = vec4(vert_texcoords[0].xy, vert_texcoords[1].xy);
59 o[5] = vec4(0.0, 0.0, vert_texcoords[2].xy);
60
61 gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
62}
63)";
64
65// TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms
66const char g_fragment_shader_hw[] = R"(
67#version 150 core
68
69#define NUM_VTX_ATTR 7
70#define NUM_TEV_STAGES 6
71
72#define SOURCE_PRIMARYCOLOR 0x0
73#define SOURCE_PRIMARYFRAGMENTCOLOR 0x1
74#define SOURCE_SECONDARYFRAGMENTCOLOR 0x2
75#define SOURCE_TEXTURE0 0x3
76#define SOURCE_TEXTURE1 0x4
77#define SOURCE_TEXTURE2 0x5
78#define SOURCE_TEXTURE3 0x6
79#define SOURCE_PREVIOUSBUFFER 0xd
80#define SOURCE_CONSTANT 0xe
81#define SOURCE_PREVIOUS 0xf
82
83#define COLORMODIFIER_SOURCECOLOR 0x0
84#define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1
85#define COLORMODIFIER_SOURCEALPHA 0x2
86#define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3
87#define COLORMODIFIER_SOURCERED 0x4
88#define COLORMODIFIER_ONEMINUSSOURCERED 0x5
89#define COLORMODIFIER_SOURCEGREEN 0x8
90#define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9
91#define COLORMODIFIER_SOURCEBLUE 0xc
92#define COLORMODIFIER_ONEMINUSSOURCEBLUE 0xd
93
94#define ALPHAMODIFIER_SOURCEALPHA 0x0
95#define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1
96#define ALPHAMODIFIER_SOURCERED 0x2
97#define ALPHAMODIFIER_ONEMINUSSOURCERED 0x3
98#define ALPHAMODIFIER_SOURCEGREEN 0x4
99#define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5
100#define ALPHAMODIFIER_SOURCEBLUE 0x6
101#define ALPHAMODIFIER_ONEMINUSSOURCEBLUE 0x7
102
103#define OPERATION_REPLACE 0
104#define OPERATION_MODULATE 1
105#define OPERATION_ADD 2
106#define OPERATION_ADDSIGNED 3
107#define OPERATION_LERP 4
108#define OPERATION_SUBTRACT 5
109#define OPERATION_MULTIPLYTHENADD 8
110#define OPERATION_ADDTHENMULTIPLY 9
111
112#define COMPAREFUNC_NEVER 0
113#define COMPAREFUNC_ALWAYS 1
114#define COMPAREFUNC_EQUAL 2
115#define COMPAREFUNC_NOTEQUAL 3
116#define COMPAREFUNC_LESSTHAN 4
117#define COMPAREFUNC_LESSTHANOREQUAL 5
118#define COMPAREFUNC_GREATERTHAN 6
119#define COMPAREFUNC_GREATERTHANOREQUAL 7
120
121in vec4 o[NUM_VTX_ATTR];
122out vec4 color;
123
124uniform bool alphatest_enabled;
125uniform int alphatest_func;
126uniform float alphatest_ref;
127
128uniform sampler2D tex[3];
129
130uniform vec4 tev_combiner_buffer_color;
131
132struct TEVConfig
133{
134 bool enabled;
135 ivec3 color_sources;
136 ivec3 alpha_sources;
137 ivec3 color_modifiers;
138 ivec3 alpha_modifiers;
139 ivec2 color_alpha_op;
140 ivec2 color_alpha_multiplier;
141 vec4 const_color;
142 bvec2 updates_combiner_buffer_color_alpha;
143};
144
145uniform TEVConfig tev_cfgs[NUM_TEV_STAGES];
146
147vec4 g_combiner_buffer;
148vec4 g_last_tex_env_out;
149vec4 g_const_color;
150
151vec4 GetSource(int source) {
152 if (source == SOURCE_PRIMARYCOLOR) {
153 return o[2];
154 } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) {
155 // HACK: Until we implement fragment lighting, use primary_color
156 return o[2];
157 } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) {
158 // HACK: Until we implement fragment lighting, use zero
159 return vec4(0.0, 0.0, 0.0, 0.0);
160 } else if (source == SOURCE_TEXTURE0) {
161 return texture(tex[0], o[3].xy);
162 } else if (source == SOURCE_TEXTURE1) {
163 return texture(tex[1], o[3].zw);
164 } else if (source == SOURCE_TEXTURE2) {
165 // TODO: Unverified
166 return texture(tex[2], o[5].zw);
167 } else if (source == SOURCE_TEXTURE3) {
168 // TODO: no 4th texture?
169 } else if (source == SOURCE_PREVIOUSBUFFER) {
170 return g_combiner_buffer;
171 } else if (source == SOURCE_CONSTANT) {
172 return g_const_color;
173 } else if (source == SOURCE_PREVIOUS) {
174 return g_last_tex_env_out;
175 }
176
177 return vec4(0.0);
178}
179
180vec3 GetColorModifier(int factor, vec4 color) {
181 if (factor == COLORMODIFIER_SOURCECOLOR) {
182 return color.rgb;
183 } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) {
184 return vec3(1.0) - color.rgb;
185 } else if (factor == COLORMODIFIER_SOURCEALPHA) {
186 return color.aaa;
187 } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) {
188 return vec3(1.0) - color.aaa;
189 } else if (factor == COLORMODIFIER_SOURCERED) {
190 return color.rrr;
191 } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) {
192 return vec3(1.0) - color.rrr;
193 } else if (factor == COLORMODIFIER_SOURCEGREEN) {
194 return color.ggg;
195 } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) {
196 return vec3(1.0) - color.ggg;
197 } else if (factor == COLORMODIFIER_SOURCEBLUE) {
198 return color.bbb;
199 } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) {
200 return vec3(1.0) - color.bbb;
201 }
202
203 return vec3(0.0);
204}
205
206float GetAlphaModifier(int factor, vec4 color) {
207 if (factor == ALPHAMODIFIER_SOURCEALPHA) {
208 return color.a;
209 } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) {
210 return 1.0 - color.a;
211 } else if (factor == ALPHAMODIFIER_SOURCERED) {
212 return color.r;
213 } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) {
214 return 1.0 - color.r;
215 } else if (factor == ALPHAMODIFIER_SOURCEGREEN) {
216 return color.g;
217 } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) {
218 return 1.0 - color.g;
219 } else if (factor == ALPHAMODIFIER_SOURCEBLUE) {
220 return color.b;
221 } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) {
222 return 1.0 - color.b;
223 }
224
225 return 0.0;
226}
227
228vec3 ColorCombine(int op, vec3 color[3]) {
229 if (op == OPERATION_REPLACE) {
230 return color[0];
231 } else if (op == OPERATION_MODULATE) {
232 return color[0] * color[1];
233 } else if (op == OPERATION_ADD) {
234 return min(color[0] + color[1], 1.0);
235 } else if (op == OPERATION_ADDSIGNED) {
236 return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0);
237 } else if (op == OPERATION_LERP) {
238 return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]);
239 } else if (op == OPERATION_SUBTRACT) {
240 return max(color[0] - color[1], 0.0);
241 } else if (op == OPERATION_MULTIPLYTHENADD) {
242 return min(color[0] * color[1] + color[2], 1.0);
243 } else if (op == OPERATION_ADDTHENMULTIPLY) {
244 return min(color[0] + color[1], 1.0) * color[2];
245 }
246
247 return vec3(0.0);
248}
249
250float AlphaCombine(int op, float alpha[3]) {
251 if (op == OPERATION_REPLACE) {
252 return alpha[0];
253 } else if (op == OPERATION_MODULATE) {
254 return alpha[0] * alpha[1];
255 } else if (op == OPERATION_ADD) {
256 return min(alpha[0] + alpha[1], 1.0);
257 } else if (op == OPERATION_ADDSIGNED) {
258 return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0);
259 } else if (op == OPERATION_LERP) {
260 return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]);
261 } else if (op == OPERATION_SUBTRACT) {
262 return max(alpha[0] - alpha[1], 0.0);
263 } else if (op == OPERATION_MULTIPLYTHENADD) {
264 return min(alpha[0] * alpha[1] + alpha[2], 1.0);
265 } else if (op == OPERATION_ADDTHENMULTIPLY) {
266 return min(alpha[0] + alpha[1], 1.0) * alpha[2];
267 }
268
269 return 0.0;
270}
271
272void main(void) {
273 g_combiner_buffer = tev_combiner_buffer_color;
274
275 for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) {
276 if (tev_cfgs[tex_env_idx].enabled) {
277 g_const_color = tev_cfgs[tex_env_idx].const_color;
278
279 vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)),
280 GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)),
281 GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z)));
282 vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results);
283
284 float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)),
285 GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)),
286 GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z)));
287 float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results);
288
289 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));
290 }
291
292 if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) {
293 g_combiner_buffer.rgb = g_last_tex_env_out.rgb;
294 }
295
296 if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) {
297 g_combiner_buffer.a = g_last_tex_env_out.a;
298 }
299 }
300
301 if (alphatest_enabled) {
302 if (alphatest_func == COMPAREFUNC_NEVER) {
303 discard;
304 } else if (alphatest_func == COMPAREFUNC_ALWAYS) {
305
306 } else if (alphatest_func == COMPAREFUNC_EQUAL) {
307 if (g_last_tex_env_out.a != alphatest_ref) {
308 discard;
309 }
310 } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) {
311 if (g_last_tex_env_out.a == alphatest_ref) {
312 discard;
313 }
314 } else if (alphatest_func == COMPAREFUNC_LESSTHAN) {
315 if (g_last_tex_env_out.a >= alphatest_ref) {
316 discard;
317 }
318 } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) {
319 if (g_last_tex_env_out.a > alphatest_ref) {
320 discard;
321 }
322 } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) {
323 if (g_last_tex_env_out.a <= alphatest_ref) {
324 discard;
325 }
326 } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) {
327 if (g_last_tex_env_out.a < alphatest_ref) {
328 discard;
329 }
330 }
331 }
332
333 color = g_last_tex_env_out;
334}
335)";
336
337}
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 6ecbedbb4..668b04259 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -65,6 +65,7 @@ public:
65 GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING 65 GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
66 GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING 66 GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
67 GLuint shader_program; // GL_CURRENT_PROGRAM 67 GLuint shader_program; // GL_CURRENT_PROGRAM
68 bool shader_dirty;
68 } draw; 69 } draw;
69 70
70 OpenGLState(); 71 OpenGLState();
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f1313b54f..ac0a058db 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,9 +21,44 @@
21#include "video_core/debug_utils/debug_utils.h" 21#include "video_core/debug_utils/debug_utils.h"
22#include "video_core/renderer_opengl/gl_rasterizer.h" 22#include "video_core/renderer_opengl/gl_rasterizer.h"
23#include "video_core/renderer_opengl/gl_shader_util.h" 23#include "video_core/renderer_opengl/gl_shader_util.h"
24#include "video_core/renderer_opengl/gl_shaders.h"
25#include "video_core/renderer_opengl/renderer_opengl.h" 24#include "video_core/renderer_opengl/renderer_opengl.h"
26 25
26static const char vertex_shader[] = R"(
27#version 150 core
28
29in vec2 vert_position;
30in vec2 vert_tex_coord;
31out vec2 frag_tex_coord;
32
33// This is a truncated 3x3 matrix for 2D transformations:
34// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
35// The third column performs translation.
36// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
37// implicitly be [0, 0, 1]
38uniform mat3x2 modelview_matrix;
39
40void main() {
41 // Multiply input position by the rotscale part of the matrix and then manually translate by
42 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
43 // to `vec3(vert_position.xy, 1.0)`
44 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
45 frag_tex_coord = vert_tex_coord;
46}
47)";
48
49static const char fragment_shader[] = R"(
50#version 150 core
51
52in vec2 frag_tex_coord;
53out vec4 color;
54
55uniform sampler2D color_texture;
56
57void main() {
58 color = texture(color_texture, frag_tex_coord);
59}
60)";
61
27/** 62/**
28 * Vertex structure that the drawn screen rectangles are composed of. 63 * Vertex structure that the drawn screen rectangles are composed of.
29 */ 64 */
@@ -207,7 +242,7 @@ void RendererOpenGL::InitOpenGLObjects() {
207 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); 242 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f);
208 243
209 // Link shaders and get variable locations 244 // Link shaders and get variable locations
210 program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader); 245 program_id = GLShader::LoadProgram(vertex_shader, fragment_shader);
211 uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix"); 246 uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix");
212 uniform_color_texture = glGetUniformLocation(program_id, "color_texture"); 247 uniform_color_texture = glGetUniformLocation(program_id, "color_texture");
213 attrib_position = glGetAttribLocation(program_id, "vert_position"); 248 attrib_position = glGetAttribLocation(program_id, "vert_position");