summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.travis-deps.sh4
-rw-r--r--dist/citra.icnsbin0 -> 1027012 bytes
-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
47 files changed, 1000 insertions, 732 deletions
diff --git a/.travis-deps.sh b/.travis-deps.sh
index 5c530dcb9..4b907abb3 100755
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -9,7 +9,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
export CXX=g++-4.9
mkdir -p $HOME/.local
- curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
+ curl -L http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
| tar -xz -C $HOME/.local --strip-components=1
(
@@ -25,6 +25,6 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update > /dev/null # silence the very verbose output
- brew install qt5 glfw3 pkgconfig
+ brew install qt5 glfw3
gem install xcpretty
fi
diff --git a/dist/citra.icns b/dist/citra.icns
new file mode 100644
index 000000000..9d3dcca83
--- /dev/null
+++ b/dist/citra.icns
Binary files differ
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
hotkeys.cpp
main.cpp
citra-qt.rc
+ Info.plist
)
set(HEADERS
@@ -71,7 +72,10 @@ else()
endif()
if (APPLE)
- add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS})
+ set(MACOSX_ICON "../../dist/citra.icns")
+ set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+ add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${MACOSX_ICON})
+ set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
else()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
endif()
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleGetInfoString</key>
+ <string></string>
+ <key>CFBundleIconFile</key>
+ <string>citra.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.citra-emu.citra</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleLongVersionString</key>
+ <string></string>
+ <key>CFBundleName</key>
+ <string>Citra</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string></string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string></string>
+ <key>CSResourcesFileMapped</key>
+ <true/>
+ <key>LSRequiresCarbon</key>
+ <true/>
+ <key>NSHumanReadableCopyright</key>
+ <string></string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>NSHighResolutionCapable</key>
+ <string>True</string>
+</dict>
+</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() {
void GRenderWindow::OnFramebufferSizeChanged()
{
// Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size
-#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
- // windowHandle() might not be accessible until the window is displayed to screen.
- auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0;
-
- unsigned width = child->QPaintDevice::width() * pixel_ratio;
- unsigned height = child->QPaintDevice::height() * pixel_ratio;
-#else
- unsigned width = child->QPaintDevice::width();
- unsigned height = child->QPaintDevice::height();
-#endif
+ qreal pixelRatio = windowPixelRatio();
+ unsigned width = child->QPaintDevice::width() * pixelRatio;
+ unsigned height = child->QPaintDevice::height() * pixelRatio;
NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
}
@@ -223,6 +216,16 @@ QByteArray GRenderWindow::saveGeometry()
return geometry;
}
+qreal GRenderWindow::windowPixelRatio()
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ // windowHandle() might not be accessible until the window is displayed to screen.
+ return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
+#else
+ return 1.0f;
+#endif
+}
+
void GRenderWindow::closeEvent(QCloseEvent* event) {
emit Closed();
QWidget::closeEvent(event);
@@ -243,14 +246,18 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event)
if (event->button() == Qt::LeftButton)
{
auto pos = event->pos();
- this->TouchPressed(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y()));
+ qreal pixelRatio = windowPixelRatio();
+ this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio),
+ static_cast<unsigned>(pos.y() * pixelRatio));
}
}
void GRenderWindow::mouseMoveEvent(QMouseEvent *event)
{
auto pos = event->pos();
- this->TouchMoved(static_cast<unsigned>(std::max(pos.x(), 0)), static_cast<unsigned>(std::max(pos.y(), 0)));
+ qreal pixelRatio = windowPixelRatio();
+ this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u),
+ std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
}
void 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:
void restoreGeometry(const QByteArray& geometry); // overridden
QByteArray saveGeometry(); // overridden
+ qreal windowPixelRatio();
+
void closeEvent(QCloseEvent* event) override;
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
{
// Highlight current instruction
int current_record_index = par->cycle_index->value();
- if (current_record_index < par->debug_data.records.size()) {
+ if (current_record_index < static_cast<int>(par->debug_data.records.size())) {
const auto& current_record = par->debug_data.records[current_record_index];
- if (index.row() == current_record.instruction_offset) {
+ if (index.row() == static_cast<int>(current_record.instruction_offset)) {
return QColor(255, 255, 63);
}
}
// Use a grey background for instructions which have no debug data associated to them
for (const auto& record : par->debug_data.records)
- if (index.row() == record.instruction_offset)
+ if (index.row() == static_cast<int>(record.instruction_offset))
return QVariant();
return QBrush(QColor(192, 192, 192));
@@ -494,7 +494,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
debug_data = Pica::Shader::ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup);
// Reload widget state
- for (unsigned int attr = 0; attr < num_attributes; ++attr) {
+ for (int attr = 0; attr < num_attributes; ++attr) {
unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr);
input_data_mapping[source_attr]->setText(QString("-> v%1").arg(attr));
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()
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
{
if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) {
- LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLatin1().data());
+ LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data());
return;
}
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)
}
void GMainWindow::BootGame(const std::string& filename) {
- LOG_INFO(Frontend, "Citra starting...\n");
+ LOG_INFO(Frontend, "Citra starting...");
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
@@ -362,7 +362,7 @@ void GMainWindow::UpdateRecentFiles() {
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
- BootGame(game_path.toLatin1().data());
+ BootGame(game_path.toLocal8Bit().data());
}
void GMainWindow::OnMenuLoadFile() {
@@ -374,7 +374,7 @@ void GMainWindow::OnMenuLoadFile() {
settings.setValue("romsPath", QFileInfo(filename).path());
StoreRecentFile(filename);
- BootGame(filename.toLatin1().data());
+ BootGame(filename.toLocal8Bit().data());
}
}
@@ -386,7 +386,7 @@ void GMainWindow::OnMenuLoadSymbolMap() {
if (!filename.isEmpty()) {
settings.setValue("symbolsPath", QFileInfo(filename).path());
- LoadSymbolMap(filename.toLatin1().data());
+ LoadSymbolMap(filename.toLocal8Bit().data());
}
}
@@ -407,7 +407,7 @@ void GMainWindow::OnMenuRecentFile() {
QString filename = action->data().toString();
QFileInfo file_info(filename);
if (file_info.exists()) {
- BootGame(filename.toLatin1().data());
+ BootGame(filename.toLocal8Bit().data());
StoreRecentFile(filename); // Put the filename on top of the list
} else {
// Display an error message and remove the file from the list.
@@ -450,6 +450,10 @@ void GMainWindow::OnOpenHotkeysDialog() {
void GMainWindow::SetHardwareRendererEnabled(bool enabled) {
VideoCore::g_hw_renderer_enabled = enabled;
+
+ Config config;
+ Settings::values.use_hw_renderer = enabled;
+ config.Save();
}
void GMainWindow::SetGdbstubEnabled(bool enabled) {
@@ -458,6 +462,10 @@ void GMainWindow::SetGdbstubEnabled(bool enabled) {
void GMainWindow::SetShaderJITEnabled(bool enabled) {
VideoCore::g_shader_jit_enabled = enabled;
+
+ Config config;
+ Settings::values.use_shader_jit = enabled;
+ config.Save();
}
void GMainWindow::ToggleWindowMode() {
@@ -469,6 +477,7 @@ void GMainWindow::ToggleWindowMode() {
if (emulation_running) {
render_window->setVisible(true);
render_window->setFocus();
+ game_list->hide();
}
} 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)
/* Miscellaneous functions */
s32 vfp_get_float(ARMul_State* state, unsigned int reg)
{
- LOG_TRACE(Core_ARM11, "VFP get float: s%d=[%08x]\n", reg, state->ExtReg[reg]);
+ LOG_TRACE(Core_ARM11, "VFP get float: s%d=[%08x]", reg, state->ExtReg[reg]);
return state->ExtReg[reg];
}
void vfp_put_float(ARMul_State* state, s32 val, unsigned int reg)
{
- LOG_TRACE(Core_ARM11, "VFP put float: s%d <= [%08x]\n", reg, val);
+ LOG_TRACE(Core_ARM11, "VFP put float: s%d <= [%08x]", reg, val);
state->ExtReg[reg] = val;
}
u64 vfp_get_double(ARMul_State* state, unsigned int reg)
{
u64 result = ((u64) state->ExtReg[reg*2+1])<<32 | state->ExtReg[reg*2];
- LOG_TRACE(Core_ARM11, "VFP get double: s[%d-%d]=[%016llx]\n", reg * 2 + 1, reg * 2, result);
+ LOG_TRACE(Core_ARM11, "VFP get double: s[%d-%d]=[%016llx]", reg * 2 + 1, reg * 2, result);
return result;
}
void vfp_put_double(ARMul_State* state, u64 val, unsigned int reg)
{
- LOG_TRACE(Core_ARM11, "VFP put double: s[%d-%d] <= [%08x-%08x]\n", reg * 2 + 1, reg * 2, (u32)(val >> 32), (u32)(val & 0xffffffff));
+ LOG_TRACE(Core_ARM11, "VFP put double: s[%d-%d] <= [%08x-%08x]", reg * 2 + 1, reg * 2, (u32)(val >> 32), (u32)(val & 0xffffffff));
state->ExtReg[reg*2] = (u32) (val & 0xffffffff);
state->ExtReg[reg*2+1] = (u32) (val>>32);
}
@@ -142,10 +142,10 @@ void vfp_put_double(ARMul_State* state, u64 val, unsigned int reg)
*/
void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x\n", exceptions);
+ LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x", exceptions);
if (exceptions == VFP_EXCEPTION_ERROR) {
- LOG_CRITICAL(Core_ARM11, "unhandled bounce %x\n", inst);
+ LOG_CRITICAL(Core_ARM11, "unhandled bounce %x", inst);
Crash();
}
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 @@
#include "core/arm/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
-#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested\n", __FUNCTION__);
+#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested", __FUNCTION__);
#define CHECK_VFP_ENABLED
#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]);
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 = {
static void vfp_double_dump(const char *str, struct vfp_double *d)
{
- LOG_TRACE(Core_ARM11, "VFP: %s: sign=%d exponent=%d significand=%016llx\n",
+ LOG_TRACE(Core_ARM11, "VFP: %s: sign=%d exponent=%d significand=%016llx",
str, d->sign != 0, d->exponent, d->significand);
}
@@ -155,7 +155,7 @@ u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd,
} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
- LOG_TRACE(Core_ARM11, "VFP: rounding increment = 0x%08llx\n", incr);
+ LOG_TRACE(Core_ARM11, "VFP: rounding increment = 0x%08llx", incr);
/*
* Is our rounding going to overflow?
@@ -210,7 +210,7 @@ pack:
vfp_double_dump("pack: final", vd);
{
s64 d = vfp_double_pack(vd);
- LOG_TRACE(Core_ARM11, "VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func,
+ LOG_TRACE(Core_ARM11, "VFP: %s: d(d%d)=%016llx exceptions=%08x", func,
dd, d, exceptions);
vfp_put_double(state, d, dd);
}
@@ -267,28 +267,28 @@ vfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn,
*/
static u32 vfp_double_fabs(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_put_double(state, vfp_double_packed_abs(vfp_get_double(state, dm)), dd);
return 0;
}
static u32 vfp_double_fcpy(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_put_double(state, vfp_get_double(state, dm), dd);
return 0;
}
static u32 vfp_double_fneg(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_put_double(state, vfp_double_packed_negate(vfp_get_double(state, dm)), dd);
return 0;
}
static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double vdm, vdd, *vdp;
int ret, tm;
@@ -383,7 +383,7 @@ static u32 vfp_compare(ARMul_State* state, int dd, int signal_on_qnan, int dm, u
s64 d, m;
u32 ret = 0;
- LOG_TRACE(Core_ARM11, "In %s, state=0x%p, fpscr=0x%x\n", __FUNCTION__, state, fpscr);
+ LOG_TRACE(Core_ARM11, "In %s, state=0x%p, fpscr=0x%x", __FUNCTION__, state, fpscr);
m = vfp_get_double(state, dm);
if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) {
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
ret |= FPSCR_CFLAG;
}
}
- LOG_TRACE(Core_ARM11, "In %s, state=0x%p, ret=0x%x\n", __FUNCTION__, state, ret);
+ LOG_TRACE(Core_ARM11, "In %s, state=0x%p, ret=0x%x", __FUNCTION__, state, ret);
return ret;
}
static u32 vfp_double_fcmp(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_compare(state, dd, 0, dm, fpscr);
}
static u32 vfp_double_fcmpe(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_compare(state, dd, 1, dm, fpscr);
}
static u32 vfp_double_fcmpz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_compare(state, dd, 0, VFP_REG_ZERO, fpscr);
}
static u32 vfp_double_fcmpez(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_compare(state, dd, 1, VFP_REG_ZERO, fpscr);
}
@@ -474,7 +474,7 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32
int tm;
u32 exceptions = 0;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
tm = vfp_double_type(&vdm);
@@ -516,7 +516,7 @@ static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32
struct vfp_double vdm;
u32 m = vfp_get_float(state, dm);
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vdm.sign = 0;
vdm.exponent = 1023 + 63 - 1;
vdm.significand = (u64)m;
@@ -529,7 +529,7 @@ static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32
struct vfp_double vdm;
u32 m = vfp_get_float(state, dm);
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vdm.sign = (m & 0x80000000) >> 16;
vdm.exponent = 1023 + 63 - 1;
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
int rmode = fpscr & FPSCR_RMODE_MASK;
int tm;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
/*
@@ -605,7 +605,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
}
}
- LOG_TRACE(Core_ARM11, "VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+ LOG_TRACE(Core_ARM11, "VFP: ftoui: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
vfp_put_float(state, d, sd);
@@ -614,7 +614,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_ftoui(state, sd, unused, dm, FPSCR_ROUND_TOZERO);
}
@@ -625,7 +625,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
int rmode = fpscr & FPSCR_RMODE_MASK;
int tm;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
vfp_double_dump("VDM", &vdm);
@@ -682,7 +682,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
}
}
- LOG_TRACE(Core_ARM11, "VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+ LOG_TRACE(Core_ARM11, "VFP: ftosi: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
vfp_put_float(state, (s32)d, sd);
@@ -691,7 +691,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_ftosi(state, dd, unused, dm, FPSCR_ROUND_TOZERO);
}
@@ -775,7 +775,7 @@ u32 vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,struct vfp_dou
if (vdn->significand & (1ULL << 63) ||
vdm->significand & (1ULL << 63)) {
- LOG_INFO(Core_ARM11, "VFP: bad FP values in %s\n", __func__);
+ LOG_INFO(Core_ARM11, "VFP: bad FP values in %s", __func__);
vfp_double_dump("VDN", vdn);
vfp_double_dump("VDM", vdm);
}
@@ -843,7 +843,7 @@ vfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn,
*/
if (vdn->exponent < vdm->exponent) {
std::swap(vdm, vdn);
- LOG_TRACE(Core_ARM11, "VFP: swapping M <-> N\n");
+ LOG_TRACE(Core_ARM11, "VFP: swapping M <-> N");
}
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
*/
static u32 vfp_double_fmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, 0, "fmac");
}
@@ -936,7 +936,7 @@ static u32 vfp_double_fmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
*/
static u32 vfp_double_fnmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac");
}
@@ -945,7 +945,7 @@ static u32 vfp_double_fnmac(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
*/
static u32 vfp_double_fmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc");
}
@@ -954,7 +954,7 @@ static u32 vfp_double_fmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
*/
static u32 vfp_double_fnmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
}
@@ -966,7 +966,7 @@ static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
struct vfp_double vdd, vdn, vdm;
u32 exceptions;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
if (vdn.exponent == 0 && vdn.significand)
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
struct vfp_double vdd, vdn, vdm;
u32 exceptions;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
if (vdn.exponent == 0 && vdn.significand)
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
struct vfp_double vdd, vdn, vdm;
u32 exceptions;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
if (vdn.exponent == 0 && vdn.significand)
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
struct vfp_double vdd, vdn, vdm;
u32 exceptions;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
if (vdn.exponent == 0 && vdn.significand)
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
u32 exceptions = 0;
int tm, tn;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
@@ -1185,7 +1185,7 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
unsigned int vecitr, veclen, vecstride;
struct op *fop;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK));
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)
else
veclen = fpscr & FPSCR_LENGTH_MASK;
- LOG_TRACE(Core_ARM11, "VFP: vecstride=%u veclen=%u\n", vecstride,
+ LOG_TRACE(Core_ARM11, "VFP: vecstride=%u veclen=%u", vecstride,
(veclen >> FPSCR_LENGTH_BIT) + 1);
if (!fop->fn) {
@@ -1230,16 +1230,16 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
type = (fop->flags & OP_SD) ? 's' : 'd';
if (op == FOP_EXT)
- LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = op[%u] (d%u)\n",
+ LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = op[%u] (d%u)",
vecitr >> FPSCR_LENGTH_BIT,
type, dest, dn, dm);
else
- LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n",
+ LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)",
vecitr >> FPSCR_LENGTH_BIT,
type, dest, dn, FOP_TO_IDX(op), dm);
except = fop->fn(state, dest, dn, dm, fpscr);
- LOG_TRACE(Core_ARM11, "VFP: itr%d: exceptions=%08x\n",
+ LOG_TRACE(Core_ARM11, "VFP: itr%d: exceptions=%08x",
vecitr >> FPSCR_LENGTH_BIT, except);
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:
* @return Opened directory, or nullptr
*/
virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
+
+ /**
+ * Get the free space
+ * @return The number of free bytes in the archive
+ */
+ virtual u64 GetFreeBytes() const = 0;
};
class 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
return std::move(directory);
}
+u64 DiskArchive::GetFreeBytes() const {
+ // TODO: Stubbed to return 1GiB
+ return 1024 * 1024 * 1024;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
DiskFile::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:
bool CreateDirectory(const Path& path) const override;
bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
+ u64 GetFreeBytes() const override;
protected:
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
return Common::make_unique<IVFCDirectory>();
}
+u64 IVFCArchive::GetFreeBytes() const {
+ LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive");
+ return 0;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
size_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:
bool CreateDirectory(const Path& path) const override;
bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
+ u64 GetFreeBytes() const override;
protected:
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) {
// Clean up the thread's wait_objects, they'll be restored if needed during
// the svcWaitSynchronization call
- for (int i = 0; i < new_thread->wait_objects.size(); ++i) {
+ for (size_t i = 0; i < new_thread->wait_objects.size(); ++i) {
SharedPtr<WaitObject> object = new_thread->wait_objects[i];
object->RemoveWaitingThread(new_thread);
}
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 @@
// Refer to the license.txt file included.
#include "core/hle/hle.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/csnd_snd.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,11 +13,11 @@
namespace CSND_SND {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010140, nullptr, "Initialize"},
- {0x00020000, nullptr, "Shutdown"},
- {0x00030040, nullptr, "ExecuteType0Commands"},
+ {0x00010140, Initialize, "Initialize"},
+ {0x00020000, Shutdown, "Shutdown"},
+ {0x00030040, ExecuteType0Commands, "ExecuteType0Commands"},
{0x00040080, nullptr, "ExecuteType1Commands"},
- {0x00050000, nullptr, "AcquireSoundChannels"},
+ {0x00050000, AcquireSoundChannels, "AcquireSoundChannels"},
{0x00060000, nullptr, "ReleaseSoundChannels"},
{0x00070000, nullptr, "AcquireCaptureDevice"},
{0x00080040, nullptr, "ReleaseCaptureDevice"},
@@ -31,4 +33,51 @@ Interface::Interface() {
Register(FunctionTable);
}
+static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
+static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr;
+
+void Initialize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ shared_memory = Kernel::SharedMemory::Create(cmd_buff[1],
+ Kernel::MemoryPermission::ReadWrite,
+ Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem");
+
+ mutex = Kernel::Mutex::Create(false);
+
+ cmd_buff[1] = 0;
+ cmd_buff[2] = 0x4000000;
+ cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom();
+ cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom();
+}
+
+void ExecuteType0Commands(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ if (shared_memory != nullptr) {
+ struct Type0Command* command = reinterpret_cast<struct Type0Command*>(
+ shared_memory->GetPointer(cmd_buff[1]));
+ if (command == nullptr) {
+ cmd_buff[1] = 1;
+ }else{
+ LOG_WARNING(Service, "(STUBBED) CSND_SND::ExecuteType0Commands");
+ command->finished |= 1;
+ cmd_buff[1] = 0;
+ }
+ }else{
+ cmd_buff[1] = 1;
+ }
+}
+
+void AcquireSoundChannels(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = 0;
+ cmd_buff[2] = 0xFFFFFF00;
+}
+
+void Shutdown(Service::Interface* self) {
+ shared_memory = nullptr;
+ mutex = nullptr;
+}
+
} // 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:
}
};
+struct Type0Command {
+ // command id and next command offset
+ u32 command_id;
+ u32 finished;
+ u32 flags;
+ u8 parameters[20];
+};
+
+void Initialize(Service::Interface* self);
+void ExecuteType0Commands(Service::Interface* self);
+void AcquireSoundChannels(Service::Interface* self);
+void Shutdown(Service::Interface* self);
+
} // 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
return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory));
}
+ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
+ ArchiveBackend* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return ERR_INVALID_HANDLE;
+ return MakeResult<u64>(archive->GetFreeBytes());
+}
+
ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) {
auto archive_itr = id_code_map.find(id_code);
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
const FileSys::Path& path);
/**
+ * Get the free space in an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @return The number of free bytes in the archive
+ */
+ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle);
+
+/**
* Erases the contents of the physical folder that contains the archive
* identified by the specified id code and path
* @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) {
}
/**
+ * FS_User::GetFreeBytes service function
+ * Inputs:
+ * 0: 0x08120080
+ * 1: Archive handle low word
+ * 2: Archive handle high word
+ * Outputs:
+ * 1: Result of function, 0 on success, otherwise error code
+ * 2: Free byte count low word
+ * 3: Free byte count high word
+ */
+static void GetFreeBytes(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
+ ResultVal<u64> bytes_res = GetFreeBytesInArchive(archive_handle);
+
+ cmd_buff[1] = bytes_res.Code().raw;
+ if (bytes_res.Succeeded()) {
+ cmd_buff[2] = (u32)*bytes_res;
+ cmd_buff[3] = *bytes_res >> 32;
+ } else {
+ cmd_buff[2] = 0;
+ cmd_buff[3] = 0;
+ }
+}
+
+/**
* FS_User::CreateExtSaveData service function
* Inputs:
* 0 : 0x08510242
@@ -700,7 +727,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
{0x08100200, nullptr, "CreateSystemSaveData"},
{0x08110040, nullptr, "DeleteSystemSaveData"},
- {0x08120080, nullptr, "GetFreeBytes"},
+ {0x08120080, GetFreeBytes, "GetFreeBytes"},
{0x08130000, nullptr, "GetCardType"},
{0x08140000, nullptr, "GetSdmcArchiveResource"},
{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) {
LOG_WARNING(Service_PTM, "(STUBBED) called");
}
+void GetTotalStepCount(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO: This function is only a stub,
+ // it returns 0 as the total step count
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
void IsLegacyPowerOff(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
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);
void GetBatteryChargeState(Interface* self);
/**
+ * PTM::GetTotalStepCount service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output of function, * = total step count
+ */
+void GetTotalStepCount(Interface* self);
+
+/**
* PTM::IsLegacyPowerOff service function
* Outputs:
* 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[] = {
{0x00090000, nullptr, "GetPedometerState"},
{0x000A0042, nullptr, "GetStepHistoryEntry"},
{0x000B00C2, nullptr, "GetStepHistory"},
- {0x000C0000, nullptr, "GetTotalStepCount"},
+ {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
{0x000D0040, nullptr, "SetPedometerRecordingMode"},
{0x000E0000, nullptr, "GetPedometerRecordingMode"},
{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) {
u32* output_buffer = reinterpret_cast<u32*>(data_buffer.get());
- for (int i = 0; i < num_tiles; ++i) {
+ for (size_t i = 0; i < num_tiles; ++i) {
int image_strip_width = 0;
int output_stride = 0;
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
for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
const auto& table = reloc_table[current_inprogress];
- LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", current_segment_reloc_table,
+ LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)", current_segment_reloc_table,
(u32)table.skip, (u32)table.patch);
pos += table.skip;
s32 num_patches = table.patch;
while (0 < num_patches && pos < end_pos) {
u32 in_addr = (u8*)pos - program_image.data();
u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
- LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n",
+ LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)",
base_addr + in_addr, addr, current_segment_reloc_table, *pos);
switch (current_segment_reloc_table) {
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 {
ErrorNotUsed,
ErrorAlreadyLoaded,
ErrorMemoryAllocationFailed,
+ ErrorEncrypted,
};
static 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() {
if (ResultStatus::Success == ReadCode(code)) {
std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
(const char*)exheader_header.codeset_info.name, 8);
- u64 program_id = *reinterpret_cast<u64_le const*>(&ncch_header.program_id[0]);
- SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id);
+ SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, ncch_header.program_id);
codeset->code.offset = 0;
codeset->code.addr = exheader_header.codeset_info.text.address;
@@ -266,6 +265,11 @@ ResultStatus AppLoader_NCCH::Load() {
LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority);
LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category);
+ if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
+ LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
+ return ResultStatus::ErrorEncrypted;
+ }
+
// Read ExeFS...
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 @@
struct NCCH_Header {
u8 signature[0x100];
- u32 magic;
- u32 content_size;
+ u32_le magic;
+ u32_le content_size;
u8 partition_id[8];
- u16 maker_code;
- u16 version;
+ u16_le maker_code;
+ u16_le version;
u8 reserved_0[4];
- u8 program_id[8];
+ u64_le program_id;
u8 reserved_1[0x10];
u8 logo_region_hash[0x20];
u8 product_code[0x10];
u8 extended_header_hash[0x20];
- u32 extended_header_size;
+ u32_le extended_header_size;
u8 reserved_2[4];
u8 flags[8];
- u32 plain_region_offset;
- u32 plain_region_size;
- u32 logo_region_offset;
- u32 logo_region_size;
- u32 exefs_offset;
- u32 exefs_size;
- u32 exefs_hash_region_size;
+ u32_le plain_region_offset;
+ u32_le plain_region_size;
+ u32_le logo_region_offset;
+ u32_le logo_region_size;
+ u32_le exefs_offset;
+ u32_le exefs_size;
+ u32_le exefs_hash_region_size;
u8 reserved_3[4];
- u32 romfs_offset;
- u32 romfs_size;
- u32 romfs_hash_region_size;
+ u32_le romfs_offset;
+ u32_le romfs_size;
+ u32_le romfs_hash_region_size;
u8 reserved_4[4];
u8 exefs_super_block_hash[0x20];
u8 romfs_super_block_hash[0x20];
@@ -109,8 +109,8 @@ struct ExHeader_StorageInfo {
};
struct ExHeader_ARM11_SystemLocalCaps {
- u8 program_id[8];
- u32 core_version;
+ u64_le program_id;
+ u32_le core_version;
u8 reserved_flags[2];
union {
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 {
};
/**
- * A (reasonably) fast way of allowing switchable and remmapable process address spaces. It loosely
+ * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
* mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
- * fetching requirements when acessing. In the usual case of an access to regular memory, it only
+ * fetching requirements when accessing. In the usual case of an access to regular memory, it only
* requires an indexed fetch and a check for NULL.
*/
struct 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 @@
set(SRCS
renderer_opengl/gl_rasterizer.cpp
renderer_opengl/gl_rasterizer_cache.cpp
+ renderer_opengl/gl_shader_gen.cpp
renderer_opengl/gl_shader_util.cpp
renderer_opengl/gl_state.cpp
renderer_opengl/renderer_opengl.cpp
@@ -21,8 +22,8 @@ set(HEADERS
renderer_opengl/gl_rasterizer.h
renderer_opengl/gl_rasterizer_cache.h
renderer_opengl/gl_resource_manager.h
+ renderer_opengl/gl_shader_gen.h
renderer_opengl/gl_shader_util.h
- renderer_opengl/gl_shaders.h
renderer_opengl/gl_state.h
renderer_opengl/pica_to_gl.h
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) {
// TODO: What happens if a loader overwrites a previous one's data?
for (unsigned component = 0; component < loader_config.component_count; ++component) {
+ if (component >= 12)
+ LOG_ERROR(HW_GPU, "Overflow in the vertex attribute loader %u trying to load component %u", loader, component);
u32 attribute_index = loader_config.GetComponent(component);
vertex_attribute_sources[attribute_index] = load_address;
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) {
// Initialize write structure
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (png_ptr == nullptr) {
- LOG_ERROR(Debug_GPU, "Could not allocate write struct\n");
+ LOG_ERROR(Debug_GPU, "Could not allocate write struct");
goto finalise;
}
@@ -649,13 +649,13 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
// Initialize info structure
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr) {
- LOG_ERROR(Debug_GPU, "Could not allocate info struct\n");
+ LOG_ERROR(Debug_GPU, "Could not allocate info struct");
goto finalise;
}
// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
- LOG_ERROR(Debug_GPU, "Error during png creation\n");
+ LOG_ERROR(Debug_GPU, "Error during png creation");
goto finalise;
}
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 {
};
union {
+ u32 sources_raw;
BitField< 0, 4, Source> color_source1;
BitField< 4, 4, Source> color_source2;
BitField< 8, 4, Source> color_source3;
@@ -326,6 +327,7 @@ struct Regs {
};
union {
+ u32 modifiers_raw;
BitField< 0, 4, ColorModifier> color_modifier1;
BitField< 4, 4, ColorModifier> color_modifier2;
BitField< 8, 4, ColorModifier> color_modifier3;
@@ -335,6 +337,7 @@ struct Regs {
};
union {
+ u32 ops_raw;
BitField< 0, 4, Operation> color_op;
BitField<16, 4, Operation> alpha_op;
};
@@ -348,6 +351,7 @@ struct Regs {
};
union {
+ u32 scales_raw;
BitField< 0, 2, u32> color_scale;
BitField<16, 2, u32> alpha_scale;
};
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,
}
default:
- LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x\n", (int)mode);
+ LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x", (int)mode);
UNIMPLEMENTED();
return 0;
}
@@ -541,7 +541,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
return combiner_output;
default:
- LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source);
+ LOG_ERROR(HW_GPU, "Unknown color combiner source %d", (int)source);
UNIMPLEMENTED();
return {0, 0, 0, 0};
}
@@ -679,7 +679,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
return { (u8)result, (u8)result, (u8)result };
}
default:
- LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op);
+ LOG_ERROR(HW_GPU, "Unknown color combiner operation %d", (int)op);
UNIMPLEMENTED();
return {0, 0, 0};
}
@@ -716,7 +716,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
default:
- LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op);
+ LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d", (int)op);
UNIMPLEMENTED();
return 0;
}
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 @@
#include <glad/glad.h>
#include "common/color.h"
+#include "common/file_util.h"
+#include "common/make_unique.h"
#include "common/math_util.h"
#include "common/microprofile.h"
#include "common/profiler.h"
@@ -19,7 +21,7 @@
#include "video_core/pica.h"
#include "video_core/utils.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
-#include "video_core/renderer_opengl/gl_shaders.h"
+#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/pica_to_gl.h"
@@ -38,38 +40,8 @@ RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr
RasterizerOpenGL::~RasterizerOpenGL() { }
void RasterizerOpenGL::InitObjects() {
- // Create the hardware shader program and get attrib/uniform locations
- shader.Create(GLShaders::g_vertex_shader_hw, GLShaders::g_fragment_shader_hw);
- attrib_position = glGetAttribLocation(shader.handle, "vert_position");
- attrib_color = glGetAttribLocation(shader.handle, "vert_color");
- attrib_texcoords = glGetAttribLocation(shader.handle, "vert_texcoords");
-
- uniform_alphatest_enabled = glGetUniformLocation(shader.handle, "alphatest_enabled");
- uniform_alphatest_func = glGetUniformLocation(shader.handle, "alphatest_func");
- uniform_alphatest_ref = glGetUniformLocation(shader.handle, "alphatest_ref");
-
- uniform_tex = glGetUniformLocation(shader.handle, "tex");
-
- uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.handle, "tev_combiner_buffer_color");
-
- const auto tev_stages = Pica::g_state.regs.GetTevStages();
- for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
- auto& uniform_tev_cfg = uniform_tev_cfgs[tev_stage_index];
-
- std::string tev_ref_str = "tev_cfgs[" + std::to_string(tev_stage_index) + "]";
- uniform_tev_cfg.enabled = glGetUniformLocation(shader.handle, (tev_ref_str + ".enabled").c_str());
- uniform_tev_cfg.color_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_sources").c_str());
- uniform_tev_cfg.alpha_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_sources").c_str());
- uniform_tev_cfg.color_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_modifiers").c_str());
- uniform_tev_cfg.alpha_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_modifiers").c_str());
- uniform_tev_cfg.color_alpha_op = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_op").c_str());
- uniform_tev_cfg.color_alpha_multiplier = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_multiplier").c_str());
- uniform_tev_cfg.const_color = glGetUniformLocation(shader.handle, (tev_ref_str + ".const_color").c_str());
- uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str());
- }
-
// Create sampler objects
- for (int i = 0; i < texture_samplers.size(); ++i) {
+ for (size_t i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create();
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
}
@@ -78,29 +50,25 @@ void RasterizerOpenGL::InitObjects() {
vertex_buffer.Create();
vertex_array.Create();
- // Update OpenGL state
state.draw.vertex_array = vertex_array.handle;
state.draw.vertex_buffer = vertex_buffer.handle;
- state.draw.shader_program = shader.handle;
-
state.Apply();
- // Set the texture samplers to correspond to different texture units
- glUniform1i(uniform_tex, 0);
- glUniform1i(uniform_tex + 1, 1);
- glUniform1i(uniform_tex + 2, 2);
-
// Set vertex attributes
- glVertexAttribPointer(attrib_position, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
- glVertexAttribPointer(attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
- glVertexAttribPointer(attrib_texcoords, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
- glVertexAttribPointer(attrib_texcoords + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
- glVertexAttribPointer(attrib_texcoords + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
- glEnableVertexAttribArray(attrib_position);
- glEnableVertexAttribArray(attrib_color);
- glEnableVertexAttribArray(attrib_texcoords);
- glEnableVertexAttribArray(attrib_texcoords + 1);
- glEnableVertexAttribArray(attrib_texcoords + 2);
+ glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION);
+
+ glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR);
+
+ glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
+ glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
+ glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD0);
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1);
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2);
+
+ SetShader();
// Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
fb_color_texture.texture.Create();
@@ -150,61 +118,15 @@ void RasterizerOpenGL::InitObjects() {
}
void RasterizerOpenGL::Reset() {
- const auto& regs = Pica::g_state.regs;
-
SyncCullMode();
SyncBlendEnabled();
SyncBlendFuncs();
SyncBlendColor();
- SyncAlphaTest();
SyncLogicOp();
SyncStencilTest();
SyncDepthTest();
- // TEV stage 0
- SyncTevSources(0, regs.tev_stage0);
- SyncTevModifiers(0, regs.tev_stage0);
- SyncTevOps(0, regs.tev_stage0);
- SyncTevColor(0, regs.tev_stage0);
- SyncTevMultipliers(0, regs.tev_stage0);
-
- // TEV stage 1
- SyncTevSources(1, regs.tev_stage1);
- SyncTevModifiers(1, regs.tev_stage1);
- SyncTevOps(1, regs.tev_stage1);
- SyncTevColor(1, regs.tev_stage1);
- SyncTevMultipliers(1, regs.tev_stage1);
-
- // TEV stage 2
- SyncTevSources(2, regs.tev_stage2);
- SyncTevModifiers(2, regs.tev_stage2);
- SyncTevOps(2, regs.tev_stage2);
- SyncTevColor(2, regs.tev_stage2);
- SyncTevMultipliers(2, regs.tev_stage2);
-
- // TEV stage 3
- SyncTevSources(3, regs.tev_stage3);
- SyncTevModifiers(3, regs.tev_stage3);
- SyncTevOps(3, regs.tev_stage3);
- SyncTevColor(3, regs.tev_stage3);
- SyncTevMultipliers(3, regs.tev_stage3);
-
- // TEV stage 4
- SyncTevSources(4, regs.tev_stage4);
- SyncTevModifiers(4, regs.tev_stage4);
- SyncTevOps(4, regs.tev_stage4);
- SyncTevColor(4, regs.tev_stage4);
- SyncTevMultipliers(4, regs.tev_stage4);
-
- // TEV stage 5
- SyncTevSources(5, regs.tev_stage5);
- SyncTevModifiers(5, regs.tev_stage5);
- SyncTevOps(5, regs.tev_stage5);
- SyncTevColor(5, regs.tev_stage5);
- SyncTevMultipliers(5, regs.tev_stage5);
-
- SyncCombinerColor();
- SyncCombinerWriteFlags();
+ SetShader();
res_cache.FullFlush();
}
@@ -221,6 +143,11 @@ void RasterizerOpenGL::DrawTriangles() {
SyncFramebuffer();
SyncDrawState();
+ if (state.draw.shader_dirty) {
+ SetShader();
+ state.draw.shader_dirty = false;
+ }
+
glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
@@ -272,6 +199,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
// Alpha test
case PICA_REG_INDEX(output_merger.alpha_test):
SyncAlphaTest();
+ state.draw.shader_dirty = true;
break;
// Stencil test
@@ -290,117 +218,57 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
SyncLogicOp();
break;
- // TEV stage 0
+ // TEV stages
case PICA_REG_INDEX(tev_stage0.color_source1):
- SyncTevSources(0, regs.tev_stage0);
- break;
case PICA_REG_INDEX(tev_stage0.color_modifier1):
- SyncTevModifiers(0, regs.tev_stage0);
- break;
case PICA_REG_INDEX(tev_stage0.color_op):
- SyncTevOps(0, regs.tev_stage0);
- break;
- case PICA_REG_INDEX(tev_stage0.const_r):
- SyncTevColor(0, regs.tev_stage0);
- break;
case PICA_REG_INDEX(tev_stage0.color_scale):
- SyncTevMultipliers(0, regs.tev_stage0);
- break;
-
- // TEV stage 1
case PICA_REG_INDEX(tev_stage1.color_source1):
- SyncTevSources(1, regs.tev_stage1);
- break;
case PICA_REG_INDEX(tev_stage1.color_modifier1):
- SyncTevModifiers(1, regs.tev_stage1);
- break;
case PICA_REG_INDEX(tev_stage1.color_op):
- SyncTevOps(1, regs.tev_stage1);
- break;
- case PICA_REG_INDEX(tev_stage1.const_r):
- SyncTevColor(1, regs.tev_stage1);
- break;
case PICA_REG_INDEX(tev_stage1.color_scale):
- SyncTevMultipliers(1, regs.tev_stage1);
- break;
-
- // TEV stage 2
case PICA_REG_INDEX(tev_stage2.color_source1):
- SyncTevSources(2, regs.tev_stage2);
- break;
case PICA_REG_INDEX(tev_stage2.color_modifier1):
- SyncTevModifiers(2, regs.tev_stage2);
- break;
case PICA_REG_INDEX(tev_stage2.color_op):
- SyncTevOps(2, regs.tev_stage2);
- break;
- case PICA_REG_INDEX(tev_stage2.const_r):
- SyncTevColor(2, regs.tev_stage2);
- break;
case PICA_REG_INDEX(tev_stage2.color_scale):
- SyncTevMultipliers(2, regs.tev_stage2);
- break;
-
- // TEV stage 3
case PICA_REG_INDEX(tev_stage3.color_source1):
- SyncTevSources(3, regs.tev_stage3);
- break;
case PICA_REG_INDEX(tev_stage3.color_modifier1):
- SyncTevModifiers(3, regs.tev_stage3);
- break;
case PICA_REG_INDEX(tev_stage3.color_op):
- SyncTevOps(3, regs.tev_stage3);
- break;
- case PICA_REG_INDEX(tev_stage3.const_r):
- SyncTevColor(3, regs.tev_stage3);
- break;
case PICA_REG_INDEX(tev_stage3.color_scale):
- SyncTevMultipliers(3, regs.tev_stage3);
- break;
-
- // TEV stage 4
case PICA_REG_INDEX(tev_stage4.color_source1):
- SyncTevSources(4, regs.tev_stage4);
- break;
case PICA_REG_INDEX(tev_stage4.color_modifier1):
- SyncTevModifiers(4, regs.tev_stage4);
- break;
case PICA_REG_INDEX(tev_stage4.color_op):
- SyncTevOps(4, regs.tev_stage4);
+ case PICA_REG_INDEX(tev_stage4.color_scale):
+ case PICA_REG_INDEX(tev_stage5.color_source1):
+ case PICA_REG_INDEX(tev_stage5.color_modifier1):
+ case PICA_REG_INDEX(tev_stage5.color_op):
+ case PICA_REG_INDEX(tev_stage5.color_scale):
+ case PICA_REG_INDEX(tev_combiner_buffer_input):
+ state.draw.shader_dirty = true;
break;
- case PICA_REG_INDEX(tev_stage4.const_r):
- SyncTevColor(4, regs.tev_stage4);
+ case PICA_REG_INDEX(tev_stage0.const_r):
+ SyncTevConstColor(0, regs.tev_stage0);
break;
- case PICA_REG_INDEX(tev_stage4.color_scale):
- SyncTevMultipliers(4, regs.tev_stage4);
+ case PICA_REG_INDEX(tev_stage1.const_r):
+ SyncTevConstColor(1, regs.tev_stage1);
break;
-
- // TEV stage 5
- case PICA_REG_INDEX(tev_stage5.color_source1):
- SyncTevSources(5, regs.tev_stage5);
+ case PICA_REG_INDEX(tev_stage2.const_r):
+ SyncTevConstColor(2, regs.tev_stage2);
break;
- case PICA_REG_INDEX(tev_stage5.color_modifier1):
- SyncTevModifiers(5, regs.tev_stage5);
+ case PICA_REG_INDEX(tev_stage3.const_r):
+ SyncTevConstColor(3, regs.tev_stage3);
break;
- case PICA_REG_INDEX(tev_stage5.color_op):
- SyncTevOps(5, regs.tev_stage5);
+ case PICA_REG_INDEX(tev_stage4.const_r):
+ SyncTevConstColor(4, regs.tev_stage4);
break;
case PICA_REG_INDEX(tev_stage5.const_r):
- SyncTevColor(5, regs.tev_stage5);
- break;
- case PICA_REG_INDEX(tev_stage5.color_scale):
- SyncTevMultipliers(5, regs.tev_stage5);
+ SyncTevConstColor(5, regs.tev_stage5);
break;
// TEV combiner buffer color
case PICA_REG_INDEX(tev_combiner_buffer_color):
SyncCombinerColor();
break;
-
- // TEV combiner buffer write flags
- case PICA_REG_INDEX(tev_combiner_buffer_input):
- SyncCombinerWriteFlags();
- break;
}
}
@@ -592,6 +460,41 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::
state.Apply();
}
+void RasterizerOpenGL::SetShader() {
+ PicaShaderConfig config = PicaShaderConfig::CurrentConfig();
+ std::unique_ptr<PicaShader> shader = Common::make_unique<PicaShader>();
+
+ // Find (or generate) the GLSL shader for the current TEV state
+ auto cached_shader = shader_cache.find(config);
+ if (cached_shader != shader_cache.end()) {
+ current_shader = cached_shader->second.get();
+
+ state.draw.shader_program = current_shader->shader.handle;
+ state.Apply();
+ } else {
+ LOG_DEBUG(Render_OpenGL, "Creating new shader");
+
+ shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str());
+
+ state.draw.shader_program = shader->shader.handle;
+ state.Apply();
+
+ // Set the texture samplers to correspond to different texture units
+ glUniform1i(PicaShader::Uniform::Texture0, 0);
+ glUniform1i(PicaShader::Uniform::Texture1, 1);
+ glUniform1i(PicaShader::Uniform::Texture2, 2);
+
+ current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
+ }
+
+ // Update uniforms
+ SyncAlphaTest();
+ SyncCombinerColor();
+ auto& tev_stages = Pica::g_state.regs.GetTevStages();
+ for (int index = 0; index < tev_stages.size(); ++index)
+ SyncTevConstColor(index, tev_stages[index]);
+}
+
void RasterizerOpenGL::SyncFramebuffer() {
const auto& regs = Pica::g_state.regs;
@@ -601,8 +504,8 @@ void RasterizerOpenGL::SyncFramebuffer() {
PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
Pica::Regs::DepthFormat new_fb_depth_format = regs.framebuffer.depth_format;
- bool fb_size_changed = fb_color_texture.width != regs.framebuffer.GetWidth() ||
- fb_color_texture.height != regs.framebuffer.GetHeight();
+ bool fb_size_changed = fb_color_texture.width != static_cast<GLsizei>(regs.framebuffer.GetWidth()) ||
+ fb_color_texture.height != static_cast<GLsizei>(regs.framebuffer.GetHeight());
bool color_fb_prop_changed = fb_color_texture.format != new_fb_color_format ||
fb_size_changed;
@@ -712,9 +615,7 @@ void RasterizerOpenGL::SyncBlendColor() {
void RasterizerOpenGL::SyncAlphaTest() {
const auto& regs = Pica::g_state.regs;
- glUniform1i(uniform_alphatest_enabled, regs.output_merger.alpha_test.enable);
- glUniform1i(uniform_alphatest_func, (GLint)regs.output_merger.alpha_test.func.Value());
- glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f);
+ glUniform1i(PicaShader::Uniform::AlphaTestRef, regs.output_merger.alpha_test.ref);
}
void RasterizerOpenGL::SyncLogicOp() {
@@ -744,56 +645,14 @@ void RasterizerOpenGL::SyncDepthTest() {
state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE;
}
-void RasterizerOpenGL::SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- GLint color_srcs[3] = { (GLint)config.color_source1.Value(),
- (GLint)config.color_source2.Value(),
- (GLint)config.color_source3.Value() };
- GLint alpha_srcs[3] = { (GLint)config.alpha_source1.Value(),
- (GLint)config.alpha_source2.Value(),
- (GLint)config.alpha_source3.Value() };
-
- glUniform3iv(uniform_tev_cfgs[stage_index].color_sources, 1, color_srcs);
- glUniform3iv(uniform_tev_cfgs[stage_index].alpha_sources, 1, alpha_srcs);
-}
-
-void RasterizerOpenGL::SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- GLint color_mods[3] = { (GLint)config.color_modifier1.Value(),
- (GLint)config.color_modifier2.Value(),
- (GLint)config.color_modifier3.Value() };
- GLint alpha_mods[3] = { (GLint)config.alpha_modifier1.Value(),
- (GLint)config.alpha_modifier2.Value(),
- (GLint)config.alpha_modifier3.Value() };
-
- glUniform3iv(uniform_tev_cfgs[stage_index].color_modifiers, 1, color_mods);
- glUniform3iv(uniform_tev_cfgs[stage_index].alpha_modifiers, 1, alpha_mods);
-}
-
-void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_op, (GLint)config.color_op.Value(), (GLint)config.alpha_op.Value());
-}
-
-void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- auto const_color = PicaToGL::ColorRGBA8(config.const_color);
- glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data());
-}
-
-void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_multiplier, config.GetColorMultiplier(), config.GetAlphaMultiplier());
-}
-
void RasterizerOpenGL::SyncCombinerColor() {
auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
- glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data());
+ glUniform4fv(PicaShader::Uniform::TevCombinerBufferColor, 1, combiner_color.data());
}
-void RasterizerOpenGL::SyncCombinerWriteFlags() {
- const auto& regs = Pica::g_state.regs;
- const auto tev_stages = regs.GetTevStages();
- for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
- glUniform2i(uniform_tev_cfgs[tev_stage_index].updates_combiner_buffer_color_alpha,
- regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index),
- regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index));
- }
+void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevStageConfig& tev_stage) {
+ auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color);
+ glUniform4fv(PicaShader::Uniform::TevConstColors + stage_index, 1, const_color.data());
}
void RasterizerOpenGL::SyncDrawState() {
@@ -824,12 +683,6 @@ void RasterizerOpenGL::SyncDrawState() {
}
}
- // Skip processing TEV stages that simply pass the previous stage results through
- const auto tev_stages = regs.GetTevStages();
- for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
- glUniform1i(uniform_tev_cfgs[tev_stage_index].enabled, !IsPassThroughTevStage(tev_stages[tev_stage_index]));
- }
-
state.Apply();
}
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 @@
#pragma once
+#include <cstddef>
+#include <cstring>
+#include <memory>
#include <vector>
+#include <unordered_map>
#include "common/common_types.h"
+#include "common/hash.h"
+#include "video_core/pica.h"
#include "video_core/hwrasterizer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/shader/shader_interpreter.h"
+/**
+ * This struct contains all state used to generate the GLSL shader program that emulates the current
+ * Pica register configuration. This struct is used as a cache key for generated GLSL shader
+ * programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by
+ * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
+ * Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
+ * two separate shaders sharing the same key.
+ */
+struct PicaShaderConfig {
+ /// Construct a PicaShaderConfig with the current Pica register configuration.
+ static PicaShaderConfig CurrentConfig() {
+ PicaShaderConfig res;
+ const auto& regs = Pica::g_state.regs;
+
+ res.alpha_test_func = regs.output_merger.alpha_test.enable ?
+ regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always;
+
+ // Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling
+ // the GetTevStages() function) because BitField explicitly disables copies.
+
+ res.tev_stages[0].sources_raw = regs.tev_stage0.sources_raw;
+ res.tev_stages[1].sources_raw = regs.tev_stage1.sources_raw;
+ res.tev_stages[2].sources_raw = regs.tev_stage2.sources_raw;
+ res.tev_stages[3].sources_raw = regs.tev_stage3.sources_raw;
+ res.tev_stages[4].sources_raw = regs.tev_stage4.sources_raw;
+ res.tev_stages[5].sources_raw = regs.tev_stage5.sources_raw;
+
+ res.tev_stages[0].modifiers_raw = regs.tev_stage0.modifiers_raw;
+ res.tev_stages[1].modifiers_raw = regs.tev_stage1.modifiers_raw;
+ res.tev_stages[2].modifiers_raw = regs.tev_stage2.modifiers_raw;
+ res.tev_stages[3].modifiers_raw = regs.tev_stage3.modifiers_raw;
+ res.tev_stages[4].modifiers_raw = regs.tev_stage4.modifiers_raw;
+ res.tev_stages[5].modifiers_raw = regs.tev_stage5.modifiers_raw;
+
+ res.tev_stages[0].ops_raw = regs.tev_stage0.ops_raw;
+ res.tev_stages[1].ops_raw = regs.tev_stage1.ops_raw;
+ res.tev_stages[2].ops_raw = regs.tev_stage2.ops_raw;
+ res.tev_stages[3].ops_raw = regs.tev_stage3.ops_raw;
+ res.tev_stages[4].ops_raw = regs.tev_stage4.ops_raw;
+ res.tev_stages[5].ops_raw = regs.tev_stage5.ops_raw;
+
+ res.tev_stages[0].scales_raw = regs.tev_stage0.scales_raw;
+ res.tev_stages[1].scales_raw = regs.tev_stage1.scales_raw;
+ res.tev_stages[2].scales_raw = regs.tev_stage2.scales_raw;
+ res.tev_stages[3].scales_raw = regs.tev_stage3.scales_raw;
+ res.tev_stages[4].scales_raw = regs.tev_stage4.scales_raw;
+ res.tev_stages[5].scales_raw = regs.tev_stage5.scales_raw;
+
+ res.combiner_buffer_input =
+ regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
+ regs.tev_combiner_buffer_input.update_mask_a.Value() << 4;
+
+ return res;
+ }
+
+ bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
+ return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index));
+ }
+
+ bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
+ return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index));
+ }
+
+ bool operator ==(const PicaShaderConfig& o) const {
+ return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0;
+ };
+
+ Pica::Regs::CompareFunc alpha_test_func;
+ std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
+ u8 combiner_buffer_input;
+};
+
+namespace std {
+
+template <>
+struct hash<PicaShaderConfig> {
+ size_t operator()(const PicaShaderConfig& k) const {
+ return Common::ComputeHash64(&k, sizeof(PicaShaderConfig));
+ }
+};
+
+} // namespace std
+
class RasterizerOpenGL : public HWRasterizer {
public:
@@ -45,20 +134,24 @@ public:
/// Notify rasterizer that a 3DS memory region has been changed
void NotifyFlush(PAddr addr, u32 size) override;
-private:
- /// Structure used for managing texture environment states
- struct TEVConfigUniforms {
- GLuint enabled;
- GLuint color_sources;
- GLuint alpha_sources;
- GLuint color_modifiers;
- GLuint alpha_modifiers;
- GLuint color_alpha_op;
- GLuint color_alpha_multiplier;
- GLuint const_color;
- GLuint updates_combiner_buffer_color_alpha;
+ /// OpenGL shader generated for a given Pica register state
+ struct PicaShader {
+ /// OpenGL shader resource
+ OGLShader shader;
+
+ /// Fragment shader uniforms
+ enum Uniform : GLuint {
+ AlphaTestRef = 0,
+ TevConstColors = 1,
+ Texture0 = 7,
+ Texture1 = 8,
+ Texture2 = 9,
+ TevCombinerBufferColor = 10,
+ };
};
+private:
+
/// Structure used for storing information about color textures
struct TextureInfo {
OGLTexture texture;
@@ -129,6 +222,9 @@ private:
/// Reconfigure the OpenGL depth texture to use the given format and dimensions
void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height);
+ /// Sets the OpenGL shader in accordance with the current PICA register state
+ void SetShader();
+
/// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer
void SyncFramebuffer();
@@ -156,27 +252,12 @@ private:
/// Syncs the depth test states to match the PICA register
void SyncDepthTest();
- /// Syncs the specified TEV stage's color and alpha sources to match the PICA register
- void SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
-
- /// Syncs the specified TEV stage's color and alpha modifiers to match the PICA register
- void SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
-
- /// Syncs the specified TEV stage's color and alpha combiner operations to match the PICA register
- void SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
-
- /// Syncs the specified TEV stage's constant color to match the PICA register
- void SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
-
- /// Syncs the specified TEV stage's color and alpha multipliers to match the PICA register
- void SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
+ /// Syncs the TEV constant color to match the PICA register
+ void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage);
/// Syncs the TEV combiner color buffer to match the PICA register
void SyncCombinerColor();
- /// Syncs the TEV combiner write flags to match the PICA register
- void SyncCombinerWriteFlags();
-
/// Syncs the remaining OpenGL drawing state to match the current PICA state
void SyncDrawState();
@@ -213,21 +294,11 @@ private:
std::array<SamplerInfo, 3> texture_samplers;
TextureInfo fb_color_texture;
DepthTextureInfo fb_depth_texture;
- OGLShader shader;
+
+ std::unordered_map<PicaShaderConfig, std::unique_ptr<PicaShader>> shader_cache;
+ const PicaShader* current_shader = nullptr;
+
OGLVertexArray vertex_array;
OGLBuffer vertex_buffer;
OGLFramebuffer framebuffer;
-
- // Hardware vertex shader
- GLuint attrib_position;
- GLuint attrib_color;
- GLuint attrib_texcoords;
-
- // Hardware fragment shader
- GLuint uniform_alphatest_enabled;
- GLuint uniform_alphatest_func;
- GLuint uniform_alphatest_ref;
- GLuint uniform_tex;
- GLuint uniform_tev_combiner_buffer_color;
- TEVConfigUniforms uniform_tev_cfgs[6];
};
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:
/// Creates a new internal OpenGL resource and stores the handle
void Create(const char* vert_shader, const char* frag_shader) {
if (handle != 0) return;
- handle = ShaderUtil::LoadShaders(vert_shader, frag_shader);
+ handle = GLShader::LoadProgram(vert_shader, frag_shader);
}
/// 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 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/pica.h"
+#include "video_core/renderer_opengl/gl_rasterizer.h"
+#include "video_core/renderer_opengl/gl_shader_gen.h"
+
+using Pica::Regs;
+using TevStageConfig = Regs::TevStageConfig;
+
+namespace GLShader {
+
+/// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code)
+static bool IsPassThroughTevStage(const TevStageConfig& stage) {
+ return (stage.color_op == TevStageConfig::Operation::Replace &&
+ stage.alpha_op == TevStageConfig::Operation::Replace &&
+ stage.color_source1 == TevStageConfig::Source::Previous &&
+ stage.alpha_source1 == TevStageConfig::Source::Previous &&
+ stage.color_modifier1 == TevStageConfig::ColorModifier::SourceColor &&
+ stage.alpha_modifier1 == TevStageConfig::AlphaModifier::SourceAlpha &&
+ stage.GetColorMultiplier() == 1 &&
+ stage.GetAlphaMultiplier() == 1);
+}
+
+/// Writes the specified TEV stage source component(s)
+static void AppendSource(std::string& out, TevStageConfig::Source source,
+ const std::string& index_name) {
+ using Source = TevStageConfig::Source;
+ switch (source) {
+ case Source::PrimaryColor:
+ out += "primary_color";
+ break;
+ case Source::PrimaryFragmentColor:
+ // HACK: Until we implement fragment lighting, use primary_color
+ out += "primary_color";
+ break;
+ case Source::SecondaryFragmentColor:
+ // HACK: Until we implement fragment lighting, use zero
+ out += "vec4(0.0)";
+ break;
+ case Source::Texture0:
+ out += "texture(tex[0], texcoord[0])";
+ break;
+ case Source::Texture1:
+ out += "texture(tex[1], texcoord[1])";
+ break;
+ case Source::Texture2:
+ out += "texture(tex[2], texcoord[2])";
+ break;
+ case Source::PreviousBuffer:
+ out += "combiner_buffer";
+ break;
+ case Source::Constant:
+ ((out += "const_color[") += index_name) += ']';
+ break;
+ case Source::Previous:
+ out += "last_tex_env_out";
+ break;
+ default:
+ out += "vec4(0.0)";
+ LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source);
+ break;
+ }
+}
+
+/// Writes the color components to use for the specified TEV stage color modifier
+static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier modifier,
+ TevStageConfig::Source source, const std::string& index_name) {
+ using ColorModifier = TevStageConfig::ColorModifier;
+ switch (modifier) {
+ case ColorModifier::SourceColor:
+ AppendSource(out, source, index_name);
+ out += ".rgb";
+ break;
+ case ColorModifier::OneMinusSourceColor:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".rgb";
+ break;
+ case ColorModifier::SourceAlpha:
+ AppendSource(out, source, index_name);
+ out += ".aaa";
+ break;
+ case ColorModifier::OneMinusSourceAlpha:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".aaa";
+ break;
+ case ColorModifier::SourceRed:
+ AppendSource(out, source, index_name);
+ out += ".rrr";
+ break;
+ case ColorModifier::OneMinusSourceRed:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".rrr";
+ break;
+ case ColorModifier::SourceGreen:
+ AppendSource(out, source, index_name);
+ out += ".ggg";
+ break;
+ case ColorModifier::OneMinusSourceGreen:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".ggg";
+ break;
+ case ColorModifier::SourceBlue:
+ AppendSource(out, source, index_name);
+ out += ".bbb";
+ break;
+ case ColorModifier::OneMinusSourceBlue:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".bbb";
+ break;
+ default:
+ out += "vec3(0.0)";
+ LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier);
+ break;
+ }
+}
+
+/// Writes the alpha component to use for the specified TEV stage alpha modifier
+static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier modifier,
+ TevStageConfig::Source source, const std::string& index_name) {
+ using AlphaModifier = TevStageConfig::AlphaModifier;
+ switch (modifier) {
+ case AlphaModifier::SourceAlpha:
+ AppendSource(out, source, index_name);
+ out += ".a";
+ break;
+ case AlphaModifier::OneMinusSourceAlpha:
+ out += "1.0 - ";
+ AppendSource(out, source, index_name);
+ out += ".a";
+ break;
+ case AlphaModifier::SourceRed:
+ AppendSource(out, source, index_name);
+ out += ".r";
+ break;
+ case AlphaModifier::OneMinusSourceRed:
+ out += "1.0 - ";
+ AppendSource(out, source, index_name);
+ out += ".r";
+ break;
+ case AlphaModifier::SourceGreen:
+ AppendSource(out, source, index_name);
+ out += ".g";
+ break;
+ case AlphaModifier::OneMinusSourceGreen:
+ out += "1.0 - ";
+ AppendSource(out, source, index_name);
+ out += ".g";
+ break;
+ case AlphaModifier::SourceBlue:
+ AppendSource(out, source, index_name);
+ out += ".b";
+ break;
+ case AlphaModifier::OneMinusSourceBlue:
+ out += "1.0 - ";
+ AppendSource(out, source, index_name);
+ out += ".b";
+ break;
+ default:
+ out += "0.0";
+ LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier);
+ break;
+ }
+}
+
+/// Writes the combiner function for the color components for the specified TEV stage operation
+static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation,
+ const std::string& variable_name) {
+ out += "clamp(";
+ using Operation = TevStageConfig::Operation;
+ switch (operation) {
+ case Operation::Replace:
+ out += variable_name + "[0]";
+ break;
+ case Operation::Modulate:
+ out += variable_name + "[0] * " + variable_name + "[1]";
+ break;
+ case Operation::Add:
+ out += variable_name + "[0] + " + variable_name + "[1]";
+ break;
+ case Operation::AddSigned:
+ out += variable_name + "[0] + " + variable_name + "[1] - vec3(0.5)";
+ break;
+ case Operation::Lerp:
+ // TODO(bunnei): Verify if HW actually does this per-component, otherwise we can just use builtin lerp
+ out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])";
+ break;
+ case Operation::Subtract:
+ out += variable_name + "[0] - " + variable_name + "[1]";
+ break;
+ case Operation::MultiplyThenAdd:
+ out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]";
+ break;
+ case Operation::AddThenMultiply:
+ out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]";
+ break;
+ default:
+ out += "vec3(0.0)";
+ LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation);
+ break;
+ }
+ out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0
+}
+
+/// Writes the combiner function for the alpha component for the specified TEV stage operation
+static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation,
+ const std::string& variable_name) {
+ out += "clamp(";
+ using Operation = TevStageConfig::Operation;
+ switch (operation) {
+ case Operation::Replace:
+ out += variable_name + "[0]";
+ break;
+ case Operation::Modulate:
+ out += variable_name + "[0] * " + variable_name + "[1]";
+ break;
+ case Operation::Add:
+ out += variable_name + "[0] + " + variable_name + "[1]";
+ break;
+ case Operation::AddSigned:
+ out += variable_name + "[0] + " + variable_name + "[1] - 0.5";
+ break;
+ case Operation::Lerp:
+ out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])";
+ break;
+ case Operation::Subtract:
+ out += variable_name + "[0] - " + variable_name + "[1]";
+ break;
+ case Operation::MultiplyThenAdd:
+ out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]";
+ break;
+ case Operation::AddThenMultiply:
+ out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]";
+ break;
+ default:
+ out += "0.0";
+ LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation);
+ break;
+ }
+ out += ", 0.0, 1.0)";
+}
+
+/// Writes the if-statement condition used to evaluate alpha testing
+static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) {
+ using CompareFunc = Regs::CompareFunc;
+ switch (func) {
+ case CompareFunc::Never:
+ out += "true";
+ break;
+ case CompareFunc::Always:
+ out += "false";
+ break;
+ case CompareFunc::Equal:
+ case CompareFunc::NotEqual:
+ case CompareFunc::LessThan:
+ case CompareFunc::LessThanOrEqual:
+ case CompareFunc::GreaterThan:
+ case CompareFunc::GreaterThanOrEqual:
+ {
+ static const char* op[] = { "!=", "==", ">=", ">", "<=", "<", };
+ unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal;
+ out += "int(last_tex_env_out.a * 255.0f) " + std::string(op[index]) + " alphatest_ref";
+ break;
+ }
+
+ default:
+ out += "false";
+ LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func);
+ break;
+ }
+}
+
+/// Writes the code to emulate the specified TEV stage
+static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
+ auto& stage = config.tev_stages[index];
+ if (!IsPassThroughTevStage(stage)) {
+ std::string index_name = std::to_string(index);
+
+ out += "vec3 color_results_" + index_name + "[3] = vec3[3](";
+ AppendColorModifier(out, stage.color_modifier1, stage.color_source1, index_name);
+ out += ", ";
+ AppendColorModifier(out, stage.color_modifier2, stage.color_source2, index_name);
+ out += ", ";
+ AppendColorModifier(out, stage.color_modifier3, stage.color_source3, index_name);
+ out += ");\n";
+
+ out += "vec3 color_output_" + index_name + " = ";
+ AppendColorCombiner(out, stage.color_op, "color_results_" + index_name);
+ out += ";\n";
+
+ out += "float alpha_results_" + index_name + "[3] = float[3](";
+ AppendAlphaModifier(out, stage.alpha_modifier1, stage.alpha_source1, index_name);
+ out += ", ";
+ AppendAlphaModifier(out, stage.alpha_modifier2, stage.alpha_source2, index_name);
+ out += ", ";
+ AppendAlphaModifier(out, stage.alpha_modifier3, stage.alpha_source3, index_name);
+ out += ");\n";
+
+ out += "float alpha_output_" + index_name + " = ";
+ AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name);
+ out += ";\n";
+
+ out += "last_tex_env_out = vec4("
+ "clamp(color_output_" + index_name + " * " + std::to_string(stage.GetColorMultiplier()) + ".0, vec3(0.0), vec3(1.0)),"
+ "clamp(alpha_output_" + index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + ".0, 0.0, 1.0));\n";
+ }
+
+ if (config.TevStageUpdatesCombinerBufferColor(index))
+ out += "combiner_buffer.rgb = last_tex_env_out.rgb;\n";
+
+ if (config.TevStageUpdatesCombinerBufferAlpha(index))
+ out += "combiner_buffer.a = last_tex_env_out.a;\n";
+}
+
+std::string GenerateFragmentShader(const PicaShaderConfig& config) {
+ std::string out = R"(
+#version 330
+#extension GL_ARB_explicit_uniform_location : require
+
+#define NUM_TEV_STAGES 6
+
+in vec4 primary_color;
+in vec2 texcoord[3];
+
+out vec4 color;
+)";
+
+ using Uniform = RasterizerOpenGL::PicaShader::Uniform;
+ out += "layout(location = " + std::to_string((int)Uniform::AlphaTestRef) + ") uniform int alphatest_ref;\n";
+ out += "layout(location = " + std::to_string((int)Uniform::TevConstColors) + ") uniform vec4 const_color[NUM_TEV_STAGES];\n";
+ out += "layout(location = " + std::to_string((int)Uniform::Texture0) + ") uniform sampler2D tex[3];\n";
+ out += "layout(location = " + std::to_string((int)Uniform::TevCombinerBufferColor) + ") uniform vec4 tev_combiner_buffer_color;\n";
+
+ out += "void main() {\n";
+ out += "vec4 combiner_buffer = tev_combiner_buffer_color;\n";
+ out += "vec4 last_tex_env_out = vec4(0.0);\n";
+
+ // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
+ if (config.alpha_test_func == Regs::CompareFunc::Never) {
+ out += "discard; }";
+ return out;
+ }
+
+ for (size_t index = 0; index < config.tev_stages.size(); ++index)
+ WriteTevStage(out, config, (unsigned)index);
+
+ if (config.alpha_test_func != Regs::CompareFunc::Always) {
+ out += "if (";
+ AppendAlphaTestCondition(out, config.alpha_test_func);
+ out += ") discard;\n";
+ }
+
+ out += "color = last_tex_env_out;\n}";
+
+ return out;
+}
+
+std::string GenerateVertexShader() {
+ std::string out = "#version 330\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n";
+
+ out += R"(
+out vec4 primary_color;
+out vec2 texcoord[3];
+
+void main() {
+ primary_color = vert_color;
+ texcoord[0] = vert_texcoord0;
+ texcoord[1] = vert_texcoord1;
+ texcoord[2] = vert_texcoord2;
+ gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
+}
+)";
+
+ return out;
+}
+
+} // 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 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+#include "video_core/renderer_opengl/gl_rasterizer.h"
+
+namespace GLShader {
+
+/**
+ * Generates the GLSL vertex shader program source code for the current Pica state
+ * @returns String of the shader source code
+ */
+std::string GenerateVertexShader();
+
+/**
+ * Generates the GLSL fragment shader program source code for the current Pica state
+ * @param config ShaderCacheKey object generated for the current Pica state, used for the shader
+ * configuration (NOTE: Use state in this struct only, not the Pica registers!)
+ * @returns String of the shader source code
+ */
+std::string GenerateFragmentShader(const PicaShaderConfig& config);
+
+} // 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 @@
#include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
-namespace ShaderUtil {
+namespace GLShader {
-GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
+GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
// Create the shaders
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
@@ -65,6 +65,7 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
GLuint program_id = glCreateProgram();
glAttachShader(program_id, vertex_shader_id);
glAttachShader(program_id, fragment_shader_id);
+
glLinkProgram(program_id);
// Check the program
@@ -87,4 +88,4 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
return program_id;
}
-}
+} // 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 @@
#include <glad/glad.h>
-namespace ShaderUtil {
+namespace GLShader {
-GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path);
+enum Attributes {
+ ATTRIBUTE_POSITION,
+ ATTRIBUTE_COLOR,
+ ATTRIBUTE_TEXCOORD0,
+ ATTRIBUTE_TEXCOORD1,
+ ATTRIBUTE_TEXCOORD2,
+};
-}
+/**
+ * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
+ * @param vertex_shader String of the GLSL vertex shader program
+ * @param fragment_shader String of the GLSL fragment shader program
+ * @returns Handle of the newly created OpenGL shader object
+ */
+GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader);
+
+} // 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 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-namespace GLShaders {
-
-const char g_vertex_shader[] = R"(
-#version 150 core
-
-in vec2 vert_position;
-in vec2 vert_tex_coord;
-out vec2 frag_tex_coord;
-
-// This is a truncated 3x3 matrix for 2D transformations:
-// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
-// The third column performs translation.
-// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
-// implicitly be [0, 0, 1]
-uniform mat3x2 modelview_matrix;
-
-void main() {
- // Multiply input position by the rotscale part of the matrix and then manually translate by
- // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
- // to `vec3(vert_position.xy, 1.0)`
- gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
- frag_tex_coord = vert_tex_coord;
-}
-)";
-
-const char g_fragment_shader[] = R"(
-#version 150 core
-
-in vec2 frag_tex_coord;
-out vec4 color;
-
-uniform sampler2D color_texture;
-
-void main() {
- color = texture(color_texture, frag_tex_coord);
-}
-)";
-
-const char g_vertex_shader_hw[] = R"(
-#version 150 core
-
-#define NUM_VTX_ATTR 7
-
-in vec4 vert_position;
-in vec4 vert_color;
-in vec2 vert_texcoords[3];
-
-out vec4 o[NUM_VTX_ATTR];
-
-void main() {
- o[2] = vert_color;
- o[3] = vec4(vert_texcoords[0].xy, vert_texcoords[1].xy);
- o[5] = vec4(0.0, 0.0, vert_texcoords[2].xy);
-
- gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
-}
-)";
-
-// TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms
-const char g_fragment_shader_hw[] = R"(
-#version 150 core
-
-#define NUM_VTX_ATTR 7
-#define NUM_TEV_STAGES 6
-
-#define SOURCE_PRIMARYCOLOR 0x0
-#define SOURCE_PRIMARYFRAGMENTCOLOR 0x1
-#define SOURCE_SECONDARYFRAGMENTCOLOR 0x2
-#define SOURCE_TEXTURE0 0x3
-#define SOURCE_TEXTURE1 0x4
-#define SOURCE_TEXTURE2 0x5
-#define SOURCE_TEXTURE3 0x6
-#define SOURCE_PREVIOUSBUFFER 0xd
-#define SOURCE_CONSTANT 0xe
-#define SOURCE_PREVIOUS 0xf
-
-#define COLORMODIFIER_SOURCECOLOR 0x0
-#define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1
-#define COLORMODIFIER_SOURCEALPHA 0x2
-#define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3
-#define COLORMODIFIER_SOURCERED 0x4
-#define COLORMODIFIER_ONEMINUSSOURCERED 0x5
-#define COLORMODIFIER_SOURCEGREEN 0x8
-#define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9
-#define COLORMODIFIER_SOURCEBLUE 0xc
-#define COLORMODIFIER_ONEMINUSSOURCEBLUE 0xd
-
-#define ALPHAMODIFIER_SOURCEALPHA 0x0
-#define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1
-#define ALPHAMODIFIER_SOURCERED 0x2
-#define ALPHAMODIFIER_ONEMINUSSOURCERED 0x3
-#define ALPHAMODIFIER_SOURCEGREEN 0x4
-#define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5
-#define ALPHAMODIFIER_SOURCEBLUE 0x6
-#define ALPHAMODIFIER_ONEMINUSSOURCEBLUE 0x7
-
-#define OPERATION_REPLACE 0
-#define OPERATION_MODULATE 1
-#define OPERATION_ADD 2
-#define OPERATION_ADDSIGNED 3
-#define OPERATION_LERP 4
-#define OPERATION_SUBTRACT 5
-#define OPERATION_MULTIPLYTHENADD 8
-#define OPERATION_ADDTHENMULTIPLY 9
-
-#define COMPAREFUNC_NEVER 0
-#define COMPAREFUNC_ALWAYS 1
-#define COMPAREFUNC_EQUAL 2
-#define COMPAREFUNC_NOTEQUAL 3
-#define COMPAREFUNC_LESSTHAN 4
-#define COMPAREFUNC_LESSTHANOREQUAL 5
-#define COMPAREFUNC_GREATERTHAN 6
-#define COMPAREFUNC_GREATERTHANOREQUAL 7
-
-in vec4 o[NUM_VTX_ATTR];
-out vec4 color;
-
-uniform bool alphatest_enabled;
-uniform int alphatest_func;
-uniform float alphatest_ref;
-
-uniform sampler2D tex[3];
-
-uniform vec4 tev_combiner_buffer_color;
-
-struct TEVConfig
-{
- bool enabled;
- ivec3 color_sources;
- ivec3 alpha_sources;
- ivec3 color_modifiers;
- ivec3 alpha_modifiers;
- ivec2 color_alpha_op;
- ivec2 color_alpha_multiplier;
- vec4 const_color;
- bvec2 updates_combiner_buffer_color_alpha;
-};
-
-uniform TEVConfig tev_cfgs[NUM_TEV_STAGES];
-
-vec4 g_combiner_buffer;
-vec4 g_last_tex_env_out;
-vec4 g_const_color;
-
-vec4 GetSource(int source) {
- if (source == SOURCE_PRIMARYCOLOR) {
- return o[2];
- } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) {
- // HACK: Until we implement fragment lighting, use primary_color
- return o[2];
- } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) {
- // HACK: Until we implement fragment lighting, use zero
- return vec4(0.0, 0.0, 0.0, 0.0);
- } else if (source == SOURCE_TEXTURE0) {
- return texture(tex[0], o[3].xy);
- } else if (source == SOURCE_TEXTURE1) {
- return texture(tex[1], o[3].zw);
- } else if (source == SOURCE_TEXTURE2) {
- // TODO: Unverified
- return texture(tex[2], o[5].zw);
- } else if (source == SOURCE_TEXTURE3) {
- // TODO: no 4th texture?
- } else if (source == SOURCE_PREVIOUSBUFFER) {
- return g_combiner_buffer;
- } else if (source == SOURCE_CONSTANT) {
- return g_const_color;
- } else if (source == SOURCE_PREVIOUS) {
- return g_last_tex_env_out;
- }
-
- return vec4(0.0);
-}
-
-vec3 GetColorModifier(int factor, vec4 color) {
- if (factor == COLORMODIFIER_SOURCECOLOR) {
- return color.rgb;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) {
- return vec3(1.0) - color.rgb;
- } else if (factor == COLORMODIFIER_SOURCEALPHA) {
- return color.aaa;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) {
- return vec3(1.0) - color.aaa;
- } else if (factor == COLORMODIFIER_SOURCERED) {
- return color.rrr;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) {
- return vec3(1.0) - color.rrr;
- } else if (factor == COLORMODIFIER_SOURCEGREEN) {
- return color.ggg;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) {
- return vec3(1.0) - color.ggg;
- } else if (factor == COLORMODIFIER_SOURCEBLUE) {
- return color.bbb;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) {
- return vec3(1.0) - color.bbb;
- }
-
- return vec3(0.0);
-}
-
-float GetAlphaModifier(int factor, vec4 color) {
- if (factor == ALPHAMODIFIER_SOURCEALPHA) {
- return color.a;
- } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) {
- return 1.0 - color.a;
- } else if (factor == ALPHAMODIFIER_SOURCERED) {
- return color.r;
- } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) {
- return 1.0 - color.r;
- } else if (factor == ALPHAMODIFIER_SOURCEGREEN) {
- return color.g;
- } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) {
- return 1.0 - color.g;
- } else if (factor == ALPHAMODIFIER_SOURCEBLUE) {
- return color.b;
- } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) {
- return 1.0 - color.b;
- }
-
- return 0.0;
-}
-
-vec3 ColorCombine(int op, vec3 color[3]) {
- if (op == OPERATION_REPLACE) {
- return color[0];
- } else if (op == OPERATION_MODULATE) {
- return color[0] * color[1];
- } else if (op == OPERATION_ADD) {
- return min(color[0] + color[1], 1.0);
- } else if (op == OPERATION_ADDSIGNED) {
- return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0);
- } else if (op == OPERATION_LERP) {
- return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]);
- } else if (op == OPERATION_SUBTRACT) {
- return max(color[0] - color[1], 0.0);
- } else if (op == OPERATION_MULTIPLYTHENADD) {
- return min(color[0] * color[1] + color[2], 1.0);
- } else if (op == OPERATION_ADDTHENMULTIPLY) {
- return min(color[0] + color[1], 1.0) * color[2];
- }
-
- return vec3(0.0);
-}
-
-float AlphaCombine(int op, float alpha[3]) {
- if (op == OPERATION_REPLACE) {
- return alpha[0];
- } else if (op == OPERATION_MODULATE) {
- return alpha[0] * alpha[1];
- } else if (op == OPERATION_ADD) {
- return min(alpha[0] + alpha[1], 1.0);
- } else if (op == OPERATION_ADDSIGNED) {
- return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0);
- } else if (op == OPERATION_LERP) {
- return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]);
- } else if (op == OPERATION_SUBTRACT) {
- return max(alpha[0] - alpha[1], 0.0);
- } else if (op == OPERATION_MULTIPLYTHENADD) {
- return min(alpha[0] * alpha[1] + alpha[2], 1.0);
- } else if (op == OPERATION_ADDTHENMULTIPLY) {
- return min(alpha[0] + alpha[1], 1.0) * alpha[2];
- }
-
- return 0.0;
-}
-
-void main(void) {
- g_combiner_buffer = tev_combiner_buffer_color;
-
- for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) {
- if (tev_cfgs[tex_env_idx].enabled) {
- g_const_color = tev_cfgs[tex_env_idx].const_color;
-
- vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)),
- GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)),
- GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z)));
- vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results);
-
- float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)),
- GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)),
- GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z)));
- float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results);
-
- 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));
- }
-
- if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) {
- g_combiner_buffer.rgb = g_last_tex_env_out.rgb;
- }
-
- if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) {
- g_combiner_buffer.a = g_last_tex_env_out.a;
- }
- }
-
- if (alphatest_enabled) {
- if (alphatest_func == COMPAREFUNC_NEVER) {
- discard;
- } else if (alphatest_func == COMPAREFUNC_ALWAYS) {
-
- } else if (alphatest_func == COMPAREFUNC_EQUAL) {
- if (g_last_tex_env_out.a != alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) {
- if (g_last_tex_env_out.a == alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_LESSTHAN) {
- if (g_last_tex_env_out.a >= alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) {
- if (g_last_tex_env_out.a > alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) {
- if (g_last_tex_env_out.a <= alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) {
- if (g_last_tex_env_out.a < alphatest_ref) {
- discard;
- }
- }
- }
-
- color = g_last_tex_env_out;
-}
-)";
-
-}
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:
GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
GLuint shader_program; // GL_CURRENT_PROGRAM
+ bool shader_dirty;
} draw;
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 @@
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
-#include "video_core/renderer_opengl/gl_shaders.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
+static const char vertex_shader[] = R"(
+#version 150 core
+
+in vec2 vert_position;
+in vec2 vert_tex_coord;
+out vec2 frag_tex_coord;
+
+// This is a truncated 3x3 matrix for 2D transformations:
+// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
+// The third column performs translation.
+// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
+// implicitly be [0, 0, 1]
+uniform mat3x2 modelview_matrix;
+
+void main() {
+ // Multiply input position by the rotscale part of the matrix and then manually translate by
+ // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
+ // to `vec3(vert_position.xy, 1.0)`
+ gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
+ frag_tex_coord = vert_tex_coord;
+}
+)";
+
+static const char fragment_shader[] = R"(
+#version 150 core
+
+in vec2 frag_tex_coord;
+out vec4 color;
+
+uniform sampler2D color_texture;
+
+void main() {
+ color = texture(color_texture, frag_tex_coord);
+}
+)";
+
/**
* Vertex structure that the drawn screen rectangles are composed of.
*/
@@ -207,7 +242,7 @@ void RendererOpenGL::InitOpenGLObjects() {
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f);
// Link shaders and get variable locations
- program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader);
+ program_id = GLShader::LoadProgram(vertex_shader, fragment_shader);
uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix");
uniform_color_texture = glGetUniformLocation(program_id, "color_texture");
attrib_position = glGetAttribLocation(program_id, "vert_position");