summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--externals/CMakeLists.txt3
-rw-r--r--externals/cryptopp/CMakeLists.txt168
m---------externals/cryptopp/cryptopp0
m---------externals/dynarmic0
-rw-r--r--src/citra_qt/debugger/graphics/graphics_cmdlists.cpp2
-rw-r--r--src/citra_qt/debugger/graphics/graphics_surface.cpp3
-rw-r--r--src/citra_qt/debugger/graphics/graphics_tracing.h6
-rw-r--r--src/citra_qt/game_list.cpp11
-rw-r--r--src/citra_qt/game_list.h2
-rw-r--r--src/citra_qt/main.cpp186
-rw-r--r--src/citra_qt/main.h21
-rw-r--r--src/citra_qt/main.ui55
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/file_sys/archive_romfs.cpp43
-rw-r--r--src/core/file_sys/archive_selfncch.cpp257
-rw-r--r--src/core/file_sys/archive_selfncch.h (renamed from src/core/file_sys/archive_romfs.h)23
-rw-r--r--src/core/file_sys/errors.h10
-rw-r--r--src/core/frontend/emu_window.cpp2
-rw-r--r--src/core/hle/ipc.h6
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/fs/archive.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp7
-rw-r--r--src/core/hle/service/hid/hid.h4
-rw-r--r--src/core/hle/service/ir/ir_u.cpp2
-rw-r--r--src/core/hle/service/nim/nim.cpp2
-rw-r--r--src/core/loader/3dsx.cpp6
-rw-r--r--src/core/loader/ncch.cpp6
-rw-r--r--src/video_core/CMakeLists.txt16
-rw-r--r--src/video_core/command_processor.cpp14
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp4
-rw-r--r--src/video_core/debug_utils/debug_utils.h4
-rw-r--r--src/video_core/pica.cpp2
-rw-r--r--src/video_core/regs.cpp21
-rw-r--r--src/video_core/regs.h68
-rw-r--r--src/video_core/regs_lighting.h8
-rw-r--r--src/video_core/regs_pipeline.h78
-rw-r--r--src/video_core/renderer_base.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp75
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp30
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h4
-rw-r--r--src/video_core/shader/shader.cpp6
-rw-r--r--src/video_core/shader/shader.h3
-rw-r--r--src/video_core/shader/shader_jit_x64_compiler.cpp8
-rw-r--r--src/video_core/swrasterizer/clipper.cpp (renamed from src/video_core/clipper.cpp)5
-rw-r--r--src/video_core/swrasterizer/clipper.h (renamed from src/video_core/clipper.h)0
-rw-r--r--src/video_core/swrasterizer/framebuffer.cpp358
-rw-r--r--src/video_core/swrasterizer/framebuffer.h29
-rw-r--r--src/video_core/swrasterizer/rasterizer.cpp (renamed from src/video_core/rasterizer.cpp)559
-rw-r--r--src/video_core/swrasterizer/rasterizer.h (renamed from src/video_core/rasterizer.h)0
-rw-r--r--src/video_core/swrasterizer/swrasterizer.cpp (renamed from src/video_core/swrasterizer.cpp)4
-rw-r--r--src/video_core/swrasterizer/swrasterizer.h (renamed from src/video_core/swrasterizer.h)0
-rw-r--r--src/video_core/swrasterizer/texturing.cpp228
-rw-r--r--src/video_core/swrasterizer/texturing.h28
-rw-r--r--src/video_core/texture/etc1.cpp2
-rw-r--r--src/video_core/texture/texture_decode.cpp2
61 files changed, 1479 insertions, 942 deletions
diff --git a/.gitmodules b/.gitmodules
index dbb1b0dd3..f98725622 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -19,3 +19,6 @@
[submodule "xbyak"]
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
+[submodule "cryptopp"]
+ path = externals/cryptopp/cryptopp
+ url = https://github.com/weidai11/cryptopp.git
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 7e4b05ffc..309e98464 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -6,3 +6,6 @@ if (ARCHITECTURE_x86_64)
target_compile_options(xbyak INTERFACE -fno-operator-names)
endif()
endif()
+
+add_subdirectory(cryptopp)
+
diff --git a/externals/cryptopp/CMakeLists.txt b/externals/cryptopp/CMakeLists.txt
new file mode 100644
index 000000000..bbac71bb9
--- /dev/null
+++ b/externals/cryptopp/CMakeLists.txt
@@ -0,0 +1,168 @@
+# The CMakeLists.txt shipped with cryptopp pollutes our option list and installation list,
+# so we made our own one. This is basically a trimmed down version of the shipped CMakeLists.txt
+# The differences are:
+# - removed support for legacy CMake versions
+# - removed support for 32-bit
+# - removed rdrand module.asm as a workaround for an issue (see below)
+# - added prefix "CRYPTOPP_" to all option names
+# - disabled testing
+# - disabled installation
+# - disabled documentation
+# - configured to build a static library only
+
+include(TestBigEndian)
+include(CheckCXXCompilerFlag)
+
+#============================================================================
+# Settable options
+#============================================================================
+
+option(CRYPTOPP_DISABLE_ASM "Disable ASM" OFF)
+option(CRYPTOPP_DISABLE_SSSE3 "Disable SSSE3" OFF)
+option(CRYPTOPP_DISABLE_AESNI "Disable AES-NI" OFF)
+option(CRYPTOPP_DISABLE_CXXFLAGS_OPTIMIZATIONS "Disable CXXFLAGS optimizations" OFF)
+
+#============================================================================
+# Internal compiler options
+#============================================================================
+
+# Only set when cross-compiling, http://www.vtk.org/Wiki/CMake_Cross_Compiling
+if (NOT (CMAKE_SYSTEM_VERSION AND CMAKE_SYSTEM_PROCESSOR))
+ set(CRYPTOPP_CROSS_COMPILE 1)
+else()
+ set(CRYPTOPP_CROSS_COMPILE 0)
+endif()
+
+# Don't use RPATH's. The resulting binary could fail a security audit.
+if (NOT CMAKE_VERSION VERSION_LESS 2.8.12)
+ set(CMAKE_MACOSX_RPATH 0)
+endif()
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
+ add_definitions(-wd68 -wd186 -wd279 -wd327 -wd161 -wd3180)
+endif()
+
+# Endianness
+TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
+if(IS_BIG_ENDIAN)
+ add_definitions(-DIS_BIG_ENDIAN)
+endif()
+
+if(CRYPTOPP_DISABLE_ASM)
+ add_definitions(-DCRYPTOPP_DISABLE_ASM)
+endif()
+if(CRYPTOPP_DISABLE_SSSE3)
+ add_definitions(-DCRYPTOPP_DISABLE_SSSE3)
+endif()
+if(CRYPTOPP_DISABLE_AESNI)
+ add_definitions(-DCRYPTOPP_DISABLE_AESNI)
+endif()
+
+# We need the output 'uname -s' for Unix and Linux system detection
+if (NOT CRYPTOPP_CROSS_COMPILE)
+ set (UNAME_CMD "uname")
+ set (UNAME_ARG "-s")
+ execute_process(COMMAND ${UNAME_CMD} ${UNAME_ARG}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ RESULT_VARIABLE UNAME_RESULT
+ OUTPUT_VARIABLE UNAME_SYSTEM)
+ string(REGEX REPLACE "\n$" "" UNAME_SYSTEM "${UNAME_SYSTEM}")
+endif()
+
+# We need the output 'uname -m' for Unix and Linux platform detection
+if (NOT CRYPTOPP_CROSS_COMPILE)
+ set (UNAME_CMD "uname")
+ set (UNAME_ARG "-m")
+ execute_process(COMMAND ${UNAME_CMD} ${UNAME_ARG}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ RESULT_VARIABLE UNAME_RESULT
+ OUTPUT_VARIABLE UNAME_MACHINE)
+ string(REGEX REPLACE "\n$" "" UNAME_MACHINE "${UNAME_MACHINE}")
+endif()
+
+if(WINDOWS_STORE OR WINDOWS_PHONE)
+ if("${CMAKE_SYSTEM_VERSION}" MATCHES "10\\.0.*")
+ SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D\"_WIN32_WINNT=0x0A00\"" )
+ endif()
+ SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FI\"winapifamily.h\"" )
+endif()
+
+# Enable PIC for all targets except Windows and 32-bit x86.
+# Avoid on 32-bit x86 due to register pressures.
+if ((NOT CRYPTOPP_CROSS_COMPILE) AND (NOT (WINDOWS OR WINDOWS_STORE OR WINDOWS_PHONE)))
+ # Use Regex; match i386, i486, i586 and i686
+ if (NOT (${UNAME_MACHINE} MATCHES "i.86"))
+ SET(CMAKE_POSITION_INDEPENDENT_CODE 1)
+ endif()
+endif()
+
+# -march=native for GCC, Clang and ICC in any version that does support it.
+if ((NOT CRYPTOPP_DISABLE_CXXFLAGS_OPTIMIZATIONS) AND (NOT CRYPTOPP_CROSS_COMPILE) AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU|Intel"))
+ CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_OPT_ARCH_NATIVE_SUPPORTED)
+ if (COMPILER_OPT_ARCH_NATIVE_SUPPORTED AND NOT CMAKE_CXX_FLAGS MATCHES "-march=")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
+ endif()
+endif()
+
+# Link is driven through the compiler, but CXXFLAGS are not used. Also see
+# http://public.kitware.com/pipermail/cmake/2003-June/003967.html
+if (NOT (WINDOWS OR WINDOWS_STORE OR WINDOWS_PHONE))
+ SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_FLAGS}")
+endif()
+
+#============================================================================
+# Sources & headers
+#============================================================================
+
+# Library headers
+file(GLOB cryptopp_HEADERS cryptopp/*.h)
+
+# Library sources. You can use the GNUmakefile to generate the list: `make sources`.
+file(GLOB cryptopp_SOURCES cryptopp/*.cpp)
+list(REMOVE_ITEM cryptopp_SOURCES
+ # These are removed in the original CMakeLists.txt
+ ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/pch.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/simple.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/winpipes.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/cryptlib_bds.cpp
+ ${cryptopp_SOURCES_TEST}
+ )
+
+if(MINGW OR WIN32)
+ list(APPEND cryptopp_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/winpipes.cpp)
+endif()
+
+if(MSVC AND NOT CRYPTOPP_DISABLE_ASM)
+ if(${CMAKE_GENERATOR} MATCHES ".*ARM")
+ message(STATUS "Disabling ASM because ARM is specified as target platform.")
+ else()
+ # Note that we removed rdrand.asm. This is a workaround for the issue that rdrand.asm cannnot compiled properly
+ # on MSVC. Because there is also a rdrand.S file in the submodule, CMake will specify the target path for
+ # rdrand.asm as "/crytopp.dir/{Debug|Release}/cryptopp/rdrand.asm.obj". The additional target folder "cryptopp"
+ # is specified because the file rdrand.asm is in the source folder "cryptopp". But MSVC assembler can't build
+ # target file to an non-existing folder("cryptopp").
+ list(APPEND cryptopp_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/x64dll.asm)
+ list(APPEND cryptopp_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/x64masm.asm)
+ # list(APPEND cryptopp_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/rdrand.asm)
+ set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/x64dll.asm PROPERTIES COMPILE_FLAGS "/D_M_X64")
+ set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/x64masm.asm PROPERTIES COMPILE_FLAGS "/D_M_X64")
+ # set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/rdrand.asm PROPERTIES COMPILE_FLAGS "/D_M_X64")
+ enable_language(ASM_MASM)
+ endif()
+endif()
+
+#============================================================================
+# Compile targets
+#============================================================================
+add_library(cryptopp STATIC ${cryptopp_SOURCES})
+
+#============================================================================
+# Third-party libraries
+#============================================================================
+
+if(WIN32)
+ target_link_libraries(cryptopp ws2_32)
+endif()
+
+find_package(Threads)
+target_link_libraries(cryptopp ${CMAKE_THREAD_LIBS_INIT})
diff --git a/externals/cryptopp/cryptopp b/externals/cryptopp/cryptopp
new file mode 160000
+Subproject 841c37e34765487a2968357369ab74db8b10a62
diff --git a/externals/dynarmic b/externals/dynarmic
-Subproject 459d7d1bafcf85677c989b7cb260d3789aa813e
+Subproject 358cf7c32205a5114964865c86a8455daf81073
diff --git a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
index 536548f36..c68fe753b 100644
--- a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp
@@ -72,7 +72,7 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0:
- return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str());
+ return QString::fromLatin1(Pica::Regs::GetRegisterName(write.cmd_id));
case 1:
return QString("%1").arg(write.cmd_id, 3, 16, QLatin1Char('0'));
case 2:
diff --git a/src/citra_qt/debugger/graphics/graphics_surface.cpp b/src/citra_qt/debugger/graphics/graphics_surface.cpp
index f83c1f96c..47d9924e1 100644
--- a/src/citra_qt/debugger/graphics/graphics_surface.cpp
+++ b/src/citra_qt/debugger/graphics/graphics_surface.cpp
@@ -17,7 +17,8 @@
#include "core/hw/gpu.h"
#include "core/memory.h"
#include "video_core/pica_state.h"
-#include "video_core/regs.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/texture/texture_decode.h"
#include "video_core/utils.h"
diff --git a/src/citra_qt/debugger/graphics/graphics_tracing.h b/src/citra_qt/debugger/graphics/graphics_tracing.h
index 3f73bcd2e..eb1292c29 100644
--- a/src/citra_qt/debugger/graphics/graphics_tracing.h
+++ b/src/citra_qt/debugger/graphics/graphics_tracing.h
@@ -15,6 +15,9 @@ public:
explicit GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr);
+ void OnEmulationStarting(EmuThread* emu_thread);
+ void OnEmulationStopping();
+
private slots:
void StartRecording();
void StopRecording();
@@ -23,9 +26,6 @@ private slots:
void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
void OnResumed() override;
- void OnEmulationStarting(EmuThread* emu_thread);
- void OnEmulationStopping();
-
signals:
void SetStartTracingButtonEnabled(bool enable);
void SetStopTracingButtonEnabled(bool enable);
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index 09469f3c5..222c82b1c 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <QFileInfo>
#include <QHeaderView>
#include <QMenu>
#include <QThreadPool>
@@ -131,6 +132,14 @@ void GameList::LoadInterfaceLayout() {
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
}
+const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf",
+ "cci", "cxi", "app"};
+
+static bool HasSupportedFileExtension(const std::string& file_name) {
+ QFileInfo file = QFileInfo(file_name.c_str());
+ return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
+}
+
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
const std::string& virtual_name) -> bool {
@@ -139,7 +148,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
if (stop_processing)
return false; // Breaks the callback loop.
- if (!FileUtil::IsDirectory(physical_name)) {
+ if (!FileUtil::IsDirectory(physical_name) && HasSupportedFileExtension(physical_name)) {
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
if (!loader)
return true;
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index 1abf10051..e6b7eea0b 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -33,6 +33,8 @@ public:
void SaveInterfaceLayout();
void LoadInterfaceLayout();
+ static const QStringList supported_file_extensions;
+
signals:
void GameChosen(QString game_path);
void ShouldCancelWorker();
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index f765c0147..513da8001 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -54,18 +54,19 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
Pica::g_debug_context = Pica::DebugContext::Construct();
-
+ setAcceptDrops(true);
ui.setupUi(this);
statusBar()->hide();
InitializeWidgets();
- InitializeDebugMenuActions();
+ InitializeDebugWidgets();
InitializeRecentFileMenuActions();
InitializeHotkeys();
SetDefaultUIGeometry();
RestoreUIState();
+ ConnectMenuEvents();
ConnectWidgetEvents();
setWindowTitle(QString("Citra | %1-%2").arg(Common::g_scm_branch, Common::g_scm_desc));
@@ -75,7 +76,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
QStringList args = QApplication::arguments();
if (args.length() >= 2) {
- BootGame(args[1].toStdString());
+ BootGame(args[1]);
}
}
@@ -93,74 +94,85 @@ void GMainWindow::InitializeWidgets() {
game_list = new GameList();
ui.horizontalLayout->addWidget(game_list);
+}
+
+void GMainWindow::InitializeDebugWidgets() {
+ connect(ui.action_Create_Pica_Surface_Viewer, &QAction::triggered, this,
+ &GMainWindow::OnCreateGraphicsSurfaceViewer);
+
+ QMenu* debug_menu = ui.menu_View_Debugging;
profilerWidget = new ProfilerWidget(this);
addDockWidget(Qt::BottomDockWidgetArea, profilerWidget);
profilerWidget->hide();
+ debug_menu->addAction(profilerWidget->toggleViewAction());
#if MICROPROFILE_ENABLED
microProfileDialog = new MicroProfileDialog(this);
microProfileDialog->hide();
+ debug_menu->addAction(microProfileDialog->toggleViewAction());
#endif
disasmWidget = new DisassemblerWidget(this, emu_thread.get());
addDockWidget(Qt::BottomDockWidgetArea, disasmWidget);
disasmWidget->hide();
+ debug_menu->addAction(disasmWidget->toggleViewAction());
+ connect(this, &GMainWindow::EmulationStarting, disasmWidget,
+ &DisassemblerWidget::OnEmulationStarting);
+ connect(this, &GMainWindow::EmulationStopping, disasmWidget,
+ &DisassemblerWidget::OnEmulationStopping);
registersWidget = new RegistersWidget(this);
addDockWidget(Qt::RightDockWidgetArea, registersWidget);
registersWidget->hide();
+ debug_menu->addAction(registersWidget->toggleViewAction());
+ connect(this, &GMainWindow::EmulationStarting, registersWidget,
+ &RegistersWidget::OnEmulationStarting);
+ connect(this, &GMainWindow::EmulationStopping, registersWidget,
+ &RegistersWidget::OnEmulationStopping);
callstackWidget = new CallstackWidget(this);
addDockWidget(Qt::RightDockWidgetArea, callstackWidget);
callstackWidget->hide();
+ debug_menu->addAction(callstackWidget->toggleViewAction());
graphicsWidget = new GPUCommandStreamWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsWidget);
graphicsWidget->hide();
+ debug_menu->addAction(graphicsWidget->toggleViewAction());
graphicsCommandsWidget = new GPUCommandListWidget(this);
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
graphicsCommandsWidget->hide();
+ debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
graphicsBreakpointsWidget->hide();
+ debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
graphicsVertexShaderWidget = new GraphicsVertexShaderWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsVertexShaderWidget);
graphicsVertexShaderWidget->hide();
+ debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
graphicsTracingWidget = new GraphicsTracingWidget(Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget);
graphicsTracingWidget->hide();
+ debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
+ connect(this, &GMainWindow::EmulationStarting, graphicsTracingWidget,
+ &GraphicsTracingWidget::OnEmulationStarting);
+ connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget,
+ &GraphicsTracingWidget::OnEmulationStopping);
waitTreeWidget = new WaitTreeWidget(this);
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
waitTreeWidget->hide();
-}
-
-void GMainWindow::InitializeDebugMenuActions() {
- auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica Surface Viewer"), this);
- connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this,
- SLOT(OnCreateGraphicsSurfaceViewer()));
-
- QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
- debug_menu->addAction(graphicsSurfaceViewerAction);
- debug_menu->addSeparator();
- debug_menu->addAction(profilerWidget->toggleViewAction());
-#if MICROPROFILE_ENABLED
- debug_menu->addAction(microProfileDialog->toggleViewAction());
-#endif
- debug_menu->addAction(disasmWidget->toggleViewAction());
- debug_menu->addAction(registersWidget->toggleViewAction());
- debug_menu->addAction(callstackWidget->toggleViewAction());
- debug_menu->addAction(graphicsWidget->toggleViewAction());
- debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
- debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
- debug_menu->addAction(graphicsVertexShaderWidget->toggleViewAction());
- debug_menu->addAction(graphicsTracingWidget->toggleViewAction());
debug_menu->addAction(waitTreeWidget->toggleViewAction());
+ connect(this, &GMainWindow::EmulationStarting, waitTreeWidget,
+ &WaitTreeWidget::OnEmulationStarting);
+ connect(this, &GMainWindow::EmulationStopping, waitTreeWidget,
+ &WaitTreeWidget::OnEmulationStopping);
}
void GMainWindow::InitializeRecentFileMenuActions() {
@@ -215,41 +227,40 @@ void GMainWindow::RestoreUIState() {
ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
ToggleWindowMode();
- ui.actionDisplay_widget_title_bars->setChecked(UISettings::values.display_titlebar);
- OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked());
+ ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar);
+ OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
}
void GMainWindow::ConnectWidgetEvents() {
- connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)),
- Qt::DirectConnection);
+ connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)));
connect(game_list, SIGNAL(OpenSaveFolderRequested(u64)), this,
- SLOT(OnGameListOpenSaveFolder(u64)), Qt::DirectConnection);
- connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure()));
- connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),
- Qt::DirectConnection);
- connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap()));
- connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this,
- SLOT(OnMenuSelectGameListRoot()));
- connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
- connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
- connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
- connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
-
- connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget,
- SLOT(OnEmulationStarting(EmuThread*)));
- connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
- connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget,
- SLOT(OnEmulationStarting(EmuThread*)));
- connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping()));
+ SLOT(OnGameListOpenSaveFolder(u64)));
+
connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window,
SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
- connect(this, SIGNAL(EmulationStarting(EmuThread*)), graphicsTracingWidget,
- SLOT(OnEmulationStarting(EmuThread*)));
- connect(this, SIGNAL(EmulationStopping()), graphicsTracingWidget, SLOT(OnEmulationStopping()));
- connect(this, SIGNAL(EmulationStarting(EmuThread*)), waitTreeWidget,
- SLOT(OnEmulationStarting(EmuThread*)));
- connect(this, SIGNAL(EmulationStopping()), waitTreeWidget, SLOT(OnEmulationStopping()));
+}
+
+void GMainWindow::ConnectMenuEvents() {
+ // File
+ connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
+ connect(ui.action_Load_Symbol_Map, &QAction::triggered, this,
+ &GMainWindow::OnMenuLoadSymbolMap);
+ connect(ui.action_Select_Game_List_Root, &QAction::triggered, this,
+ &GMainWindow::OnMenuSelectGameListRoot);
+ connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
+
+ // Emulation
+ connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
+ connect(ui.action_Pause, &QAction::triggered, this, &GMainWindow::OnPauseGame);
+ connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame);
+ connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
+
+ // View
+ connect(ui.action_Single_Window_Mode, &QAction::triggered, this,
+ &GMainWindow::ToggleWindowMode);
+ connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this,
+ &GMainWindow::OnDisplayTitleBars);
}
void GMainWindow::OnDisplayTitleBars(bool show) {
@@ -272,7 +283,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
}
}
-bool GMainWindow::LoadROM(const std::string& filename) {
+bool GMainWindow::LoadROM(const QString& filename) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
@@ -290,12 +301,13 @@ bool GMainWindow::LoadROM(const std::string& filename) {
Core::System& system{Core::System::GetInstance()};
- const Core::System::ResultStatus result{system.Load(render_window, filename)};
+ const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())};
if (result != Core::System::ResultStatus::Success) {
switch (result) {
case Core::System::ResultStatus::ErrorGetLoader:
- LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filename.c_str());
+ LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!",
+ filename.toStdString().c_str());
QMessageBox::critical(this, tr("Error while loading ROM!"),
tr("The ROM format is not supported."));
break;
@@ -335,7 +347,7 @@ bool GMainWindow::LoadROM(const std::string& filename) {
return true;
}
-void GMainWindow::BootGame(const std::string& filename) {
+void GMainWindow::BootGame(const QString& filename) {
LOG_INFO(Frontend, "Citra starting...");
StoreRecentFile(filename); // Put the filename on top of the list
@@ -411,8 +423,8 @@ void GMainWindow::ShutdownGame() {
emulation_running = false;
}
-void GMainWindow::StoreRecentFile(const std::string& filename) {
- UISettings::values.recent_files.prepend(QString::fromStdString(filename));
+void GMainWindow::StoreRecentFile(const QString& filename) {
+ UISettings::values.recent_files.prepend(filename);
UISettings::values.recent_files.removeDuplicates();
while (UISettings::values.recent_files.size() > max_recent_files_item) {
UISettings::values.recent_files.removeLast();
@@ -447,7 +459,7 @@ void GMainWindow::UpdateRecentFiles() {
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
- BootGame(game_path.toStdString());
+ BootGame(game_path);
}
void GMainWindow::OnGameListOpenSaveFolder(u64 program_id) {
@@ -466,19 +478,25 @@ void GMainWindow::OnGameListOpenSaveFolder(u64 program_id) {
}
void GMainWindow::OnMenuLoadFile() {
- QString filename =
- QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path,
- tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
+ QString extensions;
+ for (const auto& piece : game_list->supported_file_extensions)
+ extensions += "*." + piece + " ";
+
+ QString file_filter = tr("3DS Executable") + " (" + extensions + ")";
+ file_filter += ";;" + tr("All Files (*.*)");
+
+ QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
+ UISettings::values.roms_path, file_filter);
if (!filename.isEmpty()) {
UISettings::values.roms_path = QFileInfo(filename).path();
- BootGame(filename.toStdString());
+ BootGame(filename);
}
}
void GMainWindow::OnMenuLoadSymbolMap() {
QString filename = QFileDialog::getOpenFileName(
- this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)"));
+ this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol Map (*.*)"));
if (!filename.isEmpty()) {
UISettings::values.symbols_path = QFileInfo(filename).path();
@@ -501,7 +519,7 @@ void GMainWindow::OnMenuRecentFile() {
QString filename = action->data().toString();
QFileInfo file_info(filename);
if (file_info.exists()) {
- BootGame(filename.toStdString());
+ BootGame(filename);
} else {
// Display an error message and remove the file from the list.
QMessageBox::information(this, tr("File not found"),
@@ -605,7 +623,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
UISettings::values.microprofile_visible = microProfileDialog->isVisible();
#endif
UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked();
- UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked();
+ UISettings::values.display_titlebar = ui.action_Display_Dock_Widget_Headers->isChecked();
UISettings::values.first_start = false;
game_list->SaveInterfaceLayout();
@@ -620,6 +638,40 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
QWidget::closeEvent(event);
}
+static bool IsSingleFileDropEvent(QDropEvent* event) {
+ const QMimeData* mimeData = event->mimeData();
+ return mimeData->hasUrls() && mimeData->urls().length() == 1;
+}
+
+void GMainWindow::dropEvent(QDropEvent* event) {
+ if (IsSingleFileDropEvent(event) && ConfirmChangeGame()) {
+ const QMimeData* mimeData = event->mimeData();
+ QString filename = mimeData->urls().at(0).toLocalFile();
+ BootGame(filename);
+ }
+}
+
+void GMainWindow::dragEnterEvent(QDragEnterEvent* event) {
+ if (IsSingleFileDropEvent(event)) {
+ event->acceptProposedAction();
+ }
+}
+
+void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
+ event->acceptProposedAction();
+}
+
+bool GMainWindow::ConfirmChangeGame() {
+ if (emu_thread == nullptr)
+ return true;
+
+ auto answer = QMessageBox::question(
+ this, tr("Citra"),
+ tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ return answer != QMessageBox::No;
+}
+
#ifdef main
#undef main
#endif
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index a2fd45c47..87637b92b 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -64,7 +64,7 @@ signals:
private:
void InitializeWidgets();
- void InitializeDebugMenuActions();
+ void InitializeDebugWidgets();
void InitializeRecentFileMenuActions();
void InitializeHotkeys();
@@ -72,15 +72,10 @@ private:
void RestoreUIState();
void ConnectWidgetEvents();
+ void ConnectMenuEvents();
- /**
- * Initializes the emulation system.
- * @param system_mode The system mode with which to intialize the kernel.
- * @returns Whether the system was properly initialized.
- */
- bool InitializeSystem(u32 system_mode);
- bool LoadROM(const std::string& filename);
- void BootGame(const std::string& filename);
+ bool LoadROM(const QString& filename);
+ void BootGame(const QString& filename);
void ShutdownGame();
/**
@@ -94,7 +89,7 @@ private:
*
* @param filename the filename to store
*/
- void StoreRecentFile(const std::string& filename);
+ void StoreRecentFile(const QString& filename);
/**
* Updates the recent files menu.
@@ -110,6 +105,7 @@ private:
* @return true if the user confirmed
*/
bool ConfirmClose();
+ bool ConfirmChangeGame();
void closeEvent(QCloseEvent* event) override;
private slots:
@@ -155,6 +151,11 @@ private:
WaitTreeWidget* waitTreeWidget;
QAction* actions_recent_files[max_recent_files_item];
+
+protected:
+ void dropEvent(QDropEvent* event) override;
+ void dragEnterEvent(QDragEnterEvent* event) override;
+ void dragMoveEvent(QDragMoveEvent* event) override;
};
#endif // _CITRA_QT_MAIN_HXX_
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index adfa3689e..4a95cda9a 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -79,8 +79,16 @@
<property name="title">
<string>&amp;View</string>
</property>
+ <widget class="QMenu" name="menu_View_Debugging">
+ <property name="title">
+ <string>Debugging</string>
+ </property>
+ <addaction name="action_Create_Pica_Surface_Viewer"/>
+ <addaction name="separator"/>
+ </widget>
<addaction name="action_Single_Window_Mode"/>
- <addaction name="actionDisplay_widget_title_bars"/>
+ <addaction name="action_Display_Dock_Widget_Headers"/>
+ <addaction name="menu_View_Debugging"/>
</widget>
<widget class="QMenu" name="menu_Help">
<property name="title">
@@ -151,7 +159,7 @@
<string>Configure...</string>
</property>
</action>
- <action name="actionDisplay_widget_title_bars">
+ <action name="action_Display_Dock_Widget_Headers">
<property name="checkable">
<bool>true</bool>
</property>
@@ -167,44 +175,11 @@
<string>Selects a folder to display in the game list</string>
</property>
</action>
+ <action name="action_Create_Pica_Surface_Viewer">
+ <property name="text">
+ <string>Create Pica Surface Viewer</string>
+ </property>
+ </action>
</widget>
<resources/>
- <connections>
- <connection>
- <sender>action_Exit</sender>
- <signal>triggered()</signal>
- <receiver>MainWindow</receiver>
- <slot>close()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>-1</x>
- <y>-1</y>
- </hint>
- <hint type="destinationlabel">
- <x>367</x>
- <y>314</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>actionDisplay_widget_title_bars</sender>
- <signal>triggered(bool)</signal>
- <receiver>MainWindow</receiver>
- <slot>OnDisplayTitleBars(bool)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>-1</x>
- <y>-1</y>
- </hint>
- <hint type="destinationlabel">
- <x>540</x>
- <y>364</y>
- </hint>
- </hints>
- </connection>
- </connections>
- <slots>
- <slot>OnConfigure()</slot>
- <slot>OnDisplayTitleBars(bool)</slot>
- </slots>
</ui>
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 2ef3e6b05..0f0354821 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -55,6 +55,7 @@ namespace Log {
SUB(Service, DSP) \
SUB(Service, DLP) \
SUB(Service, HID) \
+ SUB(Service, HTTP) \
SUB(Service, SOC) \
SUB(Service, IR) \
SUB(Service, Y2R) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 4330ef879..f0ec922d2 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -72,6 +72,7 @@ enum class Class : ClassType {
Service_DSP, ///< The DSP (DSP control) service
Service_DLP, ///< The DLP (Download Play) service
Service_HID, ///< The HID (Human interface device) service
+ Service_HTTP, ///< The HTTP service
Service_SOC, ///< The SOC (Socket) service
Service_IR, ///< The IR service
Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b178914dd..5332e35a3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -20,10 +20,10 @@ set(SRCS
file_sys/archive_extsavedata.cpp
file_sys/archive_ncch.cpp
file_sys/archive_other_savedata.cpp
- file_sys/archive_romfs.cpp
file_sys/archive_savedata.cpp
file_sys/archive_sdmc.cpp
file_sys/archive_sdmcwriteonly.cpp
+ file_sys/archive_selfncch.cpp
file_sys/archive_source_sd_savedata.cpp
file_sys/archive_systemsavedata.cpp
file_sys/disk_archive.cpp
@@ -197,14 +197,15 @@ set(HEADERS
file_sys/archive_extsavedata.h
file_sys/archive_ncch.h
file_sys/archive_other_savedata.h
- file_sys/archive_romfs.h
file_sys/archive_savedata.h
file_sys/archive_sdmc.h
file_sys/archive_sdmcwriteonly.h
+ file_sys/archive_selfncch.h
file_sys/archive_source_sd_savedata.h
file_sys/archive_systemsavedata.h
file_sys/directory_backend.h
file_sys/disk_archive.h
+ file_sys/errors.h
file_sys/file_backend.h
file_sys/ivfc_archive.h
file_sys/path_parser.h
@@ -360,9 +361,10 @@ set(HEADERS
)
include_directories(../../externals/dynarmic/include)
+include_directories(../../externals/cryptopp)
create_directory_groups(${SRCS} ${HEADERS})
add_library(core STATIC ${SRCS} ${HEADERS})
-target_link_libraries(core dynarmic)
+target_link_libraries(core dynarmic cryptopp)
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 202cd332b..c9c9b7615 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -67,10 +67,6 @@ System::ResultStatus System::SingleStep() {
}
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
- if (app_loader) {
- app_loader.reset();
- }
-
app_loader = Loader::GetLoader(filepath);
if (!app_loader) {
@@ -123,10 +119,6 @@ void System::Reschedule() {
}
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
- if (cpu_core) {
- cpu_core.reset();
- }
-
Memory::Init();
if (Settings::values.use_cpu_jit) {
@@ -159,7 +151,8 @@ void System::Shutdown() {
Kernel::Shutdown();
HW::Shutdown();
CoreTiming::Shutdown();
- cpu_core.reset();
+ cpu_core = nullptr;
+ app_loader = nullptr;
LOG_DEBUG(Core, "Shutdown OK");
}
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp
deleted file mode 100644
index 6c99ca5b4..000000000
--- a/src/core/file_sys/archive_romfs.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <memory>
-#include "common/common_types.h"
-#include "common/logging/log.h"
-#include "core/file_sys/archive_romfs.h"
-#include "core/file_sys/ivfc_archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-ArchiveFactory_RomFS::ArchiveFactory_RomFS(Loader::AppLoader& app_loader) {
- // Load the RomFS from the app
- if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
- LOG_ERROR(Service_FS, "Unable to read RomFS!");
- }
-}
-
-ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) {
- auto archive = std::make_unique<IVFCArchive>(romfs_file, data_offset, data_size);
- return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
-}
-
-ResultCode ArchiveFactory_RomFS::Format(const Path& path,
- const FileSys::ArchiveFormatInfo& format_info) {
- LOG_ERROR(Service_FS, "Attempted to format a RomFS archive.");
- // TODO: Verify error code
- return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
- ErrorLevel::Permanent);
-}
-
-ResultVal<ArchiveFormatInfo> ArchiveFactory_RomFS::GetFormatInfo(const Path& path) const {
- // TODO(Subv): Implement
- LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
- return ResultCode(-1);
-}
-
-} // namespace FileSys
diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp
new file mode 100644
index 000000000..298a37a44
--- /dev/null
+++ b/src/core/file_sys/archive_selfncch.cpp
@@ -0,0 +1,257 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "common/swap.h"
+#include "core/file_sys/archive_selfncch.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/ivfc_archive.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+enum class SelfNCCHFilePathType : u32 {
+ RomFS = 0,
+ Code = 1, // This is not supported by SelfNCCHArchive but by archive 0x2345678E
+ ExeFS = 2,
+ UpdateRomFS = 5, // This is presumably for accessing the RomFS of the update patch.
+};
+
+struct SelfNCCHFilePath {
+ u32_le type;
+ std::array<char, 8> exefs_filename;
+};
+static_assert(sizeof(SelfNCCHFilePath) == 12, "NCCHFilePath has wrong size!");
+
+// A read-only file created from a block of data. It only allows you to read the entire file at
+// once, in a single read operation.
+class ExeFSSectionFile final : public FileBackend {
+public:
+ explicit ExeFSSectionFile(std::shared_ptr<std::vector<u8>> data_) : data(std::move(data_)) {}
+
+ ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override {
+ if (offset != 0) {
+ LOG_ERROR(Service_FS, "offset must be zero!");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ if (length != data->size()) {
+ LOG_ERROR(Service_FS, "size must match the file size!");
+ return ERROR_INCORRECT_EXEFS_READ_SIZE;
+ }
+
+ std::memcpy(buffer, data->data(), data->size());
+ return MakeResult<size_t>(data->size());
+ }
+
+ ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
+ const u8* buffer) const override {
+ LOG_ERROR(Service_FS, "The file is read-only!");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ u64 GetSize() const override {
+ return data->size();
+ }
+
+ bool SetSize(u64 size) const override {
+ return false;
+ }
+
+ bool Close() const override {
+ return true;
+ }
+
+ void Flush() const override {}
+
+private:
+ std::shared_ptr<std::vector<u8>> data;
+};
+
+// SelfNCCHArchive represents the running application itself. From this archive the application can
+// open RomFS and ExeFS, excluding the .code section.
+class SelfNCCHArchive final : public ArchiveBackend {
+public:
+ explicit SelfNCCHArchive(const NCCHData& ncch_data_) : ncch_data(ncch_data_) {}
+
+ std::string GetName() const override {
+ return "SelfNCCHArchive";
+ }
+
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode&) const override {
+ // Note: SelfNCCHArchive doesn't check the open mode.
+
+ if (path.GetType() != LowPathType::Binary) {
+ LOG_ERROR(Service_FS, "Path need to be Binary");
+ return ERROR_INVALID_PATH;
+ }
+
+ std::vector<u8> binary = path.AsBinary();
+ if (binary.size() != sizeof(SelfNCCHFilePath)) {
+ LOG_ERROR(Service_FS, "Wrong path size %zu", binary.size());
+ return ERROR_INVALID_PATH;
+ }
+
+ SelfNCCHFilePath file_path;
+ std::memcpy(&file_path, binary.data(), sizeof(SelfNCCHFilePath));
+
+ switch (static_cast<SelfNCCHFilePathType>(file_path.type)) {
+ case SelfNCCHFilePathType::UpdateRomFS:
+ LOG_WARNING(Service_FS, "(STUBBED) open update RomFS");
+ return OpenRomFS();
+
+ case SelfNCCHFilePathType::RomFS:
+ return OpenRomFS();
+
+ case SelfNCCHFilePathType::Code:
+ LOG_ERROR(Service_FS, "Reading the code section is not supported!");
+ return ERROR_COMMAND_NOT_ALLOWED;
+
+ case SelfNCCHFilePathType::ExeFS: {
+ const auto& raw = file_path.exefs_filename;
+ auto end = std::find(raw.begin(), raw.end(), '\0');
+ std::string filename(raw.begin(), end);
+ return OpenExeFS(filename);
+ }
+ default:
+ LOG_ERROR(Service_FS, "Unknown file type %u!", static_cast<u32>(file_path.type));
+ return ERROR_INVALID_PATH;
+ }
+ }
+
+ ResultCode DeleteFile(const Path& path) const override {
+ LOG_ERROR(Service_FS, "Unsupported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override {
+ LOG_ERROR(Service_FS, "Unsupported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ ResultCode DeleteDirectory(const Path& path) const override {
+ LOG_ERROR(Service_FS, "Unsupported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ ResultCode DeleteDirectoryRecursively(const Path& path) const override {
+ LOG_ERROR(Service_FS, "Unsupported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ ResultCode CreateFile(const Path& path, u64 size) const override {
+ LOG_ERROR(Service_FS, "Unsupported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ ResultCode CreateDirectory(const Path& path) const override {
+ LOG_ERROR(Service_FS, "Unsupported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override {
+ LOG_ERROR(Service_FS, "Unsupported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override {
+ LOG_ERROR(Service_FS, "Unsupported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ u64 GetFreeBytes() const override {
+ return 0;
+ }
+
+private:
+ ResultVal<std::unique_ptr<FileBackend>> OpenRomFS() const {
+ if (ncch_data.romfs_file) {
+ return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(
+ ncch_data.romfs_file, ncch_data.romfs_offset, ncch_data.romfs_size));
+ } else {
+ LOG_INFO(Service_FS, "Unable to read RomFS");
+ return ERROR_ROMFS_NOT_FOUND;
+ }
+ }
+
+ ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const {
+ if (filename == "icon") {
+ if (ncch_data.icon) {
+ return MakeResult<std::unique_ptr<FileBackend>>(
+ std::make_unique<ExeFSSectionFile>(ncch_data.icon));
+ }
+
+ LOG_WARNING(Service_FS, "Unable to read icon");
+ return ERROR_EXEFS_SECTION_NOT_FOUND;
+ }
+
+ if (filename == "logo") {
+ if (ncch_data.logo) {
+ return MakeResult<std::unique_ptr<FileBackend>>(
+ std::make_unique<ExeFSSectionFile>(ncch_data.logo));
+ }
+
+ LOG_WARNING(Service_FS, "Unable to read logo");
+ return ERROR_EXEFS_SECTION_NOT_FOUND;
+ }
+
+ if (filename == "banner") {
+ if (ncch_data.banner) {
+ return MakeResult<std::unique_ptr<FileBackend>>(
+ std::make_unique<ExeFSSectionFile>(ncch_data.banner));
+ }
+
+ LOG_WARNING(Service_FS, "Unable to read banner");
+ return ERROR_EXEFS_SECTION_NOT_FOUND;
+ }
+
+ LOG_ERROR(Service_FS, "Unknown ExeFS section %s!", filename.c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ NCCHData ncch_data;
+};
+
+ArchiveFactory_SelfNCCH::ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader) {
+ std::shared_ptr<FileUtil::IOFile> romfs_file_;
+ if (Loader::ResultStatus::Success ==
+ app_loader.ReadRomFS(romfs_file_, ncch_data.romfs_offset, ncch_data.romfs_size)) {
+
+ ncch_data.romfs_file = std::move(romfs_file_);
+ }
+
+ std::vector<u8> buffer;
+
+ if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer))
+ ncch_data.icon = std::make_shared<std::vector<u8>>(std::move(buffer));
+
+ buffer.clear();
+ if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer))
+ ncch_data.logo = std::make_shared<std::vector<u8>>(std::move(buffer));
+
+ buffer.clear();
+ if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer))
+ ncch_data.banner = std::make_shared<std::vector<u8>>(std::move(buffer));
+}
+
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) {
+ auto archive = std::make_unique<SelfNCCHArchive>(ncch_data);
+ return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
+}
+
+ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) {
+ LOG_ERROR(Service_FS, "Attempted to format a SelfNCCH archive.");
+ return ERROR_INVALID_PATH;
+}
+
+ResultVal<ArchiveFormatInfo> ArchiveFactory_SelfNCCH::GetFormatInfo(const Path&) const {
+ LOG_ERROR(Service_FS, "Attempted to get format info of a SelfNCCH archive");
+ return ERROR_INVALID_PATH;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_selfncch.h
index 1eaf99b54..f1b971296 100644
--- a/src/core/file_sys/archive_romfs.h
+++ b/src/core/file_sys/archive_selfncch.h
@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -17,22 +17,29 @@
namespace FileSys {
-/// File system interface to the RomFS archive
-class ArchiveFactory_RomFS final : public ArchiveFactory {
+struct NCCHData {
+ std::shared_ptr<std::vector<u8>> icon;
+ std::shared_ptr<std::vector<u8>> logo;
+ std::shared_ptr<std::vector<u8>> banner;
+ std::shared_ptr<FileUtil::IOFile> romfs_file;
+ u64 romfs_offset = 0;
+ u64 romfs_size = 0;
+};
+
+/// File system interface to the SelfNCCH archive
+class ArchiveFactory_SelfNCCH final : public ArchiveFactory {
public:
- explicit ArchiveFactory_RomFS(Loader::AppLoader& app_loader);
+ explicit ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader);
std::string GetName() const override {
- return "RomFS";
+ return "SelfNCCH";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
- std::shared_ptr<FileUtil::IOFile> romfs_file;
- u64 data_offset;
- u64 data_size;
+ NCCHData ncch_data;
};
} // namespace FileSys
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index 4d5f62b08..9fc8d753b 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -39,5 +39,15 @@ const ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrorDescription::FS_DirectoryNotEmpt
const ResultCode ERROR_GAMECARD_NOT_INSERTED(ErrorDescription::FS_GameCardNotInserted,
ErrorModule::FS, ErrorSummary::NotFound,
ErrorLevel::Status);
+const ResultCode ERROR_INCORRECT_EXEFS_READ_SIZE(ErrorDescription::FS_IncorrectExeFSReadSize,
+ ErrorModule::FS, ErrorSummary::NotSupported,
+ ErrorLevel::Usage);
+const ResultCode ERROR_ROMFS_NOT_FOUND(ErrorDescription::FS_RomFSNotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+const ResultCode ERROR_COMMAND_NOT_ALLOWED(ErrorDescription::FS_CommandNotAllowed, ErrorModule::FS,
+ ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+const ResultCode ERROR_EXEFS_SECTION_NOT_FOUND(ErrorDescription::FS_ExeFSSectionNotFound,
+ ErrorModule::FS, ErrorSummary::NotFound,
+ ErrorLevel::Status);
} // namespace FileSys
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 4f0f786ce..6b4637741 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -70,14 +70,12 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
(framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
touch_pressed = true;
- pad_state.touch.Assign(1);
}
void EmuWindow::TouchReleased() {
touch_pressed = false;
touch_x = 0;
touch_y = 0;
- pad_state.touch.Assign(0);
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 4535b61c0..bbaae8b79 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -77,7 +77,7 @@ union Header {
*/
inline u32 MakeHeader(u16 command_id, unsigned int normal_params_size,
unsigned int translate_params_size) {
- Header header;
+ Header header{};
header.command_id.Assign(command_id);
header.normal_params_size.Assign(normal_params_size);
header.translate_params_size.Assign(translate_params_size);
@@ -112,7 +112,7 @@ union StaticBufferDescInfo {
};
inline u32 StaticBufferDesc(u32 size, u8 buffer_id) {
- StaticBufferDescInfo info;
+ StaticBufferDescInfo info{};
info.descriptor_type.Assign(StaticBuffer);
info.buffer_id.Assign(buffer_id);
info.size.Assign(size);
@@ -150,7 +150,7 @@ union MappedBufferDescInfo {
};
inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) {
- MappedBufferDescInfo info;
+ MappedBufferDescInfo info{};
info.flags.Assign(MappedBuffer);
info.perms.Assign(perms);
info.size.Assign(size);
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 53864a3a7..cfefbbc64 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -20,6 +20,7 @@ enum class ErrorDescription : u32 {
OS_InvalidBufferDescriptor = 48,
MaxConnectionsReached = 52,
WrongAddress = 53,
+ FS_RomFSNotFound = 100,
FS_ArchiveNotMounted = 101,
FS_FileNotFound = 112,
FS_PathNotFound = 113,
@@ -35,10 +36,13 @@ enum class ErrorDescription : u32 {
OutofRangeOrMisalignedAddress =
513, // TODO(purpasmart): Check if this name fits its actual usage
GPU_FirstInitialization = 519,
+ FS_ExeFSSectionNotFound = 567,
+ FS_CommandNotAllowed = 630,
FS_InvalidReadFlag = 700,
FS_InvalidPath = 702,
FS_WriteBeyondEnd = 705,
FS_UnsupportedOpenFlags = 760,
+ FS_IncorrectExeFSReadSize = 761,
FS_UnexpectedFileOrDirectory = 770,
InvalidSection = 1000,
TooLarge = 1001,
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 519c1f3a9..2ea956e0b 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -26,7 +26,7 @@ namespace FS {
/// Supported archive types
enum class ArchiveIdCode : u32 {
- RomFS = 0x00000003,
+ SelfNCCH = 0x00000003,
SaveData = 0x00000004,
ExtSaveData = 0x00000006,
SharedExtSaveData = 0x00000007,
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index f14ab3811..fb3acb507 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -32,8 +32,8 @@ static u32 next_touch_index;
static u32 next_accelerometer_index;
static u32 next_gyroscope_index;
-static int enable_accelerometer_count = 0; // positive means enabled
-static int enable_gyroscope_count = 0; // positive means enabled
+static int enable_accelerometer_count; // positive means enabled
+static int enable_gyroscope_count; // positive means enabled
static int pad_update_event;
static int accelerometer_update_event;
@@ -323,6 +323,9 @@ void Init() {
next_accelerometer_index = 0;
next_gyroscope_index = 0;
+ enable_accelerometer_count = 0;
+ enable_gyroscope_count = 0;
+
// Create event handles
event_pad_or_touch_1 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch1");
event_pad_or_touch_2 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch2");
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 21e66dfe0..c7f4ee138 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -42,8 +42,6 @@ struct PadState {
BitField<14, 1, u32> zl;
BitField<15, 1, u32> zr;
- BitField<20, 1, u32> touch;
-
BitField<24, 1, u32> c_right;
BitField<25, 1, u32> c_left;
BitField<26, 1, u32> c_up;
@@ -203,8 +201,6 @@ const PadState PAD_Y = {{1u << 11}};
const PadState PAD_ZL = {{1u << 14}};
const PadState PAD_ZR = {{1u << 15}};
-const PadState PAD_TOUCH = {{1u << 20}};
-
const PadState PAD_C_RIGHT = {{1u << 24}};
const PadState PAD_C_LEFT = {{1u << 25}};
const PadState PAD_C_UP = {{1u << 26}};
diff --git a/src/core/hle/service/ir/ir_u.cpp b/src/core/hle/service/ir/ir_u.cpp
index 429615f31..ce00d5732 100644
--- a/src/core/hle/service/ir/ir_u.cpp
+++ b/src/core/hle/service/ir/ir_u.cpp
@@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00100000, nullptr, "GetErrorStatus"},
{0x00110040, nullptr, "SetSleepModeActive"},
{0x00120040, nullptr, "SetSleepModeState"},
- // clang-format off
+ // clang-format on
};
IR_U_Interface::IR_U_Interface() {
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 0be94322c..63c334cb2 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -19,7 +19,7 @@ void CheckSysUpdateAvailable(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 0; // No update available
- LOG_WARNING(Service_NWM, "(STUBBED) called");
+ LOG_WARNING(Service_NIM, "(STUBBED) called");
}
void Init() {
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 09266e8b0..74e336487 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -5,7 +5,7 @@
#include <algorithm>
#include <vector>
#include "common/logging/log.h"
-#include "core/file_sys/archive_romfs.h"
+#include "core/file_sys/archive_selfncch.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/fs/archive.h"
@@ -277,8 +277,8 @@ ResultStatus AppLoader_THREEDSX::Load() {
Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE);
- Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this),
- Service::FS::ArchiveIdCode::RomFS);
+ Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_SelfNCCH>(*this),
+ Service::FS::ArchiveIdCode::SelfNCCH);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 5df33f6d2..98b8259d9 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -8,7 +8,7 @@
#include "common/logging/log.h"
#include "common/string_util.h"
#include "common/swap.h"
-#include "core/file_sys/archive_romfs.h"
+#include "core/file_sys/archive_selfncch.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/cfg/cfg.h"
@@ -342,8 +342,8 @@ ResultStatus AppLoader_NCCH::Load() {
if (ResultStatus::Success != result)
return result;
- Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this),
- Service::FS::ArchiveIdCode::RomFS);
+ Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_SelfNCCH>(*this),
+ Service::FS::ArchiveIdCode::SelfNCCH);
ParseRegionLockoutInfo();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 11bc61e14..5317719e8 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,10 +1,8 @@
set(SRCS
- clipper.cpp
command_processor.cpp
debug_utils/debug_utils.cpp
pica.cpp
primitive_assembly.cpp
- rasterizer.cpp
regs.cpp
renderer_base.cpp
renderer_opengl/gl_rasterizer.cpp
@@ -15,7 +13,11 @@ set(SRCS
renderer_opengl/renderer_opengl.cpp
shader/shader.cpp
shader/shader_interpreter.cpp
- swrasterizer.cpp
+ swrasterizer/clipper.cpp
+ swrasterizer/framebuffer.cpp
+ swrasterizer/rasterizer.cpp
+ swrasterizer/swrasterizer.cpp
+ swrasterizer/texturing.cpp
texture/etc1.cpp
texture/texture_decode.cpp
vertex_loader.cpp
@@ -23,7 +25,6 @@ set(SRCS
)
set(HEADERS
- clipper.h
command_processor.h
debug_utils/debug_utils.h
gpu_debugger.h
@@ -31,7 +32,6 @@ set(HEADERS
pica_state.h
pica_types.h
primitive_assembly.h
- rasterizer.h
rasterizer_interface.h
regs.h
regs_framebuffer.h
@@ -52,7 +52,11 @@ set(HEADERS
shader/debug_data.h
shader/shader.h
shader/shader_interpreter.h
- swrasterizer.h
+ swrasterizer/clipper.h
+ swrasterizer/framebuffer.h
+ swrasterizer/rasterizer.h
+ swrasterizer/swrasterizer.h
+ swrasterizer/texturing.h
texture/etc1.h
texture/texture_decode.h
utils.h
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 91c0ca4e6..2e32ff905 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -21,6 +21,8 @@
#include "video_core/primitive_assembly.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/regs.h"
+#include "video_core/regs_pipeline.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_base.h"
#include "video_core/shader/shader.h"
#include "video_core/vertex_loader.h"
@@ -49,19 +51,23 @@ MICROPROFILE_DEFINE(GPU_Drawing, "GPU", "Drawing", MP_RGB(50, 50, 240));
static void WritePicaReg(u32 id, u32 value, u32 mask) {
auto& regs = g_state.regs;
- if (id >= regs.NumIds())
+ if (id >= Regs::NUM_REGS) {
+ LOG_ERROR(HW_GPU,
+ "Commandlist tried to write to invalid register 0x%03X (value: %08X, mask: %X)",
+ id, value, mask);
return;
+ }
// TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value
- u32 old_value = regs[id];
+ u32 old_value = regs.reg_array[id];
const u32 write_mask = expand_bits_to_bytes[mask];
- regs[id] = (old_value & ~write_mask) | (value & write_mask);
+ regs.reg_array[id] = (old_value & ~write_mask) | (value & write_mask);
// Double check for is_pica_tracing to avoid call overhead
if (DebugUtils::IsPicaTracing()) {
- DebugUtils::OnPicaRegWrite({(u16)id, (u16)mask, regs[id]});
+ DebugUtils::OnPicaRegWrite({(u16)id, (u16)mask, regs.reg_array[id]});
}
if (g_debug_context)
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index e164e83a1..47dbc8cc8 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -32,7 +32,9 @@
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
#include "video_core/rasterizer_interface.h"
-#include "video_core/regs.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_base.h"
#include "video_core/shader/shader.h"
#include "video_core/texture/texture_decode.h"
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index fd94bdbb8..89e418e27 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -17,7 +17,9 @@
#include <vector>
#include "common/common_types.h"
#include "common/vector_math.h"
-#include "video_core/regs.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
+#include "video_core/regs_texturing.h"
namespace CiTrace {
class Recorder;
diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp
index 13f0a4ab9..b95148a6a 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -5,7 +5,7 @@
#include <cstring>
#include "video_core/pica.h"
#include "video_core/pica_state.h"
-#include "video_core/regs.h"
+#include "video_core/regs_pipeline.h"
namespace Pica {
diff --git a/src/video_core/regs.cpp b/src/video_core/regs.cpp
index f47e9e763..2699e710a 100644
--- a/src/video_core/regs.cpp
+++ b/src/video_core/regs.cpp
@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <iterator>
-#include <unordered_map>
#include <utility>
#include "common/common_types.h"
@@ -474,19 +474,14 @@ static const std::pair<u16, const char*> register_names[] = {
{0x2DD, "GPUREG_VSH_OPDESCS_DATA7"},
};
-std::string Regs::GetCommandName(int index) {
- static std::unordered_map<u32, const char*> map;
-
- if (map.empty()) {
- map.insert(std::begin(register_names), std::end(register_names));
- }
-
- // Return empty string if no match is found
- auto it = map.find(index);
- if (it != map.end()) {
- return it->second;
+const char* Regs::GetRegisterName(u16 index) {
+ auto found = std::lower_bound(std::begin(register_names), std::end(register_names), index,
+ [](auto p, auto i) { return p.first < i; });
+ if (found->first == index) {
+ return found->second;
} else {
- return std::string();
+ // Return empty string if no match is found
+ return "";
}
}
diff --git a/src/video_core/regs.h b/src/video_core/regs.h
index f25edde27..86826088b 100644
--- a/src/video_core/regs.h
+++ b/src/video_core/regs.h
@@ -45,46 +45,31 @@ namespace Pica {
#endif // _MSC_VER
struct Regs {
- INSERT_PADDING_WORDS(0x10);
- u32 trigger_irq;
- INSERT_PADDING_WORDS(0x2f);
- RasterizerRegs rasterizer;
- TexturingRegs texturing;
- FramebufferRegs framebuffer;
- LightingRegs lighting;
- PipelineRegs pipeline;
- ShaderRegs gs;
- ShaderRegs vs;
- INSERT_PADDING_WORDS(0x20);
-
- // Map register indices to names readable by humans
- // Used for debugging purposes, so performance is not an issue here
- static std::string GetCommandName(int index);
-
- static constexpr size_t NumIds() {
- return sizeof(Regs) / sizeof(u32);
- }
-
- const u32& operator[](int index) const {
- const u32* content = reinterpret_cast<const u32*>(this);
- return content[index];
- }
-
- u32& operator[](int index) {
- u32* content = reinterpret_cast<u32*>(this);
- return content[index];
- }
-
-private:
- /*
- * Most physical addresses which Pica registers refer to are 8-byte aligned.
- * This function should be used to get the address from a raw register value.
- */
- static inline u32 DecodeAddressRegister(u32 register_value) {
- return register_value * 8;
- }
+ static constexpr size_t NUM_REGS = 0x300;
+
+ union {
+ struct {
+ INSERT_PADDING_WORDS(0x10);
+ u32 trigger_irq;
+ INSERT_PADDING_WORDS(0x2f);
+ RasterizerRegs rasterizer;
+ TexturingRegs texturing;
+ FramebufferRegs framebuffer;
+ LightingRegs lighting;
+ PipelineRegs pipeline;
+ ShaderRegs gs;
+ ShaderRegs vs;
+ INSERT_PADDING_WORDS(0x20);
+ };
+ std::array<u32, NUM_REGS> reg_array;
+ };
+
+ /// Map register indices to names readable by humans
+ static const char* GetRegisterName(u16 index);
};
+static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Regs struct has wrong size");
+
// TODO: MSVC does not support using offsetof() on non-static data members even though this
// is technically allowed since C++11. This macro should be enabled once MSVC adds
// support for that.
@@ -154,11 +139,4 @@ ASSERT_REG_POSITION(vs, 0x2b0);
#undef ASSERT_REG_POSITION
#endif // !defined(_MSC_VER)
-// The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value
-// anyway.
-static_assert(sizeof(Regs) <= 0x300 * sizeof(u32),
- "Register set structure larger than it should be");
-static_assert(sizeof(Regs) >= 0x300 * sizeof(u32),
- "Register set structure smaller than it should be");
-
} // namespace Pica
diff --git a/src/video_core/regs_lighting.h b/src/video_core/regs_lighting.h
index 548a6c4d5..6793405d9 100644
--- a/src/video_core/regs_lighting.h
+++ b/src/video_core/regs_lighting.h
@@ -34,7 +34,7 @@ struct LightingRegs {
* configurations that require more LUTs, more cycles are required on HW to perform lighting
* computations.
*/
- enum class LightingConfig {
+ enum class LightingConfig : u32 {
Config0 = 0, ///< Reflect Red, Distribution 0, Spotlight
Config1 = 1, ///< Reflect Red, Fresnel, Spotlight
Config2 = 2, ///< Reflect Red, Distribution 0/1
@@ -48,7 +48,7 @@ struct LightingRegs {
};
/// Selects which lighting components are affected by fresnel
- enum class LightingFresnelSelector {
+ enum class LightingFresnelSelector : u32 {
None = 0, ///< Fresnel is disabled
PrimaryAlpha = 1, ///< Primary (diffuse) lighting alpha is affected by fresnel
SecondaryAlpha = 2, ///< Secondary (specular) lighting alpha is affected by fresnel
@@ -58,7 +58,7 @@ struct LightingRegs {
};
/// Factor used to scale the output of a lighting LUT
- enum class LightingScale {
+ enum class LightingScale : u32 {
Scale1 = 0, ///< Scale is 1x
Scale2 = 1, ///< Scale is 2x
Scale4 = 2, ///< Scale is 4x
@@ -68,7 +68,7 @@ struct LightingRegs {
Scale1_2 = 7, ///< Scale is 0.5x
};
- enum class LightingLutInput {
+ enum class LightingLutInput : u32 {
NH = 0, // Cosine of the angle between the normal and half-angle vectors
VH = 1, // Cosine of the angle between the view and half-angle vectors
NV = 2, // Cosine of the angle between the normal and the view vector
diff --git a/src/video_core/regs_pipeline.h b/src/video_core/regs_pipeline.h
index 5844a66ee..0a4ec6e1e 100644
--- a/src/video_core/regs_pipeline.h
+++ b/src/video_core/regs_pipeline.h
@@ -14,7 +14,7 @@
namespace Pica {
struct PipelineRegs {
- enum class VertexAttributeFormat : u64 {
+ enum class VertexAttributeFormat : u32 {
BYTE = 0,
UBYTE = 1,
SHORT = 2,
@@ -31,34 +31,37 @@ struct PipelineRegs {
// Descriptor for internal vertex attributes
union {
BitField<0, 2, VertexAttributeFormat> format0; // size of one element
- BitField<2, 2, u64> size0; // number of elements minus 1
+ BitField<2, 2, u32> size0; // number of elements minus 1
BitField<4, 2, VertexAttributeFormat> format1;
- BitField<6, 2, u64> size1;
+ BitField<6, 2, u32> size1;
BitField<8, 2, VertexAttributeFormat> format2;
- BitField<10, 2, u64> size2;
+ BitField<10, 2, u32> size2;
BitField<12, 2, VertexAttributeFormat> format3;
- BitField<14, 2, u64> size3;
+ BitField<14, 2, u32> size3;
BitField<16, 2, VertexAttributeFormat> format4;
- BitField<18, 2, u64> size4;
+ BitField<18, 2, u32> size4;
BitField<20, 2, VertexAttributeFormat> format5;
- BitField<22, 2, u64> size5;
+ BitField<22, 2, u32> size5;
BitField<24, 2, VertexAttributeFormat> format6;
- BitField<26, 2, u64> size6;
+ BitField<26, 2, u32> size6;
BitField<28, 2, VertexAttributeFormat> format7;
- BitField<30, 2, u64> size7;
- BitField<32, 2, VertexAttributeFormat> format8;
- BitField<34, 2, u64> size8;
- BitField<36, 2, VertexAttributeFormat> format9;
- BitField<38, 2, u64> size9;
- BitField<40, 2, VertexAttributeFormat> format10;
- BitField<42, 2, u64> size10;
- BitField<44, 2, VertexAttributeFormat> format11;
- BitField<46, 2, u64> size11;
-
- BitField<48, 12, u64> attribute_mask;
+ BitField<30, 2, u32> size7;
+ };
+
+ union {
+ BitField<0, 2, VertexAttributeFormat> format8;
+ BitField<2, 2, u32> size8;
+ BitField<4, 2, VertexAttributeFormat> format9;
+ BitField<6, 2, u32> size9;
+ BitField<8, 2, VertexAttributeFormat> format10;
+ BitField<10, 2, u32> size10;
+ BitField<12, 2, VertexAttributeFormat> format11;
+ BitField<14, 2, u32> size11;
+
+ BitField<16, 12, u32> attribute_mask;
// number of total attributes minus 1
- BitField<60, 4, u64> max_attribute_index;
+ BitField<28, 4, u32> max_attribute_index;
};
inline VertexAttributeFormat GetFormat(int n) const {
@@ -69,7 +72,7 @@ struct PipelineRegs {
}
inline int GetNumElements(int n) const {
- u64 sizes[] = {size0, size1, size2, size3, size4, size5,
+ u32 sizes[] = {size0, size1, size2, size3, size4, size5,
size6, size7, size8, size9, size10, size11};
return (int)sizes[n] + 1;
}
@@ -99,27 +102,30 @@ struct PipelineRegs {
u32 data_offset;
union {
- BitField<0, 4, u64> comp0;
- BitField<4, 4, u64> comp1;
- BitField<8, 4, u64> comp2;
- BitField<12, 4, u64> comp3;
- BitField<16, 4, u64> comp4;
- BitField<20, 4, u64> comp5;
- BitField<24, 4, u64> comp6;
- BitField<28, 4, u64> comp7;
- BitField<32, 4, u64> comp8;
- BitField<36, 4, u64> comp9;
- BitField<40, 4, u64> comp10;
- BitField<44, 4, u64> comp11;
+ BitField<0, 4, u32> comp0;
+ BitField<4, 4, u32> comp1;
+ BitField<8, 4, u32> comp2;
+ BitField<12, 4, u32> comp3;
+ BitField<16, 4, u32> comp4;
+ BitField<20, 4, u32> comp5;
+ BitField<24, 4, u32> comp6;
+ BitField<28, 4, u32> comp7;
+ };
+
+ union {
+ BitField<0, 4, u32> comp8;
+ BitField<4, 4, u32> comp9;
+ BitField<8, 4, u32> comp10;
+ BitField<12, 4, u32> comp11;
// bytes for a single vertex in this loader
- BitField<48, 8, u64> byte_count;
+ BitField<16, 8, u32> byte_count;
- BitField<60, 4, u64> component_count;
+ BitField<28, 4, u32> component_count;
};
inline int GetComponent(int n) const {
- u64 components[] = {comp0, comp1, comp2, comp3, comp4, comp5,
+ u32 components[] = {comp0, comp1, comp2, comp3, comp4, comp5,
comp6, comp7, comp8, comp9, comp10, comp11};
return (int)components[n];
}
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index fd38175b3..f6ece5c4b 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -6,7 +6,7 @@
#include <memory>
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
-#include "video_core/swrasterizer.h"
+#include "video_core/swrasterizer/swrasterizer.h"
#include "video_core/video_core.h"
void RendererBase::RefreshRasterizerSetting() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 75736c99f..de1d5eba7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -15,7 +15,9 @@
#include "common/vector_math.h"
#include "core/hw/gpu.h"
#include "video_core/pica_state.h"
-#include "video_core/regs.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -26,18 +28,6 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
-static bool IsPassThroughTevStage(const Pica::TexturingRegs::TevStageConfig& stage) {
- using TevStageConfig = Pica::TexturingRegs::TevStageConfig;
-
- 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);
-}
-
RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
// Create sampler objects
for (size_t i = 0; i < texture_samplers.size(); ++i) {
@@ -1081,37 +1071,38 @@ void RasterizerOpenGL::SetShader() {
current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
- unsigned int block_index =
- glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
- GLint block_size;
- glGetActiveUniformBlockiv(current_shader->shader.handle, block_index,
- GL_UNIFORM_BLOCK_DATA_SIZE, &block_size);
- ASSERT_MSG(block_size == sizeof(UniformData),
- "Uniform block size did not match! Got %d, expected %zu",
- static_cast<int>(block_size), sizeof(UniformData));
- glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
-
- // Update uniforms
- SyncDepthScale();
- SyncDepthOffset();
- SyncAlphaTest();
- SyncCombinerColor();
- auto& tev_stages = Pica::g_state.regs.texturing.GetTevStages();
- for (int index = 0; index < tev_stages.size(); ++index)
- SyncTevConstColor(index, tev_stages[index]);
+ GLuint block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
+ if (block_index != GL_INVALID_INDEX) {
+ GLint block_size;
+ glGetActiveUniformBlockiv(current_shader->shader.handle, block_index,
+ GL_UNIFORM_BLOCK_DATA_SIZE, &block_size);
+ ASSERT_MSG(block_size == sizeof(UniformData),
+ "Uniform block size did not match! Got %d, expected %zu",
+ static_cast<int>(block_size), sizeof(UniformData));
+ glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
+
+ // Update uniforms
+ SyncDepthScale();
+ SyncDepthOffset();
+ SyncAlphaTest();
+ SyncCombinerColor();
+ auto& tev_stages = Pica::g_state.regs.texturing.GetTevStages();
+ for (int index = 0; index < tev_stages.size(); ++index)
+ SyncTevConstColor(index, tev_stages[index]);
+
+ SyncGlobalAmbient();
+ for (int light_index = 0; light_index < 8; light_index++) {
+ SyncLightSpecular0(light_index);
+ SyncLightSpecular1(light_index);
+ SyncLightDiffuse(light_index);
+ SyncLightAmbient(light_index);
+ SyncLightPosition(light_index);
+ SyncLightDistanceAttenuationBias(light_index);
+ SyncLightDistanceAttenuationScale(light_index);
+ }
- SyncGlobalAmbient();
- for (int light_index = 0; light_index < 8; light_index++) {
- SyncLightSpecular0(light_index);
- SyncLightSpecular1(light_index);
- SyncLightDiffuse(light_index);
- SyncLightAmbient(light_index);
- SyncLightPosition(light_index);
- SyncLightDistanceAttenuationBias(light_index);
- SyncLightDistanceAttenuationScale(light_index);
+ SyncFogColor();
}
-
- SyncFogColor();
}
}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index bfee911b6..ecf737438 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -19,7 +19,10 @@
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
#include "video_core/rasterizer_interface.h"
-#include "video_core/regs.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_lighting.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_state.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 4072ed49e..aea20c693 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -21,7 +21,8 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hw/gpu.h"
-#include "video_core/regs.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
namespace MathUtil {
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 3ea25f302..7abdeba05 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -7,15 +7,19 @@
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/logging/log.h"
-#include "video_core/regs.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_lighting.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
-using Pica::Regs;
-using Pica::RasterizerRegs;
+using Pica::FramebufferRegs;
using Pica::LightingRegs;
-using TevStageConfig = Pica::TexturingRegs::TevStageConfig;
+using Pica::RasterizerRegs;
+using Pica::TexturingRegs;
+using TevStageConfig = TexturingRegs::TevStageConfig;
namespace GLShader {
@@ -48,10 +52,10 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config,
case Source::Texture0:
// Only unit 0 respects the texturing type (according to 3DBrew)
switch (state.texture0_type) {
- case Pica::TexturingRegs::TextureConfig::Texture2D:
+ case TexturingRegs::TextureConfig::Texture2D:
out += "texture(tex[0], texcoord[0])";
break;
- case Pica::TexturingRegs::TextureConfig::Projection2D:
+ case TexturingRegs::TextureConfig::Projection2D:
out += "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))";
break;
default:
@@ -278,8 +282,8 @@ static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation oper
}
/// Writes the if-statement condition used to evaluate alpha testing
-static void AppendAlphaTestCondition(std::string& out, Pica::FramebufferRegs::CompareFunc func) {
- using CompareFunc = Pica::FramebufferRegs::CompareFunc;
+static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareFunc func) {
+ using CompareFunc = FramebufferRegs::CompareFunc;
switch (func) {
case CompareFunc::Never:
out += "true";
@@ -309,7 +313,7 @@ static void AppendAlphaTestCondition(std::string& out, Pica::FramebufferRegs::Co
/// Writes the code to emulate the specified TEV stage
static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
const auto stage =
- static_cast<const Pica::TexturingRegs::TevStageConfig>(config.state.tev_stages[index]);
+ static_cast<const TexturingRegs::TevStageConfig>(config.state.tev_stages[index]);
if (!IsPassThroughTevStage(stage)) {
std::string index_name = std::to_string(index);
@@ -642,7 +646,7 @@ vec4 secondary_fragment_color = vec4(0.0);
)";
// Do not do any sort of processing if it's obvious we're not going to pass the alpha test
- if (state.alpha_test_func == Pica::FramebufferRegs::CompareFunc::Never) {
+ if (state.alpha_test_func == FramebufferRegs::CompareFunc::Never) {
out += "discard; }";
return out;
}
@@ -661,7 +665,7 @@ vec4 secondary_fragment_color = vec4(0.0);
out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n";
out += "float depth = z_over_w * depth_scale + depth_offset;\n";
- if (state.depthmap_enable == Pica::RasterizerRegs::DepthBuffering::WBuffering) {
+ if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) {
out += "depth /= gl_FragCoord.w;\n";
}
@@ -675,14 +679,14 @@ vec4 secondary_fragment_color = vec4(0.0);
for (size_t index = 0; index < state.tev_stages.size(); ++index)
WriteTevStage(out, config, (unsigned)index);
- if (state.alpha_test_func != Pica::FramebufferRegs::CompareFunc::Always) {
+ if (state.alpha_test_func != FramebufferRegs::CompareFunc::Always) {
out += "if (";
AppendAlphaTestCondition(out, state.alpha_test_func);
out += ") discard;\n";
}
// Append fog combiner
- if (state.fog_mode == Pica::TexturingRegs::FogMode::Fog) {
+ if (state.fog_mode == TexturingRegs::FogMode::Fog) {
// Get index into fog LUT
if (state.fog_flip) {
out += "float fog_index = (1.0 - depth) * 128.0;\n";
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index 4b98dafc4..93d7b0b71 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -12,7 +12,9 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
-#include "video_core/regs.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_lighting.h"
+#include "video_core/regs_texturing.h"
using GLvec2 = std::array<GLfloat, 2>;
using GLvec3 = std::array<GLfloat, 3>;
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index c860375a1..67ed19ba8 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -8,7 +8,8 @@
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "video_core/pica_state.h"
-#include "video_core/regs.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
#include "video_core/shader/shader.h"
#include "video_core/shader/shader_interpreter.h"
#ifdef ARCHITECTURE_x86_64
@@ -39,9 +40,8 @@ OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs, Attri
for (unsigned comp = 0; comp < 4; ++comp) {
RasterizerRegs::VSOutputAttributes::Semantic semantic = semantics[comp];
- float24* out = &vertex_slots[semantic];
if (semantic < vertex_slots.size()) {
- *out = input.attr[i][comp];
+ vertex_slots[semantic] = input.attr[i][comp];
} else if (semantic != RasterizerRegs::VSOutputAttributes::INVALID) {
LOG_ERROR(HW_GPU, "Invalid/unknown semantic id: %u", (unsigned int)semantic);
}
diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h
index d52682479..38ea717ab 100644
--- a/src/video_core/shader/shader.h
+++ b/src/video_core/shader/shader.h
@@ -13,7 +13,8 @@
#include "common/common_types.h"
#include "common/vector_math.h"
#include "video_core/pica_types.h"
-#include "video_core/regs.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
using nihstro::RegisterType;
using nihstro::SourceRegister;
diff --git a/src/video_core/shader/shader_jit_x64_compiler.cpp b/src/video_core/shader/shader_jit_x64_compiler.cpp
index 92b35dbc0..2dbc8b147 100644
--- a/src/video_core/shader/shader_jit_x64_compiler.cpp
+++ b/src/video_core/shader/shader_jit_x64_compiler.cpp
@@ -295,14 +295,22 @@ void JitShader::Compile_DestEnable(Instruction instr, Xmm src) {
}
void JitShader::Compile_SanitizedMul(Xmm src1, Xmm src2, Xmm scratch) {
+ // 0 * inf and inf * 0 in the PICA should return 0 instead of NaN. This can be implemented by
+ // checking for NaNs before and after the multiplication. If the multiplication result is NaN
+ // where neither source was, this NaN was generated by a 0 * inf multiplication, and so the
+ // result should be transformed to 0 to match PICA fp rules.
+
+ // Set scratch to mask of (src1 != NaN and src2 != NaN)
movaps(scratch, src1);
cmpordps(scratch, src2);
mulps(src1, src2);
+ // Set src2 to mask of (result == NaN)
movaps(src2, src1);
cmpunordps(src2, src2);
+ // Clear components where scratch != src2 (i.e. if result is NaN where neither source was NaN)
xorps(scratch, src2);
andps(src1, scratch);
}
diff --git a/src/video_core/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp
index 0f71bbd06..2d80822d9 100644
--- a/src/video_core/clipper.cpp
+++ b/src/video_core/swrasterizer/clipper.cpp
@@ -11,12 +11,11 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/vector_math.h"
-#include "video_core/clipper.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
-#include "video_core/rasterizer.h"
-#include "video_core/regs.h"
#include "video_core/shader/shader.h"
+#include "video_core/swrasterizer/clipper.h"
+#include "video_core/swrasterizer/rasterizer.h"
using Pica::Rasterizer::Vertex;
diff --git a/src/video_core/clipper.h b/src/video_core/swrasterizer/clipper.h
index b51af0af9..b51af0af9 100644
--- a/src/video_core/clipper.h
+++ b/src/video_core/swrasterizer/clipper.h
diff --git a/src/video_core/swrasterizer/framebuffer.cpp b/src/video_core/swrasterizer/framebuffer.cpp
new file mode 100644
index 000000000..7de3aac75
--- /dev/null
+++ b/src/video_core/swrasterizer/framebuffer.cpp
@@ -0,0 +1,358 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "common/assert.h"
+#include "common/color.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "common/math_util.h"
+#include "common/vector_math.h"
+#include "core/hw/gpu.h"
+#include "core/memory.h"
+#include "video_core/pica_state.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/swrasterizer/framebuffer.h"
+#include "video_core/utils.h"
+
+namespace Pica {
+namespace Rasterizer {
+
+void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
+
+ // Similarly to textures, the render framebuffer is laid out from bottom to top, too.
+ // NOTE: The framebuffer height register contains the actual FB height minus one.
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel =
+ GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) +
+ coarse_y * framebuffer.width * bytes_per_pixel;
+ u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset;
+
+ switch (framebuffer.color_format) {
+ case FramebufferRegs::ColorFormat::RGBA8:
+ Color::EncodeRGBA8(color, dst_pixel);
+ break;
+
+ case FramebufferRegs::ColorFormat::RGB8:
+ Color::EncodeRGB8(color, dst_pixel);
+ break;
+
+ case FramebufferRegs::ColorFormat::RGB5A1:
+ Color::EncodeRGB5A1(color, dst_pixel);
+ break;
+
+ case FramebufferRegs::ColorFormat::RGB565:
+ Color::EncodeRGB565(color, dst_pixel);
+ break;
+
+ case FramebufferRegs::ColorFormat::RGBA4:
+ Color::EncodeRGBA4(color, dst_pixel);
+ break;
+
+ default:
+ LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
+ framebuffer.color_format.Value());
+ UNIMPLEMENTED();
+ }
+}
+
+const Math::Vec4<u8> GetPixel(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel =
+ GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) +
+ coarse_y * framebuffer.width * bytes_per_pixel;
+ u8* src_pixel = Memory::GetPhysicalPointer(addr) + src_offset;
+
+ switch (framebuffer.color_format) {
+ case FramebufferRegs::ColorFormat::RGBA8:
+ return Color::DecodeRGBA8(src_pixel);
+
+ case FramebufferRegs::ColorFormat::RGB8:
+ return Color::DecodeRGB8(src_pixel);
+
+ case FramebufferRegs::ColorFormat::RGB5A1:
+ return Color::DecodeRGB5A1(src_pixel);
+
+ case FramebufferRegs::ColorFormat::RGB565:
+ return Color::DecodeRGB565(src_pixel);
+
+ case FramebufferRegs::ColorFormat::RGBA4:
+ return Color::DecodeRGBA4(src_pixel);
+
+ default:
+ LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
+ framebuffer.color_format.Value());
+ UNIMPLEMENTED();
+ }
+
+ return {0, 0, 0, 0};
+}
+
+u32 GetDepth(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Memory::GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* src_pixel = depth_buffer + src_offset;
+
+ switch (framebuffer.depth_format) {
+ case FramebufferRegs::DepthFormat::D16:
+ return Color::DecodeD16(src_pixel);
+ case FramebufferRegs::DepthFormat::D24:
+ return Color::DecodeD24(src_pixel);
+ case FramebufferRegs::DepthFormat::D24S8:
+ return Color::DecodeD24S8(src_pixel).x;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+u8 GetStencil(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Memory::GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = Pica::FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* src_pixel = depth_buffer + src_offset;
+
+ switch (framebuffer.depth_format) {
+ case FramebufferRegs::DepthFormat::D24S8:
+ return Color::DecodeD24S8(src_pixel).y;
+
+ default:
+ LOG_WARNING(
+ HW_GPU,
+ "GetStencil called for function which doesn't have a stencil component (format %u)",
+ framebuffer.depth_format);
+ return 0;
+ }
+}
+
+void SetDepth(int x, int y, u32 value) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Memory::GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* dst_pixel = depth_buffer + dst_offset;
+
+ switch (framebuffer.depth_format) {
+ case FramebufferRegs::DepthFormat::D16:
+ Color::EncodeD16(value, dst_pixel);
+ break;
+
+ case FramebufferRegs::DepthFormat::D24:
+ Color::EncodeD24(value, dst_pixel);
+ break;
+
+ case FramebufferRegs::DepthFormat::D24S8:
+ Color::EncodeD24X8(value, dst_pixel);
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ UNIMPLEMENTED();
+ break;
+ }
+}
+
+void SetStencil(int x, int y, u8 value) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Memory::GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = Pica::FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* dst_pixel = depth_buffer + dst_offset;
+
+ switch (framebuffer.depth_format) {
+ case Pica::FramebufferRegs::DepthFormat::D16:
+ case Pica::FramebufferRegs::DepthFormat::D24:
+ // Nothing to do
+ break;
+
+ case Pica::FramebufferRegs::DepthFormat::D24S8:
+ Color::EncodeX24S8(value, dst_pixel);
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ UNIMPLEMENTED();
+ break;
+ }
+}
+
+u8 PerformStencilAction(FramebufferRegs::StencilAction action, u8 old_stencil, u8 ref) {
+ switch (action) {
+ case FramebufferRegs::StencilAction::Keep:
+ return old_stencil;
+
+ case FramebufferRegs::StencilAction::Zero:
+ return 0;
+
+ case FramebufferRegs::StencilAction::Replace:
+ return ref;
+
+ case FramebufferRegs::StencilAction::Increment:
+ // Saturated increment
+ return std::min<u8>(old_stencil, 254) + 1;
+
+ case FramebufferRegs::StencilAction::Decrement:
+ // Saturated decrement
+ return std::max<u8>(old_stencil, 1) - 1;
+
+ case FramebufferRegs::StencilAction::Invert:
+ return ~old_stencil;
+
+ case FramebufferRegs::StencilAction::IncrementWrap:
+ return old_stencil + 1;
+
+ case FramebufferRegs::StencilAction::DecrementWrap:
+ return old_stencil - 1;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action);
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+Math::Vec4<u8> EvaluateBlendEquation(const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
+ const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor,
+ FramebufferRegs::BlendEquation equation) {
+ Math::Vec4<int> result;
+
+ auto src_result = (src * srcfactor).Cast<int>();
+ auto dst_result = (dest * destfactor).Cast<int>();
+
+ switch (equation) {
+ case FramebufferRegs::BlendEquation::Add:
+ result = (src_result + dst_result) / 255;
+ break;
+
+ case FramebufferRegs::BlendEquation::Subtract:
+ result = (src_result - dst_result) / 255;
+ break;
+
+ case FramebufferRegs::BlendEquation::ReverseSubtract:
+ result = (dst_result - src_result) / 255;
+ break;
+
+ // TODO: How do these two actually work? OpenGL doesn't include the blend factors in the
+ // min/max computations, but is this what the 3DS actually does?
+ case FramebufferRegs::BlendEquation::Min:
+ result.r() = std::min(src.r(), dest.r());
+ result.g() = std::min(src.g(), dest.g());
+ result.b() = std::min(src.b(), dest.b());
+ result.a() = std::min(src.a(), dest.a());
+ break;
+
+ case FramebufferRegs::BlendEquation::Max:
+ result.r() = std::max(src.r(), dest.r());
+ result.g() = std::max(src.g(), dest.g());
+ result.b() = std::max(src.b(), dest.b());
+ result.a() = std::max(src.a(), dest.a());
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation);
+ UNIMPLEMENTED();
+ }
+
+ return Math::Vec4<u8>(MathUtil::Clamp(result.r(), 0, 255), MathUtil::Clamp(result.g(), 0, 255),
+ MathUtil::Clamp(result.b(), 0, 255), MathUtil::Clamp(result.a(), 0, 255));
+};
+
+u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op) {
+ switch (op) {
+ case FramebufferRegs::LogicOp::Clear:
+ return 0;
+
+ case FramebufferRegs::LogicOp::And:
+ return src & dest;
+
+ case FramebufferRegs::LogicOp::AndReverse:
+ return src & ~dest;
+
+ case FramebufferRegs::LogicOp::Copy:
+ return src;
+
+ case FramebufferRegs::LogicOp::Set:
+ return 255;
+
+ case FramebufferRegs::LogicOp::CopyInverted:
+ return ~src;
+
+ case FramebufferRegs::LogicOp::NoOp:
+ return dest;
+
+ case FramebufferRegs::LogicOp::Invert:
+ return ~dest;
+
+ case FramebufferRegs::LogicOp::Nand:
+ return ~(src & dest);
+
+ case FramebufferRegs::LogicOp::Or:
+ return src | dest;
+
+ case FramebufferRegs::LogicOp::Nor:
+ return ~(src | dest);
+
+ case FramebufferRegs::LogicOp::Xor:
+ return src ^ dest;
+
+ case FramebufferRegs::LogicOp::Equiv:
+ return ~(src ^ dest);
+
+ case FramebufferRegs::LogicOp::AndInverted:
+ return ~src & dest;
+
+ case FramebufferRegs::LogicOp::OrReverse:
+ return src | ~dest;
+
+ case FramebufferRegs::LogicOp::OrInverted:
+ return ~src | dest;
+ }
+};
+
+} // namespace Rasterizer
+} // namespace Pica
diff --git a/src/video_core/swrasterizer/framebuffer.h b/src/video_core/swrasterizer/framebuffer.h
new file mode 100644
index 000000000..4a32a4979
--- /dev/null
+++ b/src/video_core/swrasterizer/framebuffer.h
@@ -0,0 +1,29 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/vector_math.h"
+#include "video_core/regs_framebuffer.h"
+
+namespace Pica {
+namespace Rasterizer {
+
+void DrawPixel(int x, int y, const Math::Vec4<u8>& color);
+const Math::Vec4<u8> GetPixel(int x, int y);
+u32 GetDepth(int x, int y);
+u8 GetStencil(int x, int y);
+void SetDepth(int x, int y, u32 value);
+void SetStencil(int x, int y, u8 value);
+u8 PerformStencilAction(FramebufferRegs::StencilAction action, u8 old_stencil, u8 ref);
+
+Math::Vec4<u8> EvaluateBlendEquation(const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
+ const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor,
+ FramebufferRegs::BlendEquation equation);
+
+u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op);
+
+} // namespace Rasterizer
+} // namespace Pica
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp
index ca09c9d0e..7557fcb89 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/swrasterizer/rasterizer.cpp
@@ -18,252 +18,19 @@
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
-#include "video_core/rasterizer.h"
-#include "video_core/regs.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/shader/shader.h"
+#include "video_core/swrasterizer/framebuffer.h"
+#include "video_core/swrasterizer/rasterizer.h"
+#include "video_core/swrasterizer/texturing.h"
#include "video_core/texture/texture_decode.h"
#include "video_core/utils.h"
namespace Pica {
-
namespace Rasterizer {
-static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
- const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
- const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
-
- // Similarly to textures, the render framebuffer is laid out from bottom to top, too.
- // NOTE: The framebuffer height register contains the actual FB height minus one.
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel =
- GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
- u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) +
- coarse_y * framebuffer.width * bytes_per_pixel;
- u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset;
-
- switch (framebuffer.color_format) {
- case FramebufferRegs::ColorFormat::RGBA8:
- Color::EncodeRGBA8(color, dst_pixel);
- break;
-
- case FramebufferRegs::ColorFormat::RGB8:
- Color::EncodeRGB8(color, dst_pixel);
- break;
-
- case FramebufferRegs::ColorFormat::RGB5A1:
- Color::EncodeRGB5A1(color, dst_pixel);
- break;
-
- case FramebufferRegs::ColorFormat::RGB565:
- Color::EncodeRGB565(color, dst_pixel);
- break;
-
- case FramebufferRegs::ColorFormat::RGBA4:
- Color::EncodeRGBA4(color, dst_pixel);
- break;
-
- default:
- LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
- framebuffer.color_format.Value());
- UNIMPLEMENTED();
- }
-}
-
-static const Math::Vec4<u8> GetPixel(int x, int y) {
- const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
- const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel =
- GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
- u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) +
- coarse_y * framebuffer.width * bytes_per_pixel;
- u8* src_pixel = Memory::GetPhysicalPointer(addr) + src_offset;
-
- switch (framebuffer.color_format) {
- case FramebufferRegs::ColorFormat::RGBA8:
- return Color::DecodeRGBA8(src_pixel);
-
- case FramebufferRegs::ColorFormat::RGB8:
- return Color::DecodeRGB8(src_pixel);
-
- case FramebufferRegs::ColorFormat::RGB5A1:
- return Color::DecodeRGB5A1(src_pixel);
-
- case FramebufferRegs::ColorFormat::RGB565:
- return Color::DecodeRGB565(src_pixel);
-
- case FramebufferRegs::ColorFormat::RGBA4:
- return Color::DecodeRGBA4(src_pixel);
-
- default:
- LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
- framebuffer.color_format.Value());
- UNIMPLEMENTED();
- }
-
- return {0, 0, 0, 0};
-}
-
-static u32 GetDepth(int x, int y) {
- const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
- const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
- u8* depth_buffer = Memory::GetPhysicalPointer(addr);
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel = FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
- u32 stride = framebuffer.width * bytes_per_pixel;
-
- u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
- u8* src_pixel = depth_buffer + src_offset;
-
- switch (framebuffer.depth_format) {
- case FramebufferRegs::DepthFormat::D16:
- return Color::DecodeD16(src_pixel);
- case FramebufferRegs::DepthFormat::D24:
- return Color::DecodeD24(src_pixel);
- case FramebufferRegs::DepthFormat::D24S8:
- return Color::DecodeD24S8(src_pixel).x;
- default:
- LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
- UNIMPLEMENTED();
- return 0;
- }
-}
-
-static u8 GetStencil(int x, int y) {
- const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
- const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
- u8* depth_buffer = Memory::GetPhysicalPointer(addr);
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel = Pica::FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
- u32 stride = framebuffer.width * bytes_per_pixel;
-
- u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
- u8* src_pixel = depth_buffer + src_offset;
-
- switch (framebuffer.depth_format) {
- case FramebufferRegs::DepthFormat::D24S8:
- return Color::DecodeD24S8(src_pixel).y;
-
- default:
- LOG_WARNING(
- HW_GPU,
- "GetStencil called for function which doesn't have a stencil component (format %u)",
- framebuffer.depth_format);
- return 0;
- }
-}
-
-static void SetDepth(int x, int y, u32 value) {
- const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
- const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
- u8* depth_buffer = Memory::GetPhysicalPointer(addr);
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel = FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
- u32 stride = framebuffer.width * bytes_per_pixel;
-
- u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
- u8* dst_pixel = depth_buffer + dst_offset;
-
- switch (framebuffer.depth_format) {
- case FramebufferRegs::DepthFormat::D16:
- Color::EncodeD16(value, dst_pixel);
- break;
-
- case FramebufferRegs::DepthFormat::D24:
- Color::EncodeD24(value, dst_pixel);
- break;
-
- case FramebufferRegs::DepthFormat::D24S8:
- Color::EncodeD24X8(value, dst_pixel);
- break;
-
- default:
- LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
- UNIMPLEMENTED();
- break;
- }
-}
-
-static void SetStencil(int x, int y, u8 value) {
- const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
- const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
- u8* depth_buffer = Memory::GetPhysicalPointer(addr);
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel = Pica::FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
- u32 stride = framebuffer.width * bytes_per_pixel;
-
- u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
- u8* dst_pixel = depth_buffer + dst_offset;
-
- switch (framebuffer.depth_format) {
- case Pica::FramebufferRegs::DepthFormat::D16:
- case Pica::FramebufferRegs::DepthFormat::D24:
- // Nothing to do
- break;
-
- case Pica::FramebufferRegs::DepthFormat::D24S8:
- Color::EncodeX24S8(value, dst_pixel);
- break;
-
- default:
- LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
- UNIMPLEMENTED();
- break;
- }
-}
-
-static u8 PerformStencilAction(FramebufferRegs::StencilAction action, u8 old_stencil, u8 ref) {
- switch (action) {
- case FramebufferRegs::StencilAction::Keep:
- return old_stencil;
-
- case FramebufferRegs::StencilAction::Zero:
- return 0;
-
- case FramebufferRegs::StencilAction::Replace:
- return ref;
-
- case FramebufferRegs::StencilAction::Increment:
- // Saturated increment
- return std::min<u8>(old_stencil, 254) + 1;
-
- case FramebufferRegs::StencilAction::Decrement:
- // Saturated decrement
- return std::max<u8>(old_stencil, 1) - 1;
-
- case FramebufferRegs::StencilAction::Invert:
- return ~old_stencil;
-
- case FramebufferRegs::StencilAction::IncrementWrap:
- return old_stencil + 1;
-
- case FramebufferRegs::StencilAction::DecrementWrap:
- return old_stencil - 1;
-
- default:
- LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action);
- UNIMPLEMENTED();
- return 0;
- }
-}
-
// NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values
struct Fix12P4 {
Fix12P4() {}
@@ -537,34 +304,6 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height)))
.ToFloat32();
- static auto GetWrappedTexCoord = [](TexturingRegs::TextureConfig::WrapMode mode,
- int val, unsigned size) {
- switch (mode) {
- case TexturingRegs::TextureConfig::ClampToEdge:
- val = std::max(val, 0);
- val = std::min(val, (int)size - 1);
- return val;
-
- case TexturingRegs::TextureConfig::ClampToBorder:
- return val;
-
- case TexturingRegs::TextureConfig::Repeat:
- return (int)((unsigned)val % size);
-
- case TexturingRegs::TextureConfig::MirroredRepeat: {
- unsigned int coord = ((unsigned)val % (2 * size));
- if (coord >= size)
- coord = 2 * size - 1 - coord;
- return (int)coord;
- }
-
- default:
- LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x", (int)mode);
- UNIMPLEMENTED();
- return 0;
- }
- };
-
if ((texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder &&
(s < 0 || static_cast<u32>(s) >= texture.config.width)) ||
(texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder &&
@@ -613,9 +352,6 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
++tev_stage_index) {
const auto& tev_stage = tev_stages[tev_stage_index];
using Source = TexturingRegs::TevStageConfig::Source;
- using ColorModifier = TexturingRegs::TevStageConfig::ColorModifier;
- using AlphaModifier = TexturingRegs::TevStageConfig::AlphaModifier;
- using Operation = TexturingRegs::TevStageConfig::Operation;
auto GetSource = [&](Source source) -> Math::Vec4<u8> {
switch (source) {
@@ -655,187 +391,6 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
}
};
- static auto GetColorModifier = [](ColorModifier factor,
- const Math::Vec4<u8>& values) -> Math::Vec3<u8> {
- switch (factor) {
- case ColorModifier::SourceColor:
- return values.rgb();
-
- case ColorModifier::OneMinusSourceColor:
- return (Math::Vec3<u8>(255, 255, 255) - values.rgb()).Cast<u8>();
-
- case ColorModifier::SourceAlpha:
- return values.aaa();
-
- case ColorModifier::OneMinusSourceAlpha:
- return (Math::Vec3<u8>(255, 255, 255) - values.aaa()).Cast<u8>();
-
- case ColorModifier::SourceRed:
- return values.rrr();
-
- case ColorModifier::OneMinusSourceRed:
- return (Math::Vec3<u8>(255, 255, 255) - values.rrr()).Cast<u8>();
-
- case ColorModifier::SourceGreen:
- return values.ggg();
-
- case ColorModifier::OneMinusSourceGreen:
- return (Math::Vec3<u8>(255, 255, 255) - values.ggg()).Cast<u8>();
-
- case ColorModifier::SourceBlue:
- return values.bbb();
-
- case ColorModifier::OneMinusSourceBlue:
- return (Math::Vec3<u8>(255, 255, 255) - values.bbb()).Cast<u8>();
- }
- };
-
- static auto GetAlphaModifier = [](AlphaModifier factor,
- const Math::Vec4<u8>& values) -> u8 {
- switch (factor) {
- case AlphaModifier::SourceAlpha:
- return values.a();
-
- case AlphaModifier::OneMinusSourceAlpha:
- return 255 - values.a();
-
- case AlphaModifier::SourceRed:
- return values.r();
-
- case AlphaModifier::OneMinusSourceRed:
- return 255 - values.r();
-
- case AlphaModifier::SourceGreen:
- return values.g();
-
- case AlphaModifier::OneMinusSourceGreen:
- return 255 - values.g();
-
- case AlphaModifier::SourceBlue:
- return values.b();
-
- case AlphaModifier::OneMinusSourceBlue:
- return 255 - values.b();
- }
- };
-
- static auto ColorCombine = [](Operation op,
- const Math::Vec3<u8> input[3]) -> Math::Vec3<u8> {
- switch (op) {
- case Operation::Replace:
- return input[0];
-
- case Operation::Modulate:
- return ((input[0] * input[1]) / 255).Cast<u8>();
-
- case Operation::Add: {
- auto result = input[0] + input[1];
- result.r() = std::min(255, result.r());
- result.g() = std::min(255, result.g());
- result.b() = std::min(255, result.b());
- return result.Cast<u8>();
- }
-
- case Operation::AddSigned: {
- // TODO(bunnei): Verify that the color conversion from (float) 0.5f to
- // (byte) 128 is correct
- auto result = input[0].Cast<int>() + input[1].Cast<int>() -
- Math::MakeVec<int>(128, 128, 128);
- result.r() = MathUtil::Clamp<int>(result.r(), 0, 255);
- result.g() = MathUtil::Clamp<int>(result.g(), 0, 255);
- result.b() = MathUtil::Clamp<int>(result.b(), 0, 255);
- return result.Cast<u8>();
- }
-
- case Operation::Lerp:
- return ((input[0] * input[2] +
- input[1] *
- (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) /
- 255)
- .Cast<u8>();
-
- case Operation::Subtract: {
- auto result = input[0].Cast<int>() - input[1].Cast<int>();
- result.r() = std::max(0, result.r());
- result.g() = std::max(0, result.g());
- result.b() = std::max(0, result.b());
- return result.Cast<u8>();
- }
-
- case Operation::MultiplyThenAdd: {
- auto result = (input[0] * input[1] + 255 * input[2].Cast<int>()) / 255;
- result.r() = std::min(255, result.r());
- result.g() = std::min(255, result.g());
- result.b() = std::min(255, result.b());
- return result.Cast<u8>();
- }
-
- case Operation::AddThenMultiply: {
- auto result = input[0] + input[1];
- result.r() = std::min(255, result.r());
- result.g() = std::min(255, result.g());
- result.b() = std::min(255, result.b());
- result = (result * input[2].Cast<int>()) / 255;
- return result.Cast<u8>();
- }
- case Operation::Dot3_RGB: {
- // Not fully accurate.
- // Worst case scenario seems to yield a +/-3 error
- // Some HW results indicate that the per-component computation can't have a
- // higher precision than 1/256,
- // while dot3_rgb( (0x80,g0,b0),(0x7F,g1,b1) ) and dot3_rgb(
- // (0x80,g0,b0),(0x80,g1,b1) ) give different results
- int result =
- ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 +
- ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 +
- ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256;
- result = std::max(0, std::min(255, result));
- return {(u8)result, (u8)result, (u8)result};
- }
- default:
- LOG_ERROR(HW_GPU, "Unknown color combiner operation %d", (int)op);
- UNIMPLEMENTED();
- return {0, 0, 0};
- }
- };
-
- static auto AlphaCombine = [](Operation op, const std::array<u8, 3>& input) -> u8 {
- switch (op) {
- case Operation::Replace:
- return input[0];
-
- case Operation::Modulate:
- return input[0] * input[1] / 255;
-
- case Operation::Add:
- return std::min(255, input[0] + input[1]);
-
- case Operation::AddSigned: {
- // TODO(bunnei): Verify that the color conversion from (float) 0.5f to
- // (byte) 128 is correct
- auto result = static_cast<int>(input[0]) + static_cast<int>(input[1]) - 128;
- return static_cast<u8>(MathUtil::Clamp<int>(result, 0, 255));
- }
-
- case Operation::Lerp:
- return (input[0] * input[2] + input[1] * (255 - input[2])) / 255;
-
- case Operation::Subtract:
- return std::max(0, (int)input[0] - (int)input[1]);
-
- case Operation::MultiplyThenAdd:
- return std::min(255, (input[0] * input[1] + 255 * input[2]) / 255);
-
- case Operation::AddThenMultiply:
- return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
-
- default:
- LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d", (int)op);
- UNIMPLEMENTED();
- return 0;
- }
- };
-
// color combiner
// NOTE: Not sure if the alpha combiner might use the color output of the previous
// stage as input. Hence, we currently don't directly write the result to
@@ -1150,56 +705,6 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
return combiner_output[channel];
};
- static auto EvaluateBlendEquation = [](
- const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
- const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor,
- FramebufferRegs::BlendEquation equation) {
-
- Math::Vec4<int> result;
-
- auto src_result = (src * srcfactor).Cast<int>();
- auto dst_result = (dest * destfactor).Cast<int>();
-
- switch (equation) {
- case FramebufferRegs::BlendEquation::Add:
- result = (src_result + dst_result) / 255;
- break;
-
- case FramebufferRegs::BlendEquation::Subtract:
- result = (src_result - dst_result) / 255;
- break;
-
- case FramebufferRegs::BlendEquation::ReverseSubtract:
- result = (dst_result - src_result) / 255;
- break;
-
- // TODO: How do these two actually work?
- // OpenGL doesn't include the blend factors in the min/max computations,
- // but is this what the 3DS actually does?
- case FramebufferRegs::BlendEquation::Min:
- result.r() = std::min(src.r(), dest.r());
- result.g() = std::min(src.g(), dest.g());
- result.b() = std::min(src.b(), dest.b());
- result.a() = std::min(src.a(), dest.a());
- break;
-
- case FramebufferRegs::BlendEquation::Max:
- result.r() = std::max(src.r(), dest.r());
- result.g() = std::max(src.g(), dest.g());
- result.b() = std::max(src.b(), dest.b());
- result.a() = std::max(src.a(), dest.a());
- break;
-
- default:
- LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation);
- UNIMPLEMENTED();
- }
-
- return Math::Vec4<u8>(
- MathUtil::Clamp(result.r(), 0, 255), MathUtil::Clamp(result.g(), 0, 255),
- MathUtil::Clamp(result.b(), 0, 255), MathUtil::Clamp(result.a(), 0, 255));
- };
-
auto srcfactor = Math::MakeVec(LookupFactor(0, params.factor_source_rgb),
LookupFactor(1, params.factor_source_rgb),
LookupFactor(2, params.factor_source_rgb),
@@ -1216,58 +721,6 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
dstfactor, params.blend_equation_a)
.a();
} else {
- static auto LogicOp = [](u8 src, u8 dest, FramebufferRegs::LogicOp op) -> u8 {
- switch (op) {
- case FramebufferRegs::LogicOp::Clear:
- return 0;
-
- case FramebufferRegs::LogicOp::And:
- return src & dest;
-
- case FramebufferRegs::LogicOp::AndReverse:
- return src & ~dest;
-
- case FramebufferRegs::LogicOp::Copy:
- return src;
-
- case FramebufferRegs::LogicOp::Set:
- return 255;
-
- case FramebufferRegs::LogicOp::CopyInverted:
- return ~src;
-
- case FramebufferRegs::LogicOp::NoOp:
- return dest;
-
- case FramebufferRegs::LogicOp::Invert:
- return ~dest;
-
- case FramebufferRegs::LogicOp::Nand:
- return ~(src & dest);
-
- case FramebufferRegs::LogicOp::Or:
- return src | dest;
-
- case FramebufferRegs::LogicOp::Nor:
- return ~(src | dest);
-
- case FramebufferRegs::LogicOp::Xor:
- return src ^ dest;
-
- case FramebufferRegs::LogicOp::Equiv:
- return ~(src ^ dest);
-
- case FramebufferRegs::LogicOp::AndInverted:
- return ~src & dest;
-
- case FramebufferRegs::LogicOp::OrReverse:
- return src | ~dest;
-
- case FramebufferRegs::LogicOp::OrInverted:
- return ~src | dest;
- }
- };
-
blend_output =
Math::MakeVec(LogicOp(combiner_output.r(), dest.r(), output_merger.logic_op),
LogicOp(combiner_output.g(), dest.g(), output_merger.logic_op),
diff --git a/src/video_core/rasterizer.h b/src/video_core/swrasterizer/rasterizer.h
index 3a72ac343..3a72ac343 100644
--- a/src/video_core/rasterizer.h
+++ b/src/video_core/swrasterizer/rasterizer.h
diff --git a/src/video_core/swrasterizer.cpp b/src/video_core/swrasterizer/swrasterizer.cpp
index 9cd21f72b..402b705dd 100644
--- a/src/video_core/swrasterizer.cpp
+++ b/src/video_core/swrasterizer/swrasterizer.cpp
@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "video_core/clipper.h"
-#include "video_core/swrasterizer.h"
+#include "video_core/swrasterizer/clipper.h"
+#include "video_core/swrasterizer/swrasterizer.h"
namespace VideoCore {
diff --git a/src/video_core/swrasterizer.h b/src/video_core/swrasterizer/swrasterizer.h
index 6d42d7409..6d42d7409 100644
--- a/src/video_core/swrasterizer.h
+++ b/src/video_core/swrasterizer/swrasterizer.h
diff --git a/src/video_core/swrasterizer/texturing.cpp b/src/video_core/swrasterizer/texturing.cpp
new file mode 100644
index 000000000..eb18e4ba4
--- /dev/null
+++ b/src/video_core/swrasterizer/texturing.cpp
@@ -0,0 +1,228 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/math_util.h"
+#include "common/vector_math.h"
+#include "video_core/regs_texturing.h"
+#include "video_core/swrasterizer/texturing.h"
+
+namespace Pica {
+namespace Rasterizer {
+
+using TevStageConfig = TexturingRegs::TevStageConfig;
+
+int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) {
+ switch (mode) {
+ case TexturingRegs::TextureConfig::ClampToEdge:
+ val = std::max(val, 0);
+ val = std::min(val, (int)size - 1);
+ return val;
+
+ case TexturingRegs::TextureConfig::ClampToBorder:
+ return val;
+
+ case TexturingRegs::TextureConfig::Repeat:
+ return (int)((unsigned)val % size);
+
+ case TexturingRegs::TextureConfig::MirroredRepeat: {
+ unsigned int coord = ((unsigned)val % (2 * size));
+ if (coord >= size)
+ coord = 2 * size - 1 - coord;
+ return (int)coord;
+ }
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x", (int)mode);
+ UNIMPLEMENTED();
+ return 0;
+ }
+};
+
+Math::Vec3<u8> GetColorModifier(TevStageConfig::ColorModifier factor,
+ const Math::Vec4<u8>& values) {
+ using ColorModifier = TevStageConfig::ColorModifier;
+
+ switch (factor) {
+ case ColorModifier::SourceColor:
+ return values.rgb();
+
+ case ColorModifier::OneMinusSourceColor:
+ return (Math::Vec3<u8>(255, 255, 255) - values.rgb()).Cast<u8>();
+
+ case ColorModifier::SourceAlpha:
+ return values.aaa();
+
+ case ColorModifier::OneMinusSourceAlpha:
+ return (Math::Vec3<u8>(255, 255, 255) - values.aaa()).Cast<u8>();
+
+ case ColorModifier::SourceRed:
+ return values.rrr();
+
+ case ColorModifier::OneMinusSourceRed:
+ return (Math::Vec3<u8>(255, 255, 255) - values.rrr()).Cast<u8>();
+
+ case ColorModifier::SourceGreen:
+ return values.ggg();
+
+ case ColorModifier::OneMinusSourceGreen:
+ return (Math::Vec3<u8>(255, 255, 255) - values.ggg()).Cast<u8>();
+
+ case ColorModifier::SourceBlue:
+ return values.bbb();
+
+ case ColorModifier::OneMinusSourceBlue:
+ return (Math::Vec3<u8>(255, 255, 255) - values.bbb()).Cast<u8>();
+ }
+};
+
+u8 GetAlphaModifier(TevStageConfig::AlphaModifier factor, const Math::Vec4<u8>& values) {
+ using AlphaModifier = TevStageConfig::AlphaModifier;
+
+ switch (factor) {
+ case AlphaModifier::SourceAlpha:
+ return values.a();
+
+ case AlphaModifier::OneMinusSourceAlpha:
+ return 255 - values.a();
+
+ case AlphaModifier::SourceRed:
+ return values.r();
+
+ case AlphaModifier::OneMinusSourceRed:
+ return 255 - values.r();
+
+ case AlphaModifier::SourceGreen:
+ return values.g();
+
+ case AlphaModifier::OneMinusSourceGreen:
+ return 255 - values.g();
+
+ case AlphaModifier::SourceBlue:
+ return values.b();
+
+ case AlphaModifier::OneMinusSourceBlue:
+ return 255 - values.b();
+ }
+};
+
+Math::Vec3<u8> ColorCombine(TevStageConfig::Operation op, const Math::Vec3<u8> input[3]) {
+ using Operation = TevStageConfig::Operation;
+
+ switch (op) {
+ case Operation::Replace:
+ return input[0];
+
+ case Operation::Modulate:
+ return ((input[0] * input[1]) / 255).Cast<u8>();
+
+ case Operation::Add: {
+ auto result = input[0] + input[1];
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::AddSigned: {
+ // TODO(bunnei): Verify that the color conversion from (float) 0.5f to
+ // (byte) 128 is correct
+ auto result =
+ input[0].Cast<int>() + input[1].Cast<int>() - Math::MakeVec<int>(128, 128, 128);
+ result.r() = MathUtil::Clamp<int>(result.r(), 0, 255);
+ result.g() = MathUtil::Clamp<int>(result.g(), 0, 255);
+ result.b() = MathUtil::Clamp<int>(result.b(), 0, 255);
+ return result.Cast<u8>();
+ }
+
+ case Operation::Lerp:
+ return ((input[0] * input[2] +
+ input[1] * (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) /
+ 255)
+ .Cast<u8>();
+
+ case Operation::Subtract: {
+ auto result = input[0].Cast<int>() - input[1].Cast<int>();
+ result.r() = std::max(0, result.r());
+ result.g() = std::max(0, result.g());
+ result.b() = std::max(0, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::MultiplyThenAdd: {
+ auto result = (input[0] * input[1] + 255 * input[2].Cast<int>()) / 255;
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::AddThenMultiply: {
+ auto result = input[0] + input[1];
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ result = (result * input[2].Cast<int>()) / 255;
+ return result.Cast<u8>();
+ }
+ case Operation::Dot3_RGB: {
+ // Not fully accurate. Worst case scenario seems to yield a +/-3 error. Some HW results
+ // indicate that the per-component computation can't have a higher precision than 1/256,
+ // while dot3_rgb((0x80,g0,b0), (0x7F,g1,b1)) and dot3_rgb((0x80,g0,b0), (0x80,g1,b1)) give
+ // different results.
+ int result = ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 +
+ ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 +
+ ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256;
+ result = std::max(0, std::min(255, result));
+ return {(u8)result, (u8)result, (u8)result};
+ }
+ default:
+ LOG_ERROR(HW_GPU, "Unknown color combiner operation %d", (int)op);
+ UNIMPLEMENTED();
+ return {0, 0, 0};
+ }
+};
+
+u8 AlphaCombine(TevStageConfig::Operation op, const std::array<u8, 3>& input) {
+ switch (op) {
+ using Operation = TevStageConfig::Operation;
+ case Operation::Replace:
+ return input[0];
+
+ case Operation::Modulate:
+ return input[0] * input[1] / 255;
+
+ case Operation::Add:
+ return std::min(255, input[0] + input[1]);
+
+ case Operation::AddSigned: {
+ // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct
+ auto result = static_cast<int>(input[0]) + static_cast<int>(input[1]) - 128;
+ return static_cast<u8>(MathUtil::Clamp<int>(result, 0, 255));
+ }
+
+ case Operation::Lerp:
+ return (input[0] * input[2] + input[1] * (255 - input[2])) / 255;
+
+ case Operation::Subtract:
+ return std::max(0, (int)input[0] - (int)input[1]);
+
+ case Operation::MultiplyThenAdd:
+ return std::min(255, (input[0] * input[1] + 255 * input[2]) / 255);
+
+ case Operation::AddThenMultiply:
+ return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d", (int)op);
+ UNIMPLEMENTED();
+ return 0;
+ }
+};
+
+} // namespace Rasterizer
+} // namespace Pica
diff --git a/src/video_core/swrasterizer/texturing.h b/src/video_core/swrasterizer/texturing.h
new file mode 100644
index 000000000..24f74a5a3
--- /dev/null
+++ b/src/video_core/swrasterizer/texturing.h
@@ -0,0 +1,28 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/vector_math.h"
+#include "video_core/regs_texturing.h"
+
+namespace Pica {
+namespace Rasterizer {
+
+int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size);
+
+Math::Vec3<u8> GetColorModifier(TexturingRegs::TevStageConfig::ColorModifier factor,
+ const Math::Vec4<u8>& values);
+
+u8 GetAlphaModifier(TexturingRegs::TevStageConfig::AlphaModifier factor,
+ const Math::Vec4<u8>& values);
+
+Math::Vec3<u8> ColorCombine(TexturingRegs::TevStageConfig::Operation op,
+ const Math::Vec3<u8> input[3]);
+
+u8 AlphaCombine(TexturingRegs::TevStageConfig::Operation op, const std::array<u8, 3>& input);
+
+} // namespace Rasterizer
+} // namespace Pica
diff --git a/src/video_core/texture/etc1.cpp b/src/video_core/texture/etc1.cpp
index 2b7d26f91..43f7f56db 100644
--- a/src/video_core/texture/etc1.cpp
+++ b/src/video_core/texture/etc1.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#pragma once
-
#include <array>
#include "common/bit_field.h"
#include "common/color.h"
diff --git a/src/video_core/texture/texture_decode.cpp b/src/video_core/texture/texture_decode.cpp
index 40d363184..0818d652c 100644
--- a/src/video_core/texture/texture_decode.cpp
+++ b/src/video_core/texture/texture_decode.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#pragma once
-
#include "common/assert.h"
#include "common/color.h"
#include "common/logging/log.h"