diff options
63 files changed, 1235 insertions, 331 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f0af2d41..4668d4bea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -79,6 +79,8 @@ else() | |||
| 79 | add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS) | 79 | add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS) |
| 80 | # Avoid windows.h junk | 80 | # Avoid windows.h junk |
| 81 | add_definitions(/DNOMINMAX) | 81 | add_definitions(/DNOMINMAX) |
| 82 | # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. | ||
| 83 | add_definitions(/DWIN32_LEAN_AND_MEAN) | ||
| 82 | 84 | ||
| 83 | # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) | 85 | # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) |
| 84 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) | 86 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) |
| @@ -92,10 +94,13 @@ else() | |||
| 92 | # /W3 - Level 3 warnings | 94 | # /W3 - Level 3 warnings |
| 93 | # /MP - Multi-threaded compilation | 95 | # /MP - Multi-threaded compilation |
| 94 | # /Zi - Output debugging information | 96 | # /Zi - Output debugging information |
| 95 | # /Zo - enahnced debug info for optimized builds | 97 | # /Zo - enhanced debug info for optimized builds |
| 96 | set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo" CACHE STRING "" FORCE) | 98 | # /permissive- - enables stricter C++ standards conformance checks |
| 99 | set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo /permissive-" CACHE STRING "" FORCE) | ||
| 97 | # /EHsc - C++-only exception handling semantics | 100 | # /EHsc - C++-only exception handling semantics |
| 98 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc" CACHE STRING "" FORCE) | 101 | # /Zc:throwingNew - let codegen assume `operator new` will never return null |
| 102 | # /Zc:inline - let codegen omit inline functions in object files | ||
| 103 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc /Zc:throwingNew,inline" CACHE STRING "" FORCE) | ||
| 99 | 104 | ||
| 100 | # /MDd - Multi-threaded Debug Runtime DLL | 105 | # /MDd - Multi-threaded Debug Runtime DLL |
| 101 | set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE) | 106 | set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE) |
| @@ -5,7 +5,7 @@ Citra Emulator | |||
| 5 | [](https://travis-ci.org/citra-emu/citra) | 5 | [](https://travis-ci.org/citra-emu/citra) |
| 6 | [](https://ci.appveyor.com/project/bunnei/citra) | 6 | [](https://ci.appveyor.com/project/bunnei/citra) |
| 7 | 7 | ||
| 8 | Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward. | 8 | Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS. Citra only emulates a subset of 3DS hardware and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward. |
| 9 | 9 | ||
| 10 | Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project. | 10 | Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project. |
| 11 | 11 | ||
| @@ -27,7 +27,7 @@ If you want to contribute please take a look at the [Contributor's Guide](CONTRI | |||
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | ### Support | 29 | ### Support |
| 30 | We happily accept monetary donations, or donated games and hardware. Please see our [donations page](https://citra-emu.org/donate/) for more information on how you can contribute to Citra. Any donations received will go towards things like: | 30 | We happily accept monetary donations or donated games and hardware. Please see our [donations page](https://citra-emu.org/donate/) for more information on how you can contribute to Citra. Any donations received will go towards things like: |
| 31 | * 3DS consoles for developers to explore the hardware | 31 | * 3DS consoles for developers to explore the hardware |
| 32 | * 3DS games for testing | 32 | * 3DS games for testing |
| 33 | * Any equipment required for homebrew | 33 | * Any equipment required for homebrew |
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 1e04931ee..02e02350c 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -46,7 +46,5 @@ if (ARCHITECTURE_x86_64) | |||
| 46 | # Defined before "dynarmic" above | 46 | # Defined before "dynarmic" above |
| 47 | # add_library(xbyak INTERFACE) | 47 | # add_library(xbyak INTERFACE) |
| 48 | target_include_directories(xbyak INTERFACE ./xbyak/xbyak) | 48 | target_include_directories(xbyak INTERFACE ./xbyak/xbyak) |
| 49 | if (NOT MSVC) | 49 | target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) |
| 50 | target_compile_options(xbyak INTERFACE -fno-operator-names) | ||
| 51 | endif() | ||
| 52 | endif() | 50 | endif() |
diff --git a/externals/cryptopp/CMakeLists.txt b/externals/cryptopp/CMakeLists.txt index 864de18bb..8a626e44a 100644 --- a/externals/cryptopp/CMakeLists.txt +++ b/externals/cryptopp/CMakeLists.txt | |||
| @@ -44,6 +44,11 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Intel") | |||
| 44 | add_definitions(-wd68 -wd186 -wd279 -wd327 -wd161 -wd3180) | 44 | add_definitions(-wd68 -wd186 -wd279 -wd327 -wd161 -wd3180) |
| 45 | endif() | 45 | endif() |
| 46 | 46 | ||
| 47 | if(MSVC) | ||
| 48 | # Disable C4390: empty controlled statement found: is this the intent? | ||
| 49 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4390") | ||
| 50 | endif() | ||
| 51 | |||
| 47 | # Endianness | 52 | # Endianness |
| 48 | TEST_BIG_ENDIAN(IS_BIG_ENDIAN) | 53 | TEST_BIG_ENDIAN(IS_BIG_ENDIAN) |
| 49 | if(IS_BIG_ENDIAN) | 54 | if(IS_BIG_ENDIAN) |
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject 7707ff13e981b0aecf87f3156ee0b641469f7bb | Subproject 8f15e3f70cb96e56705e5de6ba97b5d09423a56 | ||
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index dd357ff72..14574e56c 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp | |||
| @@ -18,7 +18,10 @@ | |||
| 18 | #endif | 18 | #endif |
| 19 | 19 | ||
| 20 | #ifdef _WIN32 | 20 | #ifdef _WIN32 |
| 21 | // windows.h needs to be included before shellapi.h | ||
| 21 | #include <windows.h> | 22 | #include <windows.h> |
| 23 | |||
| 24 | #include <shellapi.h> | ||
| 22 | #endif | 25 | #endif |
| 23 | 26 | ||
| 24 | #include "citra/config.h" | 27 | #include "citra/config.h" |
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index f08b4069c..957d8dc86 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -88,9 +88,9 @@ void Config::ReadValues() { | |||
| 88 | Settings::values.toggle_framelimit = | 88 | Settings::values.toggle_framelimit = |
| 89 | sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); | 89 | sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); |
| 90 | 90 | ||
| 91 | Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); | 91 | Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); |
| 92 | Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); | 92 | Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); |
| 93 | Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); | 93 | Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0); |
| 94 | 94 | ||
| 95 | // Layout | 95 | // Layout |
| 96 | Settings::values.layout_option = | 96 | Settings::values.layout_option = |
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 2b99447ec..64ffc9152 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp | |||
| @@ -70,9 +70,9 @@ void Config::ReadValues() { | |||
| 70 | Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); | 70 | Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); |
| 71 | Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); | 71 | Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); |
| 72 | 72 | ||
| 73 | Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); | 73 | Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); |
| 74 | Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); | 74 | Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); |
| 75 | Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); | 75 | Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat(); |
| 76 | qt_config->endGroup(); | 76 | qt_config->endGroup(); |
| 77 | 77 | ||
| 78 | qt_config->beginGroup("Layout"); | 78 | qt_config->beginGroup("Layout"); |
diff --git a/src/citra_qt/configuration/configure_debug.ui b/src/citra_qt/configuration/configure_debug.ui index bbbb0e3f4..96638ebdb 100644 --- a/src/citra_qt/configuration/configure_debug.ui +++ b/src/citra_qt/configuration/configure_debug.ui | |||
| @@ -23,6 +23,13 @@ | |||
| 23 | </property> | 23 | </property> |
| 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> | 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 25 | <item> | 25 | <item> |
| 26 | <widget class="QLabel"> | ||
| 27 | <property name="text"> | ||
| 28 | <string>The GDB Stub only works correctly when the CPU JIT is off.</string> | ||
| 29 | </property> | ||
| 30 | </widget> | ||
| 31 | </item> | ||
| 32 | <item> | ||
| 26 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | 33 | <layout class="QHBoxLayout" name="horizontalLayout_3"> |
| 27 | <item> | 34 | <item> |
| 28 | <widget class="QCheckBox" name="toggle_gdbstub"> | 35 | <widget class="QCheckBox" name="toggle_gdbstub"> |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b16a89990..ea09819e5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -144,6 +144,7 @@ set(SRCS | |||
| 144 | hle/service/nwm/nwm_tst.cpp | 144 | hle/service/nwm/nwm_tst.cpp |
| 145 | hle/service/nwm/nwm_uds.cpp | 145 | hle/service/nwm/nwm_uds.cpp |
| 146 | hle/service/nwm/uds_beacon.cpp | 146 | hle/service/nwm/uds_beacon.cpp |
| 147 | hle/service/nwm/uds_data.cpp | ||
| 147 | hle/service/pm_app.cpp | 148 | hle/service/pm_app.cpp |
| 148 | hle/service/ptm/ptm.cpp | 149 | hle/service/ptm/ptm.cpp |
| 149 | hle/service/ptm/ptm_gets.cpp | 150 | hle/service/ptm/ptm_gets.cpp |
| @@ -341,6 +342,7 @@ set(HEADERS | |||
| 341 | hle/service/nwm/nwm_tst.h | 342 | hle/service/nwm/nwm_tst.h |
| 342 | hle/service/nwm/nwm_uds.h | 343 | hle/service/nwm/nwm_uds.h |
| 343 | hle/service/nwm/uds_beacon.h | 344 | hle/service/nwm/uds_beacon.h |
| 345 | hle/service/nwm/uds_data.h | ||
| 344 | hle/service/pm_app.h | 346 | hle/service/pm_app.h |
| 345 | hle/service/ptm/ptm.h | 347 | hle/service/ptm/ptm.h |
| 346 | hle/service/ptm/ptm_gets.h | 348 | hle/service/ptm/ptm_gets.h |
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 2abdfe1b3..5e6002f4e 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -229,9 +229,18 @@ void Wrap() { | |||
| 229 | u32 retval = func(¶m_1, ¶m_2, | 229 | u32 retval = func(¶m_1, ¶m_2, |
| 230 | reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3)) | 230 | reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3)) |
| 231 | .raw; | 231 | .raw; |
| 232 | // The first out parameter is moved into R2 and the second is moved into R1. | 232 | Core::CPU().SetReg(1, param_1); |
| 233 | Core::CPU().SetReg(1, param_2); | 233 | Core::CPU().SetReg(2, param_2); |
| 234 | Core::CPU().SetReg(2, param_1); | 234 | FuncReturn(retval); |
| 235 | } | ||
| 236 | |||
| 237 | template <ResultCode func(Kernel::Handle*, Kernel::Handle*)> | ||
| 238 | void Wrap() { | ||
| 239 | Kernel::Handle param_1 = 0; | ||
| 240 | Kernel::Handle param_2 = 0; | ||
| 241 | u32 retval = func(¶m_1, ¶m_2).raw; | ||
| 242 | Core::CPU().SetReg(1, param_1); | ||
| 243 | Core::CPU().SetReg(2, param_2); | ||
| 235 | FuncReturn(retval); | 244 | FuncReturn(retval); |
| 236 | } | 245 | } |
| 237 | 246 | ||
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index b3b60e7df..64aa61460 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -13,6 +13,7 @@ enum { | |||
| 13 | OutOfHandles = 19, | 13 | OutOfHandles = 19, |
| 14 | SessionClosedByRemote = 26, | 14 | SessionClosedByRemote = 26, |
| 15 | PortNameTooLong = 30, | 15 | PortNameTooLong = 30, |
| 16 | NoPendingSessions = 35, | ||
| 16 | WrongPermission = 46, | 17 | WrongPermission = 46, |
| 17 | InvalidBufferDescriptor = 48, | 18 | InvalidBufferDescriptor = 48, |
| 18 | MaxConnectionsReached = 52, | 19 | MaxConnectionsReached = 52, |
| @@ -94,5 +95,9 @@ constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(ErrorDescription::OutOfRange, Error | |||
| 94 | ErrorLevel::Permanent); // 0xD8E007FD | 95 | ErrorLevel::Permanent); // 0xD8E007FD |
| 95 | constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, | 96 | constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, |
| 96 | ErrorSummary::StatusChanged, ErrorLevel::Info); | 97 | ErrorSummary::StatusChanged, ErrorLevel::Info); |
| 98 | /// Returned when Accept() is called on a port with no sessions to be accepted. | ||
| 99 | constexpr ResultCode ERR_NO_PENDING_SESSIONS(ErrCodes::NoPendingSessions, ErrorModule::OS, | ||
| 100 | ErrorSummary::WouldBlock, | ||
| 101 | ErrorLevel::Permanent); // 0xD8401823 | ||
| 97 | 102 | ||
| 98 | } // namespace Kernel | 103 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 6cf1886cf..5ebe2eca4 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -23,6 +23,11 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s | |||
| 23 | boost::range::remove_erase(connected_sessions, server_session); | 23 | boost::range::remove_erase(connected_sessions, server_session); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | HLERequestContext::HLERequestContext(SharedPtr<ServerSession> session) | ||
| 27 | : session(std::move(session)) { | ||
| 28 | cmd_buf[0] = 0; | ||
| 29 | } | ||
| 30 | |||
| 26 | HLERequestContext::~HLERequestContext() = default; | 31 | HLERequestContext::~HLERequestContext() = default; |
| 27 | 32 | ||
| 28 | SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { | 33 | SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { |
| @@ -62,10 +67,13 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr | |||
| 62 | ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error | 67 | ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error |
| 63 | for (u32 j = 0; j < num_handles; ++j) { | 68 | for (u32 j = 0; j < num_handles; ++j) { |
| 64 | Handle handle = src_cmdbuf[i]; | 69 | Handle handle = src_cmdbuf[i]; |
| 65 | SharedPtr<Object> object = src_table.GetGeneric(handle); | 70 | SharedPtr<Object> object = nullptr; |
| 66 | ASSERT(object != nullptr); // TODO(yuriks): Return error | 71 | if (handle != 0) { |
| 67 | if (descriptor == IPC::DescriptorType::MoveHandle) { | 72 | object = src_table.GetGeneric(handle); |
| 68 | src_table.Close(handle); | 73 | ASSERT(object != nullptr); // TODO(yuriks): Return error |
| 74 | if (descriptor == IPC::DescriptorType::MoveHandle) { | ||
| 75 | src_table.Close(handle); | ||
| 76 | } | ||
| 69 | } | 77 | } |
| 70 | 78 | ||
| 71 | cmd_buf[i++] = AddOutgoingHandle(std::move(object)); | 79 | cmd_buf[i++] = AddOutgoingHandle(std::move(object)); |
| @@ -107,9 +115,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P | |||
| 107 | ASSERT(i + num_handles <= command_size); | 115 | ASSERT(i + num_handles <= command_size); |
| 108 | for (u32 j = 0; j < num_handles; ++j) { | 116 | for (u32 j = 0; j < num_handles; ++j) { |
| 109 | SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); | 117 | SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); |
| 110 | 118 | Handle handle = 0; | |
| 111 | // TODO(yuriks): Figure out the proper error handling for if this fails | 119 | if (object != nullptr) { |
| 112 | Handle handle = dst_table.Create(object).Unwrap(); | 120 | // TODO(yuriks): Figure out the proper error handling for if this fails |
| 121 | handle = dst_table.Create(object).Unwrap(); | ||
| 122 | } | ||
| 113 | dst_cmdbuf[i++] = handle; | 123 | dst_cmdbuf[i++] = handle; |
| 114 | } | 124 | } |
| 115 | break; | 125 | break; |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index cbb109d8f..35795fc1d 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -84,6 +84,7 @@ protected: | |||
| 84 | */ | 84 | */ |
| 85 | class HLERequestContext { | 85 | class HLERequestContext { |
| 86 | public: | 86 | public: |
| 87 | HLERequestContext(SharedPtr<ServerSession> session); | ||
| 87 | ~HLERequestContext(); | 88 | ~HLERequestContext(); |
| 88 | 89 | ||
| 89 | /// Returns a pointer to the IPC command buffer for this request. | 90 | /// Returns a pointer to the IPC command buffer for this request. |
| @@ -118,14 +119,14 @@ public: | |||
| 118 | */ | 119 | */ |
| 119 | void ClearIncomingObjects(); | 120 | void ClearIncomingObjects(); |
| 120 | 121 | ||
| 121 | private: | 122 | /// Populates this context with data from the requesting process/thread. |
| 122 | friend class Service::ServiceFrameworkBase; | ||
| 123 | |||
| 124 | ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, | 123 | ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, |
| 125 | HandleTable& src_table); | 124 | HandleTable& src_table); |
| 125 | /// Writes data from this context back to the requesting process/thread. | ||
| 126 | ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, | 126 | ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, |
| 127 | HandleTable& dst_table) const; | 127 | HandleTable& dst_table) const; |
| 128 | 128 | ||
| 129 | private: | ||
| 129 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | 130 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; |
| 130 | SharedPtr<ServerSession> session; | 131 | SharedPtr<ServerSession> session; |
| 131 | // TODO(yuriks): Check common usage of this and optimize size accordingly | 132 | // TODO(yuriks): Check common usage of this and optimize size accordingly |
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 804f23b1c..496d07cb5 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp | |||
| @@ -166,7 +166,7 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin | |||
| 166 | auto vma = address_space | 166 | auto vma = address_space |
| 167 | .MapBackingMemory(mapping.address, target_pointer + offset_into_region, | 167 | .MapBackingMemory(mapping.address, target_pointer + offset_into_region, |
| 168 | mapping.size, memory_state) | 168 | mapping.size, memory_state) |
| 169 | .MoveFrom(); | 169 | .Unwrap(); |
| 170 | address_space.Reprotect(vma, | 170 | address_space.Reprotect(vma, |
| 171 | mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); | 171 | mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); |
| 172 | } | 172 | } |
| @@ -176,14 +176,14 @@ void MapSharedPages(VMManager& address_space) { | |||
| 176 | .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, | 176 | .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, |
| 177 | reinterpret_cast<u8*>(&ConfigMem::config_mem), | 177 | reinterpret_cast<u8*>(&ConfigMem::config_mem), |
| 178 | Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) | 178 | Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) |
| 179 | .MoveFrom(); | 179 | .Unwrap(); |
| 180 | address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); | 180 | address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); |
| 181 | 181 | ||
| 182 | auto shared_page_vma = address_space | 182 | auto shared_page_vma = address_space |
| 183 | .MapBackingMemory(Memory::SHARED_PAGE_VADDR, | 183 | .MapBackingMemory(Memory::SHARED_PAGE_VADDR, |
| 184 | reinterpret_cast<u8*>(&SharedPage::shared_page), | 184 | reinterpret_cast<u8*>(&SharedPage::shared_page), |
| 185 | Memory::SHARED_PAGE_SIZE, MemoryState::Shared) | 185 | Memory::SHARED_PAGE_SIZE, MemoryState::Shared) |
| 186 | .MoveFrom(); | 186 | .Unwrap(); |
| 187 | address_space.Reprotect(shared_page_vma, VMAPermission::Read); | 187 | address_space.Reprotect(shared_page_vma, VMAPermission::Read); |
| 188 | } | 188 | } |
| 189 | 189 | ||
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 1c31ec950..522ad2333 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -151,6 +151,8 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { | |||
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | VAddr Process::GetLinearHeapAreaAddress() const { | 153 | VAddr Process::GetLinearHeapAreaAddress() const { |
| 154 | // Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of | ||
| 155 | // the extra RAM in the n3DS. | ||
| 154 | return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; | 156 | return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; |
| 155 | } | 157 | } |
| 156 | 158 | ||
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 4d20c39a1..49a9cdfa3 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -5,8 +5,10 @@ | |||
| 5 | #include <tuple> | 5 | #include <tuple> |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "core/hle/kernel/client_port.h" | 7 | #include "core/hle/kernel/client_port.h" |
| 8 | #include "core/hle/kernel/errors.h" | ||
| 8 | #include "core/hle/kernel/kernel.h" | 9 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/kernel/server_port.h" | 10 | #include "core/hle/kernel/server_port.h" |
| 11 | #include "core/hle/kernel/server_session.h" | ||
| 10 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 11 | 13 | ||
| 12 | namespace Kernel { | 14 | namespace Kernel { |
| @@ -14,6 +16,16 @@ namespace Kernel { | |||
| 14 | ServerPort::ServerPort() {} | 16 | ServerPort::ServerPort() {} |
| 15 | ServerPort::~ServerPort() {} | 17 | ServerPort::~ServerPort() {} |
| 16 | 18 | ||
| 19 | ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { | ||
| 20 | if (pending_sessions.empty()) { | ||
| 21 | return ERR_NO_PENDING_SESSIONS; | ||
| 22 | } | ||
| 23 | |||
| 24 | auto session = std::move(pending_sessions.back()); | ||
| 25 | pending_sessions.pop_back(); | ||
| 26 | return MakeResult(std::move(session)); | ||
| 27 | } | ||
| 28 | |||
| 17 | bool ServerPort::ShouldWait(Thread* thread) const { | 29 | bool ServerPort::ShouldWait(Thread* thread) const { |
| 18 | // If there are no pending sessions, we wait until a new one is added. | 30 | // If there are no pending sessions, we wait until a new one is added. |
| 19 | return pending_sessions.size() == 0; | 31 | return pending_sessions.size() == 0; |
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index f1419cd46..6fe7c7f2f 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | 15 | ||
| 16 | class ClientPort; | 16 | class ClientPort; |
| 17 | class ServerSession; | ||
| 17 | class SessionRequestHandler; | 18 | class SessionRequestHandler; |
| 18 | 19 | ||
| 19 | class ServerPort final : public WaitObject { | 20 | class ServerPort final : public WaitObject { |
| @@ -41,6 +42,12 @@ public: | |||
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | /** | 44 | /** |
| 45 | * Accepts a pending incoming connection on this port. If there are no pending sessions, will | ||
| 46 | * return ERR_NO_PENDING_SESSIONS. | ||
| 47 | */ | ||
| 48 | ResultVal<SharedPtr<ServerSession>> Accept(); | ||
| 49 | |||
| 50 | /** | ||
| 44 | * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port | 51 | * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port |
| 45 | * will inherit a reference to this handler. | 52 | * will inherit a reference to this handler. |
| 46 | */ | 53 | */ |
| @@ -50,8 +57,8 @@ public: | |||
| 50 | 57 | ||
| 51 | std::string name; ///< Name of port (optional) | 58 | std::string name; ///< Name of port (optional) |
| 52 | 59 | ||
| 53 | std::vector<SharedPtr<WaitObject>> | 60 | /// ServerSessions waiting to be accepted by the port |
| 54 | pending_sessions; ///< ServerSessions waiting to be accepted by the port | 61 | std::vector<SharedPtr<ServerSession>> pending_sessions; |
| 55 | 62 | ||
| 56 | /// This session's HLE request handler template (optional) | 63 | /// This session's HLE request handler template (optional) |
| 57 | /// ServerSessions created from this port inherit a reference to this handler. | 64 | /// ServerSessions created from this port inherit a reference to this handler. |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 970eac5fe..337896abf 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -81,7 +81,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { | |||
| 81 | 81 | ||
| 82 | ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name, | 82 | ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name, |
| 83 | SharedPtr<ClientPort> port) { | 83 | SharedPtr<ClientPort> port) { |
| 84 | auto server_session = ServerSession::Create(name + "_Server").MoveFrom(); | 84 | auto server_session = ServerSession::Create(name + "_Server").Unwrap(); |
| 85 | SharedPtr<ClientSession> client_session(new ClientSession); | 85 | SharedPtr<ClientSession> client_session(new ClientSession); |
| 86 | client_session->name = name + "_Client"; | 86 | client_session->name = name + "_Client"; |
| 87 | 87 | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 75ce626f8..f5f2eb2f7 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -389,7 +389,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 389 | thread->wait_objects.clear(); | 389 | thread->wait_objects.clear(); |
| 390 | thread->wait_address = 0; | 390 | thread->wait_address = 0; |
| 391 | thread->name = std::move(name); | 391 | thread->name = std::move(name); |
| 392 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); | 392 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); |
| 393 | thread->owner_process = g_current_process; | 393 | thread->owner_process = g_current_process; |
| 394 | 394 | ||
| 395 | // Find the next available TLS index, and mark it as used | 395 | // Find the next available TLS index, and mark it as used |
| @@ -484,7 +484,7 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { | |||
| 484 | auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, | 484 | auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, |
| 485 | Memory::HEAP_VADDR_END); | 485 | Memory::HEAP_VADDR_END); |
| 486 | 486 | ||
| 487 | SharedPtr<Thread> thread = thread_res.MoveFrom(); | 487 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); |
| 488 | 488 | ||
| 489 | thread->context.fpscr = | 489 | thread->context.fpscr = |
| 490 | FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 | 490 | FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 |
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 6f2cf3b02..d7ec93672 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp | |||
| @@ -30,7 +30,7 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { | |||
| 30 | timer->name = std::move(name); | 30 | timer->name = std::move(name); |
| 31 | timer->initial_delay = 0; | 31 | timer->initial_delay = 0; |
| 32 | timer->interval_delay = 0; | 32 | timer->interval_delay = 0; |
| 33 | timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); | 33 | timer->callback_handle = timer_callback_handle_table.Create(timer).Unwrap(); |
| 34 | 34 | ||
| 35 | return timer; | 35 | return timer; |
| 36 | } | 36 | } |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 5f2cdbb96..47b6e2b23 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -388,13 +388,14 @@ public: | |||
| 388 | } | 388 | } |
| 389 | 389 | ||
| 390 | /// Asserts that the result succeeded and returns a reference to it. | 390 | /// Asserts that the result succeeded and returns a reference to it. |
| 391 | T& Unwrap() { | 391 | T& Unwrap() & { |
| 392 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | 392 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); |
| 393 | return **this; | 393 | return **this; |
| 394 | } | 394 | } |
| 395 | 395 | ||
| 396 | T&& MoveFrom() { | 396 | T&& Unwrap() && { |
| 397 | return std::move(Unwrap()); | 397 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); |
| 398 | return std::move(**this); | ||
| 398 | } | 399 | } |
| 399 | 400 | ||
| 400 | private: | 401 | private: |
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 4c587e3c8..25e7b777d 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp | |||
| @@ -55,8 +55,8 @@ void Initialize(Service::Interface* self) { | |||
| 55 | u32 flags = rp.Pop<u32>(); | 55 | u32 flags = rp.Pop<u32>(); |
| 56 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); | 56 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); |
| 57 | rb.Push(RESULT_SUCCESS); | 57 | rb.Push(RESULT_SUCCESS); |
| 58 | rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).MoveFrom(), | 58 | rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), |
| 59 | Kernel::g_handle_table.Create(parameter_event).MoveFrom()); | 59 | Kernel::g_handle_table.Create(parameter_event).Unwrap()); |
| 60 | 60 | ||
| 61 | // TODO(bunnei): Check if these events are cleared every time Initialize is called. | 61 | // TODO(bunnei): Check if these events are cleared every time Initialize is called. |
| 62 | notification_event->Clear(); | 62 | notification_event->Clear(); |
| @@ -93,7 +93,7 @@ void GetSharedFont(Service::Interface* self) { | |||
| 93 | // allocated, the real APT service calculates this address by scanning the entire address space | 93 | // allocated, the real APT service calculates this address by scanning the entire address space |
| 94 | // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font. | 94 | // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font. |
| 95 | rb.Push(target_address); | 95 | rb.Push(target_address); |
| 96 | rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).MoveFrom()); | 96 | rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).Unwrap()); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | void NotifyToWait(Service::Interface* self) { | 99 | void NotifyToWait(Service::Interface* self) { |
| @@ -115,7 +115,7 @@ void GetLockHandle(Service::Interface* self) { | |||
| 115 | rb.Push(RESULT_SUCCESS); // No error | 115 | rb.Push(RESULT_SUCCESS); // No error |
| 116 | rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. | 116 | rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. |
| 117 | rb.Push<u32>(0); // Least significant bit = power button state | 117 | rb.Push<u32>(0); // Least significant bit = power button state |
| 118 | Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).MoveFrom(); | 118 | Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); |
| 119 | rb.PushCopyHandles(handle_copy); | 119 | rb.PushCopyHandles(handle_copy); |
| 120 | 120 | ||
| 121 | LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy, | 121 | LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy, |
| @@ -231,7 +231,7 @@ void ReceiveParameter(Service::Interface* self) { | |||
| 231 | rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size | 231 | rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size |
| 232 | 232 | ||
| 233 | rb.PushMoveHandles((next_parameter.object != nullptr) | 233 | rb.PushMoveHandles((next_parameter.object != nullptr) |
| 234 | ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() | 234 | ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() |
| 235 | : 0); | 235 | : 0); |
| 236 | rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); | 236 | rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); |
| 237 | 237 | ||
| @@ -261,7 +261,7 @@ void GlanceParameter(Service::Interface* self) { | |||
| 261 | rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size | 261 | rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size |
| 262 | 262 | ||
| 263 | rb.PushCopyHandles((next_parameter.object != nullptr) | 263 | rb.PushCopyHandles((next_parameter.object != nullptr) |
| 264 | ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() | 264 | ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() |
| 265 | : 0); | 265 | : 0); |
| 266 | rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); | 266 | rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); |
| 267 | 267 | ||
diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 7394c844f..c9f9e9d95 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp | |||
| @@ -347,7 +347,7 @@ void GetVsyncInterruptEvent(Service::Interface* self) { | |||
| 347 | int port = *port_select.begin(); | 347 | int port = *port_select.begin(); |
| 348 | rb.Push(RESULT_SUCCESS); | 348 | rb.Push(RESULT_SUCCESS); |
| 349 | rb.PushCopyHandles( | 349 | rb.PushCopyHandles( |
| 350 | Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom()); | 350 | Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).Unwrap()); |
| 351 | } else { | 351 | } else { |
| 352 | LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); | 352 | LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); |
| 353 | rb.Push(ERROR_INVALID_ENUM_VALUE); | 353 | rb.Push(ERROR_INVALID_ENUM_VALUE); |
| @@ -366,7 +366,7 @@ void GetBufferErrorInterruptEvent(Service::Interface* self) { | |||
| 366 | int port = *port_select.begin(); | 366 | int port = *port_select.begin(); |
| 367 | rb.Push(RESULT_SUCCESS); | 367 | rb.Push(RESULT_SUCCESS); |
| 368 | rb.PushCopyHandles( | 368 | rb.PushCopyHandles( |
| 369 | Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom()); | 369 | Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).Unwrap()); |
| 370 | } else { | 370 | } else { |
| 371 | LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); | 371 | LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); |
| 372 | rb.Push(ERROR_INVALID_ENUM_VALUE); | 372 | rb.Push(ERROR_INVALID_ENUM_VALUE); |
| @@ -400,7 +400,7 @@ void SetReceiving(Service::Interface* self) { | |||
| 400 | } | 400 | } |
| 401 | 401 | ||
| 402 | rb.Push(RESULT_SUCCESS); | 402 | rb.Push(RESULT_SUCCESS); |
| 403 | rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).MoveFrom()); | 403 | rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).Unwrap()); |
| 404 | } else { | 404 | } else { |
| 405 | LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); | 405 | LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); |
| 406 | rb.Push(ERROR_INVALID_ENUM_VALUE); | 406 | rb.Push(ERROR_INVALID_ENUM_VALUE); |
diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index bd9814244..421006a9e 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp | |||
| @@ -31,8 +31,8 @@ void GetCecStateAbbreviated(Service::Interface* self) { | |||
| 31 | void GetCecInfoEventHandle(Service::Interface* self) { | 31 | void GetCecInfoEventHandle(Service::Interface* self) { |
| 32 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 32 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 33 | 33 | ||
| 34 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 34 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 35 | cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).MoveFrom(); // Event handle | 35 | cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).Unwrap(); // Event handle |
| 36 | 36 | ||
| 37 | LOG_WARNING(Service_CECD, "(STUBBED) called"); | 37 | LOG_WARNING(Service_CECD, "(STUBBED) called"); |
| 38 | } | 38 | } |
| @@ -40,8 +40,8 @@ void GetCecInfoEventHandle(Service::Interface* self) { | |||
| 40 | void GetChangeStateEventHandle(Service::Interface* self) { | 40 | void GetChangeStateEventHandle(Service::Interface* self) { |
| 41 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 41 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 42 | 42 | ||
| 43 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 43 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 44 | cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).MoveFrom(); // Event handle | 44 | cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).Unwrap(); // Event handle |
| 45 | 45 | ||
| 46 | LOG_WARNING(Service_CECD, "(STUBBED) called"); | 46 | LOG_WARNING(Service_CECD, "(STUBBED) called"); |
| 47 | } | 47 | } |
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 5a7878b31..6624f1711 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -406,7 +406,7 @@ ResultCode UpdateConfigNANDSavegame() { | |||
| 406 | auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode); | 406 | auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode); |
| 407 | ASSERT_MSG(config_result.Succeeded(), "could not open file"); | 407 | ASSERT_MSG(config_result.Succeeded(), "could not open file"); |
| 408 | 408 | ||
| 409 | auto config = config_result.MoveFrom(); | 409 | auto config = std::move(config_result).Unwrap(); |
| 410 | config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); | 410 | config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); |
| 411 | 411 | ||
| 412 | return RESULT_SUCCESS; | 412 | return RESULT_SUCCESS; |
| @@ -560,7 +560,7 @@ ResultCode LoadConfigNANDSaveFile() { | |||
| 560 | 560 | ||
| 561 | // Read the file if it already exists | 561 | // Read the file if it already exists |
| 562 | if (config_result.Succeeded()) { | 562 | if (config_result.Succeeded()) { |
| 563 | auto config = config_result.MoveFrom(); | 563 | auto config = std::move(config_result).Unwrap(); |
| 564 | config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); | 564 | config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); |
| 565 | return RESULT_SUCCESS; | 565 | return RESULT_SUCCESS; |
| 566 | } | 566 | } |
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 1455f20ca..9471ec1ef 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp | |||
| @@ -51,8 +51,8 @@ static void Initialize(Interface* self) { | |||
| 51 | 51 | ||
| 52 | cmd_buff[1] = RESULT_SUCCESS.raw; | 52 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 53 | cmd_buff[2] = IPC::CopyHandleDesc(2); | 53 | cmd_buff[2] = IPC::CopyHandleDesc(2); |
| 54 | cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom(); | 54 | cmd_buff[3] = Kernel::g_handle_table.Create(mutex).Unwrap(); |
| 55 | cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom(); | 55 | cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).Unwrap(); |
| 56 | 56 | ||
| 57 | LOG_WARNING(Service_CSND, "(STUBBED) called"); | 57 | LOG_WARNING(Service_CSND, "(STUBBED) called"); |
| 58 | } | 58 | } |
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 363066d14..7d746054f 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -168,7 +168,7 @@ static void GetSemaphoreEventHandle(Service::Interface* self) { | |||
| 168 | cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); | 168 | cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); |
| 169 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 169 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 170 | // cmd_buff[2] not set | 170 | // cmd_buff[2] not set |
| 171 | cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle | 171 | cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).Unwrap(); // Event handle |
| 172 | 172 | ||
| 173 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | 173 | LOG_WARNING(Service_DSP, "(STUBBED) called"); |
| 174 | } | 174 | } |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 3605ef175..033fbc9aa 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -311,7 +311,7 @@ ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handl | |||
| 311 | if (backend.Failed()) | 311 | if (backend.Failed()) |
| 312 | return backend.Code(); | 312 | return backend.Code(); |
| 313 | 313 | ||
| 314 | auto file = std::shared_ptr<File>(new File(backend.MoveFrom(), path)); | 314 | auto file = std::shared_ptr<File>(new File(std::move(backend).Unwrap(), path)); |
| 315 | return MakeResult<std::shared_ptr<File>>(std::move(file)); | 315 | return MakeResult<std::shared_ptr<File>>(std::move(file)); |
| 316 | } | 316 | } |
| 317 | 317 | ||
| @@ -401,7 +401,7 @@ ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle arc | |||
| 401 | if (backend.Failed()) | 401 | if (backend.Failed()) |
| 402 | return backend.Code(); | 402 | return backend.Code(); |
| 403 | 403 | ||
| 404 | auto directory = std::shared_ptr<Directory>(new Directory(backend.MoveFrom(), path)); | 404 | auto directory = std::shared_ptr<Directory>(new Directory(std::move(backend).Unwrap(), path)); |
| 405 | return MakeResult<std::shared_ptr<Directory>>(std::move(directory)); | 405 | return MakeResult<std::shared_ptr<Directory>>(std::move(directory)); |
| 406 | } | 406 | } |
| 407 | 407 | ||
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 34e1783ec..b9eab7838 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp | |||
| @@ -87,7 +87,7 @@ static void OpenFile(Service::Interface* self) { | |||
| 87 | file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); | 87 | file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); |
| 88 | 88 | ||
| 89 | rb.PushMoveHandles( | 89 | rb.PushMoveHandles( |
| 90 | Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom()); | 90 | Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap()); |
| 91 | } else { | 91 | } else { |
| 92 | rb.PushMoveHandles(0); | 92 | rb.PushMoveHandles(0); |
| 93 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | 93 | LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); |
| @@ -153,7 +153,7 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
| 153 | file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); | 153 | file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); |
| 154 | 154 | ||
| 155 | cmd_buff[3] = | 155 | cmd_buff[3] = |
| 156 | Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); | 156 | Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap(); |
| 157 | } else { | 157 | } else { |
| 158 | cmd_buff[3] = 0; | 158 | cmd_buff[3] = 0; |
| 159 | LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", | 159 | LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", |
| @@ -420,7 +420,7 @@ static void OpenDirectory(Service::Interface* self) { | |||
| 420 | directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); | 420 | directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); |
| 421 | 421 | ||
| 422 | cmd_buff[3] = | 422 | cmd_buff[3] = |
| 423 | Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); | 423 | Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap(); |
| 424 | } else { | 424 | } else { |
| 425 | LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", | 425 | LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", |
| 426 | dirname_type, dirname_size, dir_path.DebugStr().c_str()); | 426 | dirname_type, dirname_size, dir_path.DebugStr().c_str()); |
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 6ff0f4812..bc964ec60 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -389,8 +389,8 @@ static void RegisterInterruptRelayQueue(Interface* self) { | |||
| 389 | } else { | 389 | } else { |
| 390 | cmd_buff[1] = RESULT_SUCCESS.raw; | 390 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 391 | } | 391 | } |
| 392 | cmd_buff[2] = g_thread_id++; // Thread ID | 392 | cmd_buff[2] = g_thread_id++; // Thread ID |
| 393 | cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); // GSP shared memory | 393 | cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).Unwrap(); // GSP shared memory |
| 394 | 394 | ||
| 395 | g_interrupt_event->Signal(); // TODO(bunnei): Is this correct? | 395 | g_interrupt_event->Signal(); // TODO(bunnei): Is this correct? |
| 396 | 396 | ||
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 5255f6dc8..2014b8461 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -253,12 +253,12 @@ void GetIPCHandles(Service::Interface* self) { | |||
| 253 | cmd_buff[1] = 0; // No error | 253 | cmd_buff[1] = 0; // No error |
| 254 | cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header | 254 | cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header |
| 255 | // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) | 255 | // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) |
| 256 | cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).MoveFrom(); | 256 | cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).Unwrap(); |
| 257 | cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).MoveFrom(); | 257 | cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).Unwrap(); |
| 258 | cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).MoveFrom(); | 258 | cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).Unwrap(); |
| 259 | cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).MoveFrom(); | 259 | cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).Unwrap(); |
| 260 | cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).MoveFrom(); | 260 | cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).Unwrap(); |
| 261 | cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).MoveFrom(); | 261 | cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).Unwrap(); |
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | void EnableAccelerometer(Service::Interface* self) { | 264 | void EnableAccelerometer(Service::Interface* self) { |
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 0de698003..837413f93 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp | |||
| @@ -145,8 +145,8 @@ static void GetHandles(Interface* self) { | |||
| 145 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 0, 0); | 145 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 0, 0); |
| 146 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); | 146 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); |
| 147 | rb.Push(RESULT_SUCCESS); | 147 | rb.Push(RESULT_SUCCESS); |
| 148 | rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(), | 148 | rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).Unwrap(), |
| 149 | Kernel::g_handle_table.Create(Service::IR::update_event).MoveFrom()); | 149 | Kernel::g_handle_table.Create(Service::IR::update_event).Unwrap()); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | /** | 152 | /** |
diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index fdecdce64..fbdf7a465 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp | |||
| @@ -337,7 +337,7 @@ void GetReceiveEvent(Interface* self) { | |||
| 337 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2); | 337 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2); |
| 338 | 338 | ||
| 339 | rb.Push(RESULT_SUCCESS); | 339 | rb.Push(RESULT_SUCCESS); |
| 340 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).MoveFrom()); | 340 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).Unwrap()); |
| 341 | 341 | ||
| 342 | LOG_INFO(Service_IR, "called"); | 342 | LOG_INFO(Service_IR, "called"); |
| 343 | } | 343 | } |
| @@ -354,7 +354,7 @@ void GetSendEvent(Interface* self) { | |||
| 354 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2); | 354 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2); |
| 355 | 355 | ||
| 356 | rb.Push(RESULT_SUCCESS); | 356 | rb.Push(RESULT_SUCCESS); |
| 357 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).MoveFrom()); | 357 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).Unwrap()); |
| 358 | 358 | ||
| 359 | LOG_INFO(Service_IR, "called"); | 359 | LOG_INFO(Service_IR, "called"); |
| 360 | } | 360 | } |
| @@ -394,7 +394,7 @@ static void GetConnectionStatusEvent(Interface* self) { | |||
| 394 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2); | 394 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2); |
| 395 | 395 | ||
| 396 | rb.Push(RESULT_SUCCESS); | 396 | rb.Push(RESULT_SUCCESS); |
| 397 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom()); | 397 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).Unwrap()); |
| 398 | 398 | ||
| 399 | LOG_INFO(Service_IR, "called"); | 399 | LOG_INFO(Service_IR, "called"); |
| 400 | } | 400 | } |
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 35212b59b..23e1ff094 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp | |||
| @@ -160,7 +160,7 @@ static void IsSampling(Interface* self) { | |||
| 160 | static void GetBufferFullEvent(Interface* self) { | 160 | static void GetBufferFullEvent(Interface* self) { |
| 161 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 161 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 162 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 162 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 163 | cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom(); | 163 | cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).Unwrap(); |
| 164 | LOG_WARNING(Service_MIC, "(STUBBED) called"); | 164 | LOG_WARNING(Service_MIC, "(STUBBED) called"); |
| 165 | } | 165 | } |
| 166 | 166 | ||
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index b44a9f668..cb09ed0b7 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp | |||
| @@ -95,7 +95,7 @@ void GetTagInRangeEvent(Interface* self) { | |||
| 95 | cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2); | 95 | cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2); |
| 96 | cmd_buff[1] = RESULT_SUCCESS.raw; | 96 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 97 | cmd_buff[2] = IPC::CopyHandleDesc(); | 97 | cmd_buff[2] = IPC::CopyHandleDesc(); |
| 98 | cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).MoveFrom(); | 98 | cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).Unwrap(); |
| 99 | LOG_WARNING(Service_NFC, "(STUBBED) called"); | 99 | LOG_WARNING(Service_NFC, "(STUBBED) called"); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| @@ -105,7 +105,7 @@ void GetTagOutOfRangeEvent(Interface* self) { | |||
| 105 | cmd_buff[0] = IPC::MakeHeader(0xC, 1, 2); | 105 | cmd_buff[0] = IPC::MakeHeader(0xC, 1, 2); |
| 106 | cmd_buff[1] = RESULT_SUCCESS.raw; | 106 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 107 | cmd_buff[2] = IPC::CopyHandleDesc(); | 107 | cmd_buff[2] = IPC::CopyHandleDesc(); |
| 108 | cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).MoveFrom(); | 108 | cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).Unwrap(); |
| 109 | LOG_WARNING(Service_NFC, "(STUBBED) called"); | 109 | LOG_WARNING(Service_NFC, "(STUBBED) called"); |
| 110 | } | 110 | } |
| 111 | 111 | ||
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index e92900d48..6dbdff044 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 16 | #include "core/hle/service/nwm/nwm_uds.h" | 16 | #include "core/hle/service/nwm/nwm_uds.h" |
| 17 | #include "core/hle/service/nwm/uds_beacon.h" | 17 | #include "core/hle/service/nwm/uds_beacon.h" |
| 18 | #include "core/hle/service/nwm/uds_data.h" | ||
| 18 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| 19 | 20 | ||
| 20 | namespace Service { | 21 | namespace Service { |
| @@ -190,7 +191,7 @@ static void InitializeWithVersion(Interface* self) { | |||
| 190 | 191 | ||
| 191 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | 192 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |
| 192 | rb.Push(RESULT_SUCCESS); | 193 | rb.Push(RESULT_SUCCESS); |
| 193 | rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).MoveFrom()); | 194 | rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); |
| 194 | 195 | ||
| 195 | LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", | 196 | LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", |
| 196 | sharedmem_size, version, sharedmem_handle); | 197 | sharedmem_size, version, sharedmem_handle); |
| @@ -265,7 +266,7 @@ static void Bind(Interface* self) { | |||
| 265 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | 266 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |
| 266 | 267 | ||
| 267 | rb.Push(RESULT_SUCCESS); | 268 | rb.Push(RESULT_SUCCESS); |
| 268 | rb.PushCopyHandles(Kernel::g_handle_table.Create(event).MoveFrom()); | 269 | rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap()); |
| 269 | } | 270 | } |
| 270 | 271 | ||
| 271 | /** | 272 | /** |
| @@ -373,6 +374,80 @@ static void DestroyNetwork(Interface* self) { | |||
| 373 | } | 374 | } |
| 374 | 375 | ||
| 375 | /** | 376 | /** |
| 377 | * NWM_UDS::SendTo service function. | ||
| 378 | * Sends a data frame to the UDS network we're connected to. | ||
| 379 | * Inputs: | ||
| 380 | * 0 : Command header. | ||
| 381 | * 1 : Unknown. | ||
| 382 | * 2 : u16 Destination network node id. | ||
| 383 | * 3 : u8 Data channel. | ||
| 384 | * 4 : Buffer size >> 2 | ||
| 385 | * 5 : Data size | ||
| 386 | * 6 : Flags | ||
| 387 | * 7 : Input buffer descriptor | ||
| 388 | * 8 : Input buffer address | ||
| 389 | * Outputs: | ||
| 390 | * 0 : Return header | ||
| 391 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 392 | */ | ||
| 393 | static void SendTo(Interface* self) { | ||
| 394 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 6, 2); | ||
| 395 | |||
| 396 | rp.Skip(1, false); | ||
| 397 | u16 dest_node_id = rp.Pop<u16>(); | ||
| 398 | u8 data_channel = rp.Pop<u8>(); | ||
| 399 | rp.Skip(1, false); | ||
| 400 | u32 data_size = rp.Pop<u32>(); | ||
| 401 | u32 flags = rp.Pop<u32>(); | ||
| 402 | |||
| 403 | size_t desc_size; | ||
| 404 | const VAddr input_address = rp.PopStaticBuffer(&desc_size, false); | ||
| 405 | ASSERT(desc_size == data_size); | ||
| 406 | |||
| 407 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 408 | |||
| 409 | if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && | ||
| 410 | connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { | ||
| 411 | rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, | ||
| 412 | ErrorSummary::InvalidState, ErrorLevel::Status)); | ||
| 413 | return; | ||
| 414 | } | ||
| 415 | |||
| 416 | if (dest_node_id == connection_status.network_node_id) { | ||
| 417 | rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, | ||
| 418 | ErrorSummary::WrongArgument, ErrorLevel::Status)); | ||
| 419 | return; | ||
| 420 | } | ||
| 421 | |||
| 422 | // TODO(Subv): Do something with the flags. | ||
| 423 | |||
| 424 | constexpr size_t MaxSize = 0x5C6; | ||
| 425 | if (data_size > MaxSize) { | ||
| 426 | rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, | ||
| 427 | ErrorSummary::WrongArgument, ErrorLevel::Usage)); | ||
| 428 | return; | ||
| 429 | } | ||
| 430 | |||
| 431 | std::vector<u8> data(data_size); | ||
| 432 | Memory::ReadBlock(input_address, data.data(), data.size()); | ||
| 433 | |||
| 434 | // TODO(Subv): Increment the sequence number after each sent packet. | ||
| 435 | u16 sequence_number = 0; | ||
| 436 | std::vector<u8> data_payload = GenerateDataPayload( | ||
| 437 | data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); | ||
| 438 | |||
| 439 | // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt | ||
| 440 | // and encapsulate the payload. | ||
| 441 | |||
| 442 | // TODO(Subv): Send the frame. | ||
| 443 | |||
| 444 | rb.Push(RESULT_SUCCESS); | ||
| 445 | |||
| 446 | LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u", | ||
| 447 | static_cast<u32>(dest_node_id), data_size, flags, static_cast<u32>(data_channel)); | ||
| 448 | } | ||
| 449 | |||
| 450 | /** | ||
| 376 | * NWM_UDS::GetChannel service function. | 451 | * NWM_UDS::GetChannel service function. |
| 377 | * Returns the WiFi channel in which the network we're connected to is transmitting. | 452 | * Returns the WiFi channel in which the network we're connected to is transmitting. |
| 378 | * Inputs: | 453 | * Inputs: |
| @@ -543,6 +618,42 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { | |||
| 543 | beacon_broadcast_event, 0); | 618 | beacon_broadcast_event, 0); |
| 544 | } | 619 | } |
| 545 | 620 | ||
| 621 | /* | ||
| 622 | * Returns an available index in the nodes array for the | ||
| 623 | * currently-hosted UDS network. | ||
| 624 | */ | ||
| 625 | static u32 GetNextAvailableNodeId() { | ||
| 626 | ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), | ||
| 627 | "Can not accept clients if we're not hosting a network"); | ||
| 628 | |||
| 629 | for (unsigned index = 0; index < connection_status.max_nodes; ++index) { | ||
| 630 | if ((connection_status.node_bitmask & (1 << index)) == 0) | ||
| 631 | return index; | ||
| 632 | } | ||
| 633 | |||
| 634 | // Any connection attempts to an already full network should have been refused. | ||
| 635 | ASSERT_MSG(false, "No available connection slots in the network"); | ||
| 636 | } | ||
| 637 | |||
| 638 | /* | ||
| 639 | * Called when a client connects to an UDS network we're hosting, | ||
| 640 | * updates the connection status and signals the update event. | ||
| 641 | * @param network_node_id Network Node Id of the connecting client. | ||
| 642 | */ | ||
| 643 | void OnClientConnected(u16 network_node_id) { | ||
| 644 | ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), | ||
| 645 | "Can not accept clients if we're not hosting a network"); | ||
| 646 | ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, | ||
| 647 | "Can not accept connections on a full network"); | ||
| 648 | |||
| 649 | u32 node_id = GetNextAvailableNodeId(); | ||
| 650 | connection_status.node_bitmask |= 1 << node_id; | ||
| 651 | connection_status.changed_nodes |= 1 << node_id; | ||
| 652 | connection_status.nodes[node_id] = network_node_id; | ||
| 653 | connection_status.total_nodes++; | ||
| 654 | connection_status_event->Signal(); | ||
| 655 | } | ||
| 656 | |||
| 546 | const Interface::FunctionInfo FunctionTable[] = { | 657 | const Interface::FunctionInfo FunctionTable[] = { |
| 547 | {0x00010442, nullptr, "Initialize (deprecated)"}, | 658 | {0x00010442, nullptr, "Initialize (deprecated)"}, |
| 548 | {0x00020000, nullptr, "Scrap"}, | 659 | {0x00020000, nullptr, "Scrap"}, |
| @@ -564,7 +675,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 564 | {0x00130040, nullptr, "Unbind"}, | 675 | {0x00130040, nullptr, "Unbind"}, |
| 565 | {0x001400C0, nullptr, "PullPacket"}, | 676 | {0x001400C0, nullptr, "PullPacket"}, |
| 566 | {0x00150080, nullptr, "SetMaxSendDelay"}, | 677 | {0x00150080, nullptr, "SetMaxSendDelay"}, |
| 567 | {0x00170182, nullptr, "SendTo"}, | 678 | {0x00170182, SendTo, "SendTo"}, |
| 568 | {0x001A0000, GetChannel, "GetChannel"}, | 679 | {0x001A0000, GetChannel, "GetChannel"}, |
| 569 | {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, | 680 | {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, |
| 570 | {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, | 681 | {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, |
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp new file mode 100644 index 000000000..8c6742dba --- /dev/null +++ b/src/core/hle/service/nwm/uds_data.cpp | |||
| @@ -0,0 +1,278 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <cryptopp/aes.h> | ||
| 7 | #include <cryptopp/ccm.h> | ||
| 8 | #include <cryptopp/filters.h> | ||
| 9 | #include <cryptopp/md5.h> | ||
| 10 | #include <cryptopp/modes.h> | ||
| 11 | #include "core/hle/service/nwm/nwm_uds.h" | ||
| 12 | #include "core/hle/service/nwm/uds_data.h" | ||
| 13 | #include "core/hw/aes/key.h" | ||
| 14 | |||
| 15 | namespace Service { | ||
| 16 | namespace NWM { | ||
| 17 | |||
| 18 | using MacAddress = std::array<u8, 6>; | ||
| 19 | |||
| 20 | /* | ||
| 21 | * Generates a SNAP-enabled 802.2 LLC header for the specified protocol. | ||
| 22 | * @returns a buffer with the bytes of the generated header. | ||
| 23 | */ | ||
| 24 | static std::vector<u8> GenerateLLCHeader(EtherType protocol) { | ||
| 25 | LLCHeader header{}; | ||
| 26 | header.protocol = static_cast<u16>(protocol); | ||
| 27 | |||
| 28 | std::vector<u8> buffer(sizeof(header)); | ||
| 29 | memcpy(buffer.data(), &header, sizeof(header)); | ||
| 30 | |||
| 31 | return buffer; | ||
| 32 | } | ||
| 33 | |||
| 34 | /* | ||
| 35 | * Generates a Nintendo UDS SecureData header with the specified parameters. | ||
| 36 | * @returns a buffer with the bytes of the generated header. | ||
| 37 | */ | ||
| 38 | static std::vector<u8> GenerateSecureDataHeader(u16 data_size, u8 channel, u16 dest_node_id, | ||
| 39 | u16 src_node_id, u16 sequence_number) { | ||
| 40 | SecureDataHeader header{}; | ||
| 41 | header.protocol_size = data_size + sizeof(SecureDataHeader); | ||
| 42 | // Note: This size includes everything except the first 4 bytes of the structure, | ||
| 43 | // reinforcing the hypotheses that the first 4 bytes are actually the header of | ||
| 44 | // another container protocol. | ||
| 45 | header.securedata_size = data_size + sizeof(SecureDataHeader) - 4; | ||
| 46 | // Frames sent by the emulated application are never UDS management frames | ||
| 47 | header.is_management = 0; | ||
| 48 | header.data_channel = channel; | ||
| 49 | header.sequence_number = sequence_number; | ||
| 50 | header.dest_node_id = dest_node_id; | ||
| 51 | header.src_node_id = src_node_id; | ||
| 52 | |||
| 53 | std::vector<u8> buffer(sizeof(header)); | ||
| 54 | memcpy(buffer.data(), &header, sizeof(header)); | ||
| 55 | |||
| 56 | return buffer; | ||
| 57 | } | ||
| 58 | |||
| 59 | /* | ||
| 60 | * Calculates the CTR used for the AES-CTR process that calculates | ||
| 61 | * the CCMP crypto key for data frames. | ||
| 62 | * @returns The CTR used for data frames crypto key generation. | ||
| 63 | */ | ||
| 64 | static std::array<u8, CryptoPP::MD5::DIGESTSIZE> GetDataCryptoCTR(const NetworkInfo& network_info) { | ||
| 65 | DataFrameCryptoCTR data{}; | ||
| 66 | |||
| 67 | data.host_mac = network_info.host_mac_address; | ||
| 68 | data.wlan_comm_id = network_info.wlan_comm_id; | ||
| 69 | data.id = network_info.id; | ||
| 70 | data.network_id = network_info.network_id; | ||
| 71 | |||
| 72 | std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash; | ||
| 73 | CryptoPP::MD5().CalculateDigest(hash.data(), reinterpret_cast<u8*>(&data), sizeof(data)); | ||
| 74 | |||
| 75 | return hash; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* | ||
| 79 | * Generates the key used for encrypting the 802.11 data frames generated by UDS. | ||
| 80 | * @returns The key used for data frames crypto. | ||
| 81 | */ | ||
| 82 | static std::array<u8, CryptoPP::AES::BLOCKSIZE> GenerateDataCCMPKey( | ||
| 83 | const std::vector<u8>& passphrase, const NetworkInfo& network_info) { | ||
| 84 | // Calculate the MD5 hash of the input passphrase. | ||
| 85 | std::array<u8, CryptoPP::MD5::DIGESTSIZE> passphrase_hash; | ||
| 86 | CryptoPP::MD5().CalculateDigest(passphrase_hash.data(), passphrase.data(), passphrase.size()); | ||
| 87 | |||
| 88 | std::array<u8, CryptoPP::AES::BLOCKSIZE> ccmp_key; | ||
| 89 | |||
| 90 | // The CCMP key is the result of encrypting the MD5 hash of the passphrase with AES-CTR using | ||
| 91 | // keyslot 0x2D. | ||
| 92 | using CryptoPP::AES; | ||
| 93 | std::array<u8, CryptoPP::MD5::DIGESTSIZE> counter = GetDataCryptoCTR(network_info); | ||
| 94 | std::array<u8, AES::BLOCKSIZE> key = HW::AES::GetNormalKey(HW::AES::KeySlotID::UDSDataKey); | ||
| 95 | CryptoPP::CTR_Mode<AES>::Encryption aes; | ||
| 96 | aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, counter.data()); | ||
| 97 | aes.ProcessData(ccmp_key.data(), passphrase_hash.data(), passphrase_hash.size()); | ||
| 98 | |||
| 99 | return ccmp_key; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* | ||
| 103 | * Generates the Additional Authenticated Data (AAD) for an UDS 802.11 encrypted data frame. | ||
| 104 | * @returns a buffer with the bytes of the AAD. | ||
| 105 | */ | ||
| 106 | static std::vector<u8> GenerateCCMPAAD(const MacAddress& sender, const MacAddress& receiver, | ||
| 107 | const MacAddress& bssid, u16 frame_control) { | ||
| 108 | // Reference: IEEE 802.11-2007 | ||
| 109 | |||
| 110 | // 8.3.3.3.2 Construct AAD (22-30 bytes) | ||
| 111 | // The AAD is constructed from the MPDU header. The AAD does not include the header Duration | ||
| 112 | // field, because the Duration field value can change due to normal IEEE 802.11 operation (e.g., | ||
| 113 | // a rate change during retransmission). For similar reasons, several subfields in the Frame | ||
| 114 | // Control field are masked to 0. | ||
| 115 | struct { | ||
| 116 | u16_be FC; // MPDU Frame Control field | ||
| 117 | MacAddress A1; | ||
| 118 | MacAddress A2; | ||
| 119 | MacAddress A3; | ||
| 120 | u16_be SC; // MPDU Sequence Control field | ||
| 121 | } aad_struct{}; | ||
| 122 | |||
| 123 | constexpr u16 AADFrameControlMask = 0x8FC7; | ||
| 124 | aad_struct.FC = frame_control & AADFrameControlMask; | ||
| 125 | aad_struct.SC = 0; | ||
| 126 | |||
| 127 | bool to_ds = (frame_control & (1 << 0)) != 0; | ||
| 128 | bool from_ds = (frame_control & (1 << 1)) != 0; | ||
| 129 | // In the 802.11 standard, ToDS = 1 and FromDS = 1 is a valid configuration, | ||
| 130 | // however, the 3DS doesn't seem to transmit frames with such combination. | ||
| 131 | ASSERT_MSG(to_ds != from_ds, "Invalid combination"); | ||
| 132 | |||
| 133 | // The meaning of the address fields depends on the ToDS and FromDS fields. | ||
| 134 | if (from_ds) { | ||
| 135 | aad_struct.A1 = receiver; | ||
| 136 | aad_struct.A2 = bssid; | ||
| 137 | aad_struct.A3 = sender; | ||
| 138 | } | ||
| 139 | |||
| 140 | if (to_ds) { | ||
| 141 | aad_struct.A1 = bssid; | ||
| 142 | aad_struct.A2 = sender; | ||
| 143 | aad_struct.A3 = receiver; | ||
| 144 | } | ||
| 145 | |||
| 146 | std::vector<u8> aad(sizeof(aad_struct)); | ||
| 147 | std::memcpy(aad.data(), &aad_struct, sizeof(aad_struct)); | ||
| 148 | |||
| 149 | return aad; | ||
| 150 | } | ||
| 151 | |||
| 152 | /* | ||
| 153 | * Decrypts the payload of an encrypted 802.11 data frame using the specified key. | ||
| 154 | * @returns The decrypted payload. | ||
| 155 | */ | ||
| 156 | static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload, | ||
| 157 | const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key, | ||
| 158 | const MacAddress& sender, const MacAddress& receiver, | ||
| 159 | const MacAddress& bssid, u16 sequence_number, | ||
| 160 | u16 frame_control) { | ||
| 161 | |||
| 162 | // Reference: IEEE 802.11-2007 | ||
| 163 | |||
| 164 | std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control); | ||
| 165 | |||
| 166 | std::vector<u8> packet_number{0, | ||
| 167 | 0, | ||
| 168 | 0, | ||
| 169 | 0, | ||
| 170 | static_cast<u8>((sequence_number >> 8) & 0xFF), | ||
| 171 | static_cast<u8>(sequence_number & 0xFF)}; | ||
| 172 | |||
| 173 | // 8.3.3.3.3 Construct CCM nonce (13 bytes) | ||
| 174 | std::vector<u8> nonce; | ||
| 175 | nonce.push_back(0); // priority | ||
| 176 | nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2 | ||
| 177 | nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN | ||
| 178 | |||
| 179 | try { | ||
| 180 | CryptoPP::CCM<CryptoPP::AES, 8>::Decryption d; | ||
| 181 | d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size()); | ||
| 182 | d.SpecifyDataLengths(aad.size(), encrypted_payload.size() - 8, 0); | ||
| 183 | |||
| 184 | CryptoPP::AuthenticatedDecryptionFilter df( | ||
| 185 | d, nullptr, CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_END | | ||
| 186 | CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION); | ||
| 187 | // put aad | ||
| 188 | df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size()); | ||
| 189 | |||
| 190 | // put cipher with mac | ||
| 191 | df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, encrypted_payload.data(), | ||
| 192 | encrypted_payload.size() - 8); | ||
| 193 | df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, | ||
| 194 | encrypted_payload.data() + encrypted_payload.size() - 8, 8); | ||
| 195 | |||
| 196 | df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL); | ||
| 197 | df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); | ||
| 198 | df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); | ||
| 199 | |||
| 200 | int size = df.MaxRetrievable(); | ||
| 201 | |||
| 202 | std::vector<u8> pdata(size); | ||
| 203 | df.Get(pdata.data(), size); | ||
| 204 | return pdata; | ||
| 205 | } catch (CryptoPP::Exception&) { | ||
| 206 | LOG_ERROR(Service_NWM, "failed to decrypt"); | ||
| 207 | } | ||
| 208 | |||
| 209 | return {}; | ||
| 210 | } | ||
| 211 | |||
| 212 | /* | ||
| 213 | * Encrypts the payload of an 802.11 data frame using the specified key. | ||
| 214 | * @returns The encrypted payload. | ||
| 215 | */ | ||
| 216 | static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload, | ||
| 217 | const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key, | ||
| 218 | const MacAddress& sender, const MacAddress& receiver, | ||
| 219 | const MacAddress& bssid, u16 sequence_number, | ||
| 220 | u16 frame_control) { | ||
| 221 | // Reference: IEEE 802.11-2007 | ||
| 222 | |||
| 223 | std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control); | ||
| 224 | |||
| 225 | std::vector<u8> packet_number{0, | ||
| 226 | 0, | ||
| 227 | 0, | ||
| 228 | 0, | ||
| 229 | static_cast<u8>((sequence_number >> 8) & 0xFF), | ||
| 230 | static_cast<u8>(sequence_number & 0xFF)}; | ||
| 231 | |||
| 232 | // 8.3.3.3.3 Construct CCM nonce (13 bytes) | ||
| 233 | std::vector<u8> nonce; | ||
| 234 | nonce.push_back(0); // priority | ||
| 235 | nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2 | ||
| 236 | nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN | ||
| 237 | |||
| 238 | try { | ||
| 239 | CryptoPP::CCM<CryptoPP::AES, 8>::Encryption d; | ||
| 240 | d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size()); | ||
| 241 | d.SpecifyDataLengths(aad.size(), payload.size(), 0); | ||
| 242 | |||
| 243 | CryptoPP::AuthenticatedEncryptionFilter df(d); | ||
| 244 | // put aad | ||
| 245 | df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size()); | ||
| 246 | df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL); | ||
| 247 | |||
| 248 | // put plaintext | ||
| 249 | df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, payload.data(), payload.size()); | ||
| 250 | df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); | ||
| 251 | |||
| 252 | df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); | ||
| 253 | |||
| 254 | int size = df.MaxRetrievable(); | ||
| 255 | |||
| 256 | std::vector<u8> cipher(size); | ||
| 257 | df.Get(cipher.data(), size); | ||
| 258 | return cipher; | ||
| 259 | } catch (CryptoPP::Exception&) { | ||
| 260 | LOG_ERROR(Service_NWM, "failed to encrypt"); | ||
| 261 | } | ||
| 262 | |||
| 263 | return {}; | ||
| 264 | } | ||
| 265 | |||
| 266 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | ||
| 267 | u16 src_node, u16 sequence_number) { | ||
| 268 | std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData); | ||
| 269 | std::vector<u8> securedata_header = | ||
| 270 | GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number); | ||
| 271 | |||
| 272 | buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); | ||
| 273 | buffer.insert(buffer.end(), data.begin(), data.end()); | ||
| 274 | return buffer; | ||
| 275 | } | ||
| 276 | |||
| 277 | } // namespace NWM | ||
| 278 | } // namespace Service | ||
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h new file mode 100644 index 000000000..a23520a41 --- /dev/null +++ b/src/core/hle/service/nwm/uds_data.h | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <vector> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/swap.h" | ||
| 11 | #include "core/hle/service/service.h" | ||
| 12 | |||
| 13 | namespace Service { | ||
| 14 | namespace NWM { | ||
| 15 | |||
| 16 | enum class SAP : u8 { SNAPExtensionUsed = 0xAA }; | ||
| 17 | |||
| 18 | enum class PDUControl : u8 { UnnumberedInformation = 3 }; | ||
| 19 | |||
| 20 | enum class EtherType : u16 { SecureData = 0x876D, EAPoL = 0x888E }; | ||
| 21 | |||
| 22 | /* | ||
| 23 | * 802.2 header, UDS packets always use SNAP for these headers, | ||
| 24 | * which means the dsap and ssap are always SNAPExtensionUsed (0xAA) | ||
| 25 | * and the OUI is always 0. | ||
| 26 | */ | ||
| 27 | struct LLCHeader { | ||
| 28 | u8 dsap = static_cast<u8>(SAP::SNAPExtensionUsed); | ||
| 29 | u8 ssap = static_cast<u8>(SAP::SNAPExtensionUsed); | ||
| 30 | u8 control = static_cast<u8>(PDUControl::UnnumberedInformation); | ||
| 31 | std::array<u8, 3> OUI = {}; | ||
| 32 | u16_be protocol; | ||
| 33 | }; | ||
| 34 | |||
| 35 | static_assert(sizeof(LLCHeader) == 8, "LLCHeader has the wrong size"); | ||
| 36 | |||
| 37 | /* | ||
| 38 | * Nintendo SecureData header, every UDS packet contains one, | ||
| 39 | * it is used to store metadata about the transmission such as | ||
| 40 | * the source and destination network node ids. | ||
| 41 | */ | ||
| 42 | struct SecureDataHeader { | ||
| 43 | // TODO(Subv): It is likely that the first 4 bytes of this header are | ||
| 44 | // actually part of another container protocol. | ||
| 45 | u16_be protocol_size; | ||
| 46 | INSERT_PADDING_BYTES(2); | ||
| 47 | u16_be securedata_size; | ||
| 48 | u8 is_management; | ||
| 49 | u8 data_channel; | ||
| 50 | u16_be sequence_number; | ||
| 51 | u16_be dest_node_id; | ||
| 52 | u16_be src_node_id; | ||
| 53 | }; | ||
| 54 | |||
| 55 | static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size"); | ||
| 56 | |||
| 57 | /* | ||
| 58 | * The raw bytes of this structure are the CTR used in the encryption (AES-CTR) | ||
| 59 | * process used to generate the CCMP key for data frame encryption. | ||
| 60 | */ | ||
| 61 | struct DataFrameCryptoCTR { | ||
| 62 | u32_le wlan_comm_id; | ||
| 63 | u32_le network_id; | ||
| 64 | std::array<u8, 6> host_mac; | ||
| 65 | u16_le id; | ||
| 66 | }; | ||
| 67 | |||
| 68 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Generates an unencrypted 802.11 data payload. | ||
| 72 | * @returns The generated frame payload. | ||
| 73 | */ | ||
| 74 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | ||
| 75 | u16 src_node, u16 sequence_number); | ||
| 76 | |||
| 77 | } // namespace NWM | ||
| 78 | } // namespace Service | ||
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 39382ef09..a0b959797 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp | |||
| @@ -152,7 +152,7 @@ void Init() { | |||
| 152 | auto gamecoin_result = | 152 | auto gamecoin_result = |
| 153 | Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); | 153 | Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); |
| 154 | if (gamecoin_result.Succeeded()) { | 154 | if (gamecoin_result.Succeeded()) { |
| 155 | auto gamecoin = gamecoin_result.MoveFrom(); | 155 | auto gamecoin = std::move(gamecoin_result).Unwrap(); |
| 156 | gamecoin->backend->Write(0, sizeof(GameCoin), true, | 156 | gamecoin->backend->Write(0, sizeof(GameCoin), true, |
| 157 | reinterpret_cast<const u8*>(&default_game_coin)); | 157 | reinterpret_cast<const u8*>(&default_game_coin)); |
| 158 | gamecoin->backend->Close(); | 158 | gamecoin->backend->Close(); |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 791a65c19..aad950e50 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -173,8 +173,7 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses | |||
| 173 | 173 | ||
| 174 | // TODO(yuriks): The kernel should be the one handling this as part of translation after | 174 | // TODO(yuriks): The kernel should be the one handling this as part of translation after |
| 175 | // everything else is migrated | 175 | // everything else is migrated |
| 176 | Kernel::HLERequestContext context; | 176 | Kernel::HLERequestContext context(std::move(server_session)); |
| 177 | context.session = std::move(server_session); | ||
| 178 | context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, | 177 | context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, |
| 179 | Kernel::g_handle_table); | 178 | Kernel::g_handle_table); |
| 180 | 179 | ||
| @@ -207,7 +206,7 @@ void AddService(Interface* interface_) { | |||
| 207 | auto server_port = | 206 | auto server_port = |
| 208 | SM::g_service_manager | 207 | SM::g_service_manager |
| 209 | ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) | 208 | ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) |
| 210 | .MoveFrom(); | 209 | .Unwrap(); |
| 211 | server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); | 210 | server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); |
| 212 | } | 211 | } |
| 213 | 212 | ||
diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index 74a1256e0..352941e69 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp | |||
| @@ -113,13 +113,13 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { | |||
| 113 | (*session)->GetObjectId()); | 113 | (*session)->GetObjectId()); |
| 114 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | 114 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |
| 115 | rb.Push(session.Code()); | 115 | rb.Push(session.Code()); |
| 116 | rb.PushObjects(session.MoveFrom()); | 116 | rb.PushObjects(std::move(session).Unwrap()); |
| 117 | } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { | 117 | } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { |
| 118 | LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", | 118 | LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", |
| 119 | name.c_str(), (*client_port)->GetObjectId()); | 119 | name.c_str(), (*client_port)->GetObjectId()); |
| 120 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | 120 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |
| 121 | rb.Push(ERR_MAX_CONNECTIONS_REACHED); | 121 | rb.Push(ERR_MAX_CONNECTIONS_REACHED); |
| 122 | rb.PushObjects(client_port.MoveFrom()); | 122 | rb.PushObjects(std::move(client_port).Unwrap()); |
| 123 | } else { | 123 | } else { |
| 124 | LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); | 124 | LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); |
| 125 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | 125 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index bb7bf2d67..e73971d5f 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp | |||
| @@ -275,7 +275,7 @@ static void GetTransferEndEvent(Interface* self) { | |||
| 275 | 275 | ||
| 276 | cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); | 276 | cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); |
| 277 | cmd_buff[1] = RESULT_SUCCESS.raw; | 277 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 278 | cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); | 278 | cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).Unwrap(); |
| 279 | 279 | ||
| 280 | LOG_DEBUG(Service_Y2R, "called"); | 280 | LOG_DEBUG(Service_Y2R, "called"); |
| 281 | } | 281 | } |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 0a7f011f3..e4b803046 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -37,8 +37,9 @@ | |||
| 37 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 37 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 38 | // Namespace SVC | 38 | // Namespace SVC |
| 39 | 39 | ||
| 40 | using Kernel::SharedPtr; | ||
| 41 | using Kernel::ERR_INVALID_HANDLE; | 40 | using Kernel::ERR_INVALID_HANDLE; |
| 41 | using Kernel::Handle; | ||
| 42 | using Kernel::SharedPtr; | ||
| 42 | 43 | ||
| 43 | namespace SVC { | 44 | namespace SVC { |
| 44 | 45 | ||
| @@ -1040,7 +1041,6 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client | |||
| 1040 | 1041 | ||
| 1041 | using Kernel::ServerPort; | 1042 | using Kernel::ServerPort; |
| 1042 | using Kernel::ClientPort; | 1043 | using Kernel::ClientPort; |
| 1043 | using Kernel::SharedPtr; | ||
| 1044 | 1044 | ||
| 1045 | auto ports = ServerPort::CreatePortPair(max_sessions); | 1045 | auto ports = ServerPort::CreatePortPair(max_sessions); |
| 1046 | CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create( | 1046 | CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create( |
| @@ -1054,6 +1054,41 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client | |||
| 1054 | return RESULT_SUCCESS; | 1054 | return RESULT_SUCCESS; |
| 1055 | } | 1055 | } |
| 1056 | 1056 | ||
| 1057 | static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) { | ||
| 1058 | using Kernel::ClientPort; | ||
| 1059 | SharedPtr<ClientPort> client_port = Kernel::g_handle_table.Get<ClientPort>(client_port_handle); | ||
| 1060 | if (client_port == nullptr) | ||
| 1061 | return ERR_INVALID_HANDLE; | ||
| 1062 | |||
| 1063 | CASCADE_RESULT(auto session, client_port->Connect()); | ||
| 1064 | CASCADE_RESULT(*out_client_session, Kernel::g_handle_table.Create(std::move(session))); | ||
| 1065 | return RESULT_SUCCESS; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | static ResultCode CreateSession(Handle* server_session, Handle* client_session) { | ||
| 1069 | auto sessions = Kernel::ServerSession::CreateSessionPair(); | ||
| 1070 | |||
| 1071 | auto& server = std::get<SharedPtr<Kernel::ServerSession>>(sessions); | ||
| 1072 | CASCADE_RESULT(*server_session, Kernel::g_handle_table.Create(std::move(server))); | ||
| 1073 | |||
| 1074 | auto& client = std::get<SharedPtr<Kernel::ClientSession>>(sessions); | ||
| 1075 | CASCADE_RESULT(*client_session, Kernel::g_handle_table.Create(std::move(client))); | ||
| 1076 | |||
| 1077 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 1078 | return RESULT_SUCCESS; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle) { | ||
| 1082 | using Kernel::ServerPort; | ||
| 1083 | SharedPtr<ServerPort> server_port = Kernel::g_handle_table.Get<ServerPort>(server_port_handle); | ||
| 1084 | if (server_port == nullptr) | ||
| 1085 | return ERR_INVALID_HANDLE; | ||
| 1086 | |||
| 1087 | CASCADE_RESULT(auto session, server_port->Accept()); | ||
| 1088 | CASCADE_RESULT(*out_server_session, Kernel::g_handle_table.Create(std::move(session))); | ||
| 1089 | return RESULT_SUCCESS; | ||
| 1090 | } | ||
| 1091 | |||
| 1057 | static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { | 1092 | static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { |
| 1058 | using Kernel::MemoryRegion; | 1093 | using Kernel::MemoryRegion; |
| 1059 | 1094 | ||
| @@ -1228,9 +1263,9 @@ static const FunctionDef SVC_Table[] = { | |||
| 1228 | {0x45, nullptr, "Unknown"}, | 1263 | {0x45, nullptr, "Unknown"}, |
| 1229 | {0x46, nullptr, "Unknown"}, | 1264 | {0x46, nullptr, "Unknown"}, |
| 1230 | {0x47, HLE::Wrap<CreatePort>, "CreatePort"}, | 1265 | {0x47, HLE::Wrap<CreatePort>, "CreatePort"}, |
| 1231 | {0x48, nullptr, "CreateSessionToPort"}, | 1266 | {0x48, HLE::Wrap<CreateSessionToPort>, "CreateSessionToPort"}, |
| 1232 | {0x49, nullptr, "CreateSession"}, | 1267 | {0x49, HLE::Wrap<CreateSession>, "CreateSession"}, |
| 1233 | {0x4A, nullptr, "AcceptSession"}, | 1268 | {0x4A, HLE::Wrap<AcceptSession>, "AcceptSession"}, |
| 1234 | {0x4B, nullptr, "ReplyAndReceive1"}, | 1269 | {0x4B, nullptr, "ReplyAndReceive1"}, |
| 1235 | {0x4C, nullptr, "ReplyAndReceive2"}, | 1270 | {0x4C, nullptr, "ReplyAndReceive2"}, |
| 1236 | {0x4D, nullptr, "ReplyAndReceive3"}, | 1271 | {0x4D, nullptr, "ReplyAndReceive3"}, |
diff --git a/src/core/hw/aes/key.h b/src/core/hw/aes/key.h index b01d04f13..c9f1342f4 100644 --- a/src/core/hw/aes/key.h +++ b/src/core/hw/aes/key.h | |||
| @@ -12,6 +12,8 @@ namespace HW { | |||
| 12 | namespace AES { | 12 | namespace AES { |
| 13 | 13 | ||
| 14 | enum KeySlotID : size_t { | 14 | enum KeySlotID : size_t { |
| 15 | // AES Keyslot used to generate the UDS data frame CCMP key. | ||
| 16 | UDSDataKey = 0x2D, | ||
| 15 | APTWrap = 0x31, | 17 | APTWrap = 0x31, |
| 16 | 18 | ||
| 17 | MaxKeySlotID = 0x40, | 19 | MaxKeySlotID = 0x40, |
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 42809c731..6838e449c 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <numeric> | 6 | #include <numeric> |
| 7 | #include <type_traits> | 7 | #include <type_traits> |
| 8 | #include "common/alignment.h" | ||
| 8 | #include "common/color.h" | 9 | #include "common/color.h" |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| @@ -313,7 +314,7 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) { | |||
| 313 | const PAddr src_addr = config.GetPhysicalInputAddress(); | 314 | const PAddr src_addr = config.GetPhysicalInputAddress(); |
| 314 | const PAddr dst_addr = config.GetPhysicalOutputAddress(); | 315 | const PAddr dst_addr = config.GetPhysicalOutputAddress(); |
| 315 | 316 | ||
| 316 | // TODO: do hwtest with these cases | 317 | // TODO: do hwtest with invalid addresses |
| 317 | if (!Memory::IsValidPhysicalAddress(src_addr)) { | 318 | if (!Memory::IsValidPhysicalAddress(src_addr)) { |
| 318 | LOG_CRITICAL(HW_GPU, "invalid input address 0x%08X", src_addr); | 319 | LOG_CRITICAL(HW_GPU, "invalid input address 0x%08X", src_addr); |
| 319 | return; | 320 | return; |
| @@ -324,31 +325,36 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) { | |||
| 324 | return; | 325 | return; |
| 325 | } | 326 | } |
| 326 | 327 | ||
| 327 | if (config.texture_copy.input_width == 0) { | 328 | if (VideoCore::g_renderer->Rasterizer()->AccelerateTextureCopy(config)) |
| 328 | LOG_CRITICAL(HW_GPU, "zero input width"); | ||
| 329 | return; | 329 | return; |
| 330 | } | ||
| 331 | 330 | ||
| 332 | if (config.texture_copy.output_width == 0) { | 331 | u8* src_pointer = Memory::GetPhysicalPointer(src_addr); |
| 333 | LOG_CRITICAL(HW_GPU, "zero output width"); | 332 | u8* dst_pointer = Memory::GetPhysicalPointer(dst_addr); |
| 333 | |||
| 334 | u32 remaining_size = Common::AlignDown(config.texture_copy.size, 16); | ||
| 335 | |||
| 336 | if (remaining_size == 0) { | ||
| 337 | LOG_CRITICAL(HW_GPU, "zero size. Real hardware freezes on this."); | ||
| 334 | return; | 338 | return; |
| 335 | } | 339 | } |
| 336 | 340 | ||
| 337 | if (config.texture_copy.size == 0) { | 341 | u32 input_gap = config.texture_copy.input_gap * 16; |
| 338 | LOG_CRITICAL(HW_GPU, "zero size"); | 342 | u32 output_gap = config.texture_copy.output_gap * 16; |
| 343 | |||
| 344 | // Zero gap means contiguous input/output even if width = 0. To avoid infinite loop below, width | ||
| 345 | // is assigned with the total size if gap = 0. | ||
| 346 | u32 input_width = input_gap == 0 ? remaining_size : config.texture_copy.input_width * 16; | ||
| 347 | u32 output_width = output_gap == 0 ? remaining_size : config.texture_copy.output_width * 16; | ||
| 348 | |||
| 349 | if (input_width == 0) { | ||
| 350 | LOG_CRITICAL(HW_GPU, "zero input width. Real hardware freezes on this."); | ||
| 339 | return; | 351 | return; |
| 340 | } | 352 | } |
| 341 | 353 | ||
| 342 | if (VideoCore::g_renderer->Rasterizer()->AccelerateTextureCopy(config)) | 354 | if (output_width == 0) { |
| 355 | LOG_CRITICAL(HW_GPU, "zero output width. Real hardware freezes on this."); | ||
| 343 | return; | 356 | return; |
| 344 | 357 | } | |
| 345 | u8* src_pointer = Memory::GetPhysicalPointer(src_addr); | ||
| 346 | u8* dst_pointer = Memory::GetPhysicalPointer(dst_addr); | ||
| 347 | |||
| 348 | u32 input_width = config.texture_copy.input_width * 16; | ||
| 349 | u32 input_gap = config.texture_copy.input_gap * 16; | ||
| 350 | u32 output_width = config.texture_copy.output_width * 16; | ||
| 351 | u32 output_gap = config.texture_copy.output_gap * 16; | ||
| 352 | 358 | ||
| 353 | size_t contiguous_input_size = | 359 | size_t contiguous_input_size = |
| 354 | config.texture_copy.size / input_width * (input_width + input_gap); | 360 | config.texture_copy.size / input_width * (input_width + input_gap); |
| @@ -360,7 +366,6 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) { | |||
| 360 | Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), | 366 | Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), |
| 361 | static_cast<u32>(contiguous_output_size)); | 367 | static_cast<u32>(contiguous_output_size)); |
| 362 | 368 | ||
| 363 | u32 remaining_size = config.texture_copy.size; | ||
| 364 | u32 remaining_input = input_width; | 369 | u32 remaining_input = input_width; |
| 365 | u32 remaining_output = output_width; | 370 | u32 remaining_output = output_width; |
| 366 | while (remaining_size > 0) { | 371 | while (remaining_size > 0) { |
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index bdd997b2a..21b127fee 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h | |||
| @@ -225,7 +225,7 @@ struct Regs { | |||
| 225 | INSERT_PADDING_WORDS(0x1); | 225 | INSERT_PADDING_WORDS(0x1); |
| 226 | 226 | ||
| 227 | struct { | 227 | struct { |
| 228 | u32 size; | 228 | u32 size; // The lower 4 bits are ignored |
| 229 | 229 | ||
| 230 | union { | 230 | union { |
| 231 | u32 input_size; | 231 | u32 input_size; |
diff --git a/src/core/memory.h b/src/core/memory.h index 802aa465e..71fb278ad 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -55,8 +55,10 @@ enum : PAddr { | |||
| 55 | 55 | ||
| 56 | /// Main FCRAM | 56 | /// Main FCRAM |
| 57 | FCRAM_PADDR = 0x20000000, | 57 | FCRAM_PADDR = 0x20000000, |
| 58 | FCRAM_SIZE = 0x08000000, ///< FCRAM size (128MB) | 58 | FCRAM_SIZE = 0x08000000, ///< FCRAM size on the Old 3DS (128MB) |
| 59 | FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB) | ||
| 59 | FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE, | 60 | FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE, |
| 61 | FCRAM_N3DS_PADDR_END = FCRAM_PADDR + FCRAM_N3DS_SIZE, | ||
| 60 | }; | 62 | }; |
| 61 | 63 | ||
| 62 | /// Virtual user-space memory regions | 64 | /// Virtual user-space memory regions |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 00d7c636a..a14df325a 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,8 +1,9 @@ | |||
| 1 | set(SRCS | 1 | set(SRCS |
| 2 | glad.cpp | ||
| 3 | tests.cpp | ||
| 4 | common/param_package.cpp | 2 | common/param_package.cpp |
| 5 | core/file_sys/path_parser.cpp | 3 | core/file_sys/path_parser.cpp |
| 4 | core/hle/kernel/hle_ipc.cpp | ||
| 5 | glad.cpp | ||
| 6 | tests.cpp | ||
| 6 | ) | 7 | ) |
| 7 | 8 | ||
| 8 | set(HEADERS | 9 | set(HEADERS |
diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp new file mode 100644 index 000000000..52336d027 --- /dev/null +++ b/src/tests/core/hle/kernel/hle_ipc.cpp | |||
| @@ -0,0 +1,216 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch.hpp> | ||
| 6 | #include "core/hle/ipc.h" | ||
| 7 | #include "core/hle/kernel/client_port.h" | ||
| 8 | #include "core/hle/kernel/client_session.h" | ||
| 9 | #include "core/hle/kernel/event.h" | ||
| 10 | #include "core/hle/kernel/handle_table.h" | ||
| 11 | #include "core/hle/kernel/hle_ipc.h" | ||
| 12 | #include "core/hle/kernel/process.h" | ||
| 13 | #include "core/hle/kernel/server_session.h" | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | |||
| 17 | static SharedPtr<Object> MakeObject() { | ||
| 18 | return Event::Create(ResetType::OneShot); | ||
| 19 | } | ||
| 20 | |||
| 21 | TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { | ||
| 22 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); | ||
| 23 | HLERequestContext context(std::move(session)); | ||
| 24 | |||
| 25 | auto process = Process::Create(CodeSet::Create("", 0)); | ||
| 26 | HandleTable handle_table; | ||
| 27 | |||
| 28 | SECTION("works with empty cmdbuf") { | ||
| 29 | const u32_le input[]{ | ||
| 30 | IPC::MakeHeader(0x1234, 0, 0), | ||
| 31 | }; | ||
| 32 | |||
| 33 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 34 | |||
| 35 | REQUIRE(context.CommandBuffer()[0] == 0x12340000); | ||
| 36 | } | ||
| 37 | |||
| 38 | SECTION("translates regular params") { | ||
| 39 | const u32_le input[]{ | ||
| 40 | IPC::MakeHeader(0, 3, 0), 0x12345678, 0x21122112, 0xAABBCCDD, | ||
| 41 | }; | ||
| 42 | |||
| 43 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 44 | |||
| 45 | auto* output = context.CommandBuffer(); | ||
| 46 | REQUIRE(output[1] == 0x12345678); | ||
| 47 | REQUIRE(output[2] == 0x21122112); | ||
| 48 | REQUIRE(output[3] == 0xAABBCCDD); | ||
| 49 | } | ||
| 50 | |||
| 51 | SECTION("translates move handles") { | ||
| 52 | auto a = MakeObject(); | ||
| 53 | Handle a_handle = handle_table.Create(a).Unwrap(); | ||
| 54 | const u32_le input[]{ | ||
| 55 | IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), a_handle, | ||
| 56 | }; | ||
| 57 | |||
| 58 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 59 | |||
| 60 | auto* output = context.CommandBuffer(); | ||
| 61 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 62 | REQUIRE(handle_table.GetGeneric(a_handle) == nullptr); | ||
| 63 | } | ||
| 64 | |||
| 65 | SECTION("translates copy handles") { | ||
| 66 | auto a = MakeObject(); | ||
| 67 | Handle a_handle = handle_table.Create(a).Unwrap(); | ||
| 68 | const u32_le input[]{ | ||
| 69 | IPC::MakeHeader(0, 0, 2), IPC::CopyHandleDesc(1), a_handle, | ||
| 70 | }; | ||
| 71 | |||
| 72 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 73 | |||
| 74 | auto* output = context.CommandBuffer(); | ||
| 75 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 76 | REQUIRE(handle_table.GetGeneric(a_handle) == a); | ||
| 77 | } | ||
| 78 | |||
| 79 | SECTION("translates multi-handle descriptors") { | ||
| 80 | auto a = MakeObject(); | ||
| 81 | auto b = MakeObject(); | ||
| 82 | auto c = MakeObject(); | ||
| 83 | const u32_le input[]{ | ||
| 84 | IPC::MakeHeader(0, 0, 5), IPC::MoveHandleDesc(2), | ||
| 85 | handle_table.Create(a).Unwrap(), handle_table.Create(b).Unwrap(), | ||
| 86 | IPC::MoveHandleDesc(1), handle_table.Create(c).Unwrap(), | ||
| 87 | }; | ||
| 88 | |||
| 89 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 90 | |||
| 91 | auto* output = context.CommandBuffer(); | ||
| 92 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 93 | REQUIRE(context.GetIncomingHandle(output[3]) == b); | ||
| 94 | REQUIRE(context.GetIncomingHandle(output[5]) == c); | ||
| 95 | } | ||
| 96 | |||
| 97 | SECTION("translates null handles") { | ||
| 98 | const u32_le input[]{ | ||
| 99 | IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), 0, | ||
| 100 | }; | ||
| 101 | |||
| 102 | auto result = context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 103 | |||
| 104 | REQUIRE(result == RESULT_SUCCESS); | ||
| 105 | auto* output = context.CommandBuffer(); | ||
| 106 | REQUIRE(context.GetIncomingHandle(output[2]) == nullptr); | ||
| 107 | } | ||
| 108 | |||
| 109 | SECTION("translates CallingPid descriptors") { | ||
| 110 | const u32_le input[]{ | ||
| 111 | IPC::MakeHeader(0, 0, 2), IPC::CallingPidDesc(), 0x98989898, | ||
| 112 | }; | ||
| 113 | |||
| 114 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 115 | |||
| 116 | REQUIRE(context.CommandBuffer()[2] == process->process_id); | ||
| 117 | } | ||
| 118 | |||
| 119 | SECTION("translates mixed params") { | ||
| 120 | auto a = MakeObject(); | ||
| 121 | const u32_le input[]{ | ||
| 122 | IPC::MakeHeader(0, 2, 4), | ||
| 123 | 0x12345678, | ||
| 124 | 0xABCDEF00, | ||
| 125 | IPC::MoveHandleDesc(1), | ||
| 126 | handle_table.Create(a).Unwrap(), | ||
| 127 | IPC::CallingPidDesc(), | ||
| 128 | 0, | ||
| 129 | }; | ||
| 130 | |||
| 131 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 132 | |||
| 133 | auto* output = context.CommandBuffer(); | ||
| 134 | REQUIRE(output[1] == 0x12345678); | ||
| 135 | REQUIRE(output[2] == 0xABCDEF00); | ||
| 136 | REQUIRE(context.GetIncomingHandle(output[4]) == a); | ||
| 137 | REQUIRE(output[6] == process->process_id); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { | ||
| 142 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); | ||
| 143 | HLERequestContext context(std::move(session)); | ||
| 144 | |||
| 145 | auto process = Process::Create(CodeSet::Create("", 0)); | ||
| 146 | HandleTable handle_table; | ||
| 147 | auto* input = context.CommandBuffer(); | ||
| 148 | u32_le output[IPC::COMMAND_BUFFER_LENGTH]; | ||
| 149 | |||
| 150 | SECTION("works with empty cmdbuf") { | ||
| 151 | input[0] = IPC::MakeHeader(0x1234, 0, 0); | ||
| 152 | |||
| 153 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 154 | |||
| 155 | REQUIRE(output[0] == 0x12340000); | ||
| 156 | } | ||
| 157 | |||
| 158 | SECTION("translates regular params") { | ||
| 159 | input[0] = IPC::MakeHeader(0, 3, 0); | ||
| 160 | input[1] = 0x12345678; | ||
| 161 | input[2] = 0x21122112; | ||
| 162 | input[3] = 0xAABBCCDD; | ||
| 163 | |||
| 164 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 165 | |||
| 166 | REQUIRE(output[1] == 0x12345678); | ||
| 167 | REQUIRE(output[2] == 0x21122112); | ||
| 168 | REQUIRE(output[3] == 0xAABBCCDD); | ||
| 169 | } | ||
| 170 | |||
| 171 | SECTION("translates move/copy handles") { | ||
| 172 | auto a = MakeObject(); | ||
| 173 | auto b = MakeObject(); | ||
| 174 | input[0] = IPC::MakeHeader(0, 0, 4); | ||
| 175 | input[1] = IPC::MoveHandleDesc(1); | ||
| 176 | input[2] = context.AddOutgoingHandle(a); | ||
| 177 | input[3] = IPC::CopyHandleDesc(1); | ||
| 178 | input[4] = context.AddOutgoingHandle(b); | ||
| 179 | |||
| 180 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 181 | |||
| 182 | REQUIRE(handle_table.GetGeneric(output[2]) == a); | ||
| 183 | REQUIRE(handle_table.GetGeneric(output[4]) == b); | ||
| 184 | } | ||
| 185 | |||
| 186 | SECTION("translates null handles") { | ||
| 187 | input[0] = IPC::MakeHeader(0, 0, 2); | ||
| 188 | input[1] = IPC::MoveHandleDesc(1); | ||
| 189 | input[2] = context.AddOutgoingHandle(nullptr); | ||
| 190 | |||
| 191 | auto result = context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 192 | |||
| 193 | REQUIRE(result == RESULT_SUCCESS); | ||
| 194 | REQUIRE(output[2] == 0); | ||
| 195 | } | ||
| 196 | |||
| 197 | SECTION("translates multi-handle descriptors") { | ||
| 198 | auto a = MakeObject(); | ||
| 199 | auto b = MakeObject(); | ||
| 200 | auto c = MakeObject(); | ||
| 201 | input[0] = IPC::MakeHeader(0, 0, 5); | ||
| 202 | input[1] = IPC::MoveHandleDesc(2); | ||
| 203 | input[2] = context.AddOutgoingHandle(a); | ||
| 204 | input[3] = context.AddOutgoingHandle(b); | ||
| 205 | input[4] = IPC::CopyHandleDesc(1); | ||
| 206 | input[5] = context.AddOutgoingHandle(c); | ||
| 207 | |||
| 208 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 209 | |||
| 210 | REQUIRE(handle_table.GetGeneric(output[2]) == a); | ||
| 211 | REQUIRE(handle_table.GetGeneric(output[3]) == b); | ||
| 212 | REQUIRE(handle_table.GetGeneric(output[5]) == c); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | } // namespace Kernel | ||
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h index f46db09fb..2d23d34e6 100644 --- a/src/video_core/pica_state.h +++ b/src/video_core/pica_state.h | |||
| @@ -87,12 +87,18 @@ struct State { | |||
| 87 | // LUT value, encoded as 12-bit fixed point, with 12 fraction bits | 87 | // LUT value, encoded as 12-bit fixed point, with 12 fraction bits |
| 88 | BitField<0, 12, u32> value; // 0.0.12 fixed point | 88 | BitField<0, 12, u32> value; // 0.0.12 fixed point |
| 89 | 89 | ||
| 90 | // Used by HW for efficient interpolation, Citra does not use these | 90 | // Used for efficient interpolation. |
| 91 | BitField<12, 12, s32> difference; // 1.0.11 fixed point | 91 | BitField<12, 11, u32> difference; // 0.0.11 fixed point |
| 92 | BitField<23, 1, u32> neg_difference; | ||
| 92 | 93 | ||
| 93 | float ToFloat() { | 94 | float ToFloat() const { |
| 94 | return static_cast<float>(value) / 4095.f; | 95 | return static_cast<float>(value) / 4095.f; |
| 95 | } | 96 | } |
| 97 | |||
| 98 | float DiffToFloat() const { | ||
| 99 | float diff = static_cast<float>(difference) / 2047.f; | ||
| 100 | return neg_difference ? -diff : diff; | ||
| 101 | } | ||
| 96 | }; | 102 | }; |
| 97 | 103 | ||
| 98 | std::array<std::array<LutEntry, 256>, 24> luts; | 104 | std::array<std::array<LutEntry, 256>, 24> luts; |
| @@ -105,6 +111,14 @@ struct State { | |||
| 105 | 111 | ||
| 106 | BitField<0, 13, s32> difference; // 1.1.11 fixed point | 112 | BitField<0, 13, s32> difference; // 1.1.11 fixed point |
| 107 | BitField<13, 11, u32> value; // 0.0.11 fixed point | 113 | BitField<13, 11, u32> value; // 0.0.11 fixed point |
| 114 | |||
| 115 | float ToFloat() const { | ||
| 116 | return static_cast<float>(value) / 2047.0f; | ||
| 117 | } | ||
| 118 | |||
| 119 | float DiffToFloat() const { | ||
| 120 | return static_cast<float>(difference) / 2047.0f; | ||
| 121 | } | ||
| 108 | }; | 122 | }; |
| 109 | 123 | ||
| 110 | std::array<LutEntry, 128> lut; | 124 | std::array<LutEntry, 128> lut; |
diff --git a/src/video_core/regs_lighting.h b/src/video_core/regs_lighting.h index fbfebc0a7..b89709cfe 100644 --- a/src/video_core/regs_lighting.h +++ b/src/video_core/regs_lighting.h | |||
| @@ -26,6 +26,8 @@ struct LightingRegs { | |||
| 26 | DistanceAttenuation = 16, | 26 | DistanceAttenuation = 16, |
| 27 | }; | 27 | }; |
| 28 | 28 | ||
| 29 | static constexpr unsigned NumLightingSampler = 24; | ||
| 30 | |||
| 29 | static LightingSampler SpotlightAttenuationSampler(unsigned index) { | 31 | static LightingSampler SpotlightAttenuationSampler(unsigned index) { |
| 30 | return static_cast<LightingSampler>( | 32 | return static_cast<LightingSampler>( |
| 31 | static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index); | 33 | static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index); |
| @@ -84,7 +86,7 @@ struct LightingRegs { | |||
| 84 | NV = 2, // Cosine of the angle between the normal and the view vector | 86 | NV = 2, // Cosine of the angle between the normal and the view vector |
| 85 | LN = 3, // Cosine of the angle between the light and the normal vectors | 87 | LN = 3, // Cosine of the angle between the light and the normal vectors |
| 86 | SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors | 88 | SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors |
| 87 | CP = 5, // TODO: document and implement | 89 | CP = 5, // Cosine of the angle between the tangent and projection of half-angle vectors |
| 88 | }; | 90 | }; |
| 89 | 91 | ||
| 90 | enum class LightingBumpMode : u32 { | 92 | enum class LightingBumpMode : u32 { |
| @@ -168,6 +170,8 @@ struct LightingRegs { | |||
| 168 | union { | 170 | union { |
| 169 | BitField<0, 1, u32> directional; | 171 | BitField<0, 1, u32> directional; |
| 170 | BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 | 172 | BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 |
| 173 | BitField<2, 1, u32> geometric_factor_0; | ||
| 174 | BitField<3, 1, u32> geometric_factor_1; | ||
| 171 | } config; | 175 | } config; |
| 172 | 176 | ||
| 173 | BitField<0, 20, u32> dist_atten_bias; | 177 | BitField<0, 20, u32> dist_atten_bias; |
diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h index 3f5355fa9..0b09f2299 100644 --- a/src/video_core/regs_texturing.h +++ b/src/video_core/regs_texturing.h | |||
| @@ -30,10 +30,10 @@ struct TexturingRegs { | |||
| 30 | Repeat = 2, | 30 | Repeat = 2, |
| 31 | MirroredRepeat = 3, | 31 | MirroredRepeat = 3, |
| 32 | // Mode 4-7 produces some weird result and may be just invalid: | 32 | // Mode 4-7 produces some weird result and may be just invalid: |
| 33 | // 4: Positive coord: clamp to edge; negative coord: repeat | 33 | ClampToEdge2 = 4, // Positive coord: clamp to edge; negative coord: repeat |
| 34 | // 5: Positive coord: clamp to border; negative coord: repeat | 34 | ClampToBorder2 = 5, // Positive coord: clamp to border; negative coord: repeat |
| 35 | // 6: Repeat | 35 | Repeat2 = 6, // Same as Repeat |
| 36 | // 7: Repeat | 36 | Repeat3 = 7, // Same as Repeat |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | enum TextureFilter : u32 { | 39 | enum TextureFilter : u32 { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index e6cccebf6..ff3f69ba3 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -49,9 +49,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 49 | 49 | ||
| 50 | uniform_block_data.dirty = true; | 50 | uniform_block_data.dirty = true; |
| 51 | 51 | ||
| 52 | for (unsigned index = 0; index < lighting_luts.size(); index++) { | 52 | uniform_block_data.lut_dirty.fill(true); |
| 53 | uniform_block_data.lut_dirty[index] = true; | ||
| 54 | } | ||
| 55 | 53 | ||
| 56 | uniform_block_data.fog_lut_dirty = true; | 54 | uniform_block_data.fog_lut_dirty = true; |
| 57 | 55 | ||
| @@ -96,36 +94,32 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 96 | framebuffer.Create(); | 94 | framebuffer.Create(); |
| 97 | 95 | ||
| 98 | // Allocate and bind lighting lut textures | 96 | // Allocate and bind lighting lut textures |
| 99 | for (size_t i = 0; i < lighting_luts.size(); ++i) { | 97 | lighting_lut.Create(); |
| 100 | lighting_luts[i].Create(); | 98 | state.lighting_lut.texture_buffer = lighting_lut.handle; |
| 101 | state.lighting_luts[i].texture_1d = lighting_luts[i].handle; | ||
| 102 | } | ||
| 103 | state.Apply(); | 99 | state.Apply(); |
| 104 | 100 | lighting_lut_buffer.Create(); | |
| 105 | for (size_t i = 0; i < lighting_luts.size(); ++i) { | 101 | glBindBuffer(GL_TEXTURE_BUFFER, lighting_lut_buffer.handle); |
| 106 | glActiveTexture(static_cast<GLenum>(GL_TEXTURE3 + i)); | 102 | glBufferData(GL_TEXTURE_BUFFER, |
| 107 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); | 103 | sizeof(GLfloat) * 2 * 256 * Pica::LightingRegs::NumLightingSampler, nullptr, |
| 108 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 104 | GL_DYNAMIC_DRAW); |
| 109 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 105 | glActiveTexture(TextureUnits::LightingLUT.Enum()); |
| 110 | } | 106 | glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, lighting_lut_buffer.handle); |
| 111 | 107 | ||
| 112 | // Setup the LUT for the fog | 108 | // Setup the LUT for the fog |
| 113 | { | 109 | fog_lut.Create(); |
| 114 | fog_lut.Create(); | 110 | state.fog_lut.texture_buffer = fog_lut.handle; |
| 115 | state.fog_lut.texture_1d = fog_lut.handle; | ||
| 116 | } | ||
| 117 | state.Apply(); | 111 | state.Apply(); |
| 118 | 112 | fog_lut_buffer.Create(); | |
| 119 | glActiveTexture(GL_TEXTURE9); | 113 | glBindBuffer(GL_TEXTURE_BUFFER, fog_lut_buffer.handle); |
| 120 | glTexImage1D(GL_TEXTURE_1D, 0, GL_R32UI, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr); | 114 | glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 2 * 128, nullptr, GL_DYNAMIC_DRAW); |
| 121 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 115 | glActiveTexture(TextureUnits::FogLUT.Enum()); |
| 122 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 116 | glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, fog_lut_buffer.handle); |
| 123 | 117 | ||
| 124 | // Setup the noise LUT for proctex | 118 | // Setup the noise LUT for proctex |
| 125 | proctex_noise_lut.Create(); | 119 | proctex_noise_lut.Create(); |
| 126 | state.proctex_noise_lut.texture_1d = proctex_noise_lut.handle; | 120 | state.proctex_noise_lut.texture_1d = proctex_noise_lut.handle; |
| 127 | state.Apply(); | 121 | state.Apply(); |
| 128 | glActiveTexture(GL_TEXTURE10); | 122 | glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum()); |
| 129 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); | 123 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); |
| 130 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 124 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 131 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 125 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| @@ -134,7 +128,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 134 | proctex_color_map.Create(); | 128 | proctex_color_map.Create(); |
| 135 | state.proctex_color_map.texture_1d = proctex_color_map.handle; | 129 | state.proctex_color_map.texture_1d = proctex_color_map.handle; |
| 136 | state.Apply(); | 130 | state.Apply(); |
| 137 | glActiveTexture(GL_TEXTURE11); | 131 | glActiveTexture(TextureUnits::ProcTexColorMap.Enum()); |
| 138 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); | 132 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); |
| 139 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 133 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 140 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 134 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| @@ -143,7 +137,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 143 | proctex_alpha_map.Create(); | 137 | proctex_alpha_map.Create(); |
| 144 | state.proctex_alpha_map.texture_1d = proctex_alpha_map.handle; | 138 | state.proctex_alpha_map.texture_1d = proctex_alpha_map.handle; |
| 145 | state.Apply(); | 139 | state.Apply(); |
| 146 | glActiveTexture(GL_TEXTURE12); | 140 | glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum()); |
| 147 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); | 141 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr); |
| 148 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 142 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 149 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 143 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| @@ -152,7 +146,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 152 | proctex_lut.Create(); | 146 | proctex_lut.Create(); |
| 153 | state.proctex_lut.texture_1d = proctex_lut.handle; | 147 | state.proctex_lut.texture_1d = proctex_lut.handle; |
| 154 | state.Apply(); | 148 | state.Apply(); |
| 155 | glActiveTexture(GL_TEXTURE13); | 149 | glActiveTexture(TextureUnits::ProcTexLUT.Enum()); |
| 156 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); | 150 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); |
| 157 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 151 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 158 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 152 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| @@ -161,7 +155,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 161 | proctex_diff_lut.Create(); | 155 | proctex_diff_lut.Create(); |
| 162 | state.proctex_diff_lut.texture_1d = proctex_diff_lut.handle; | 156 | state.proctex_diff_lut.texture_1d = proctex_diff_lut.handle; |
| 163 | state.Apply(); | 157 | state.Apply(); |
| 164 | glActiveTexture(GL_TEXTURE14); | 158 | glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum()); |
| 165 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); | 159 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); |
| 166 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 160 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 167 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 161 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| @@ -313,7 +307,7 @@ void RasterizerOpenGL::DrawTriangles() { | |||
| 313 | } | 307 | } |
| 314 | 308 | ||
| 315 | // Sync the lighting luts | 309 | // Sync the lighting luts |
| 316 | for (unsigned index = 0; index < lighting_luts.size(); index++) { | 310 | for (unsigned index = 0; index < uniform_block_data.lut_dirty.size(); index++) { |
| 317 | if (uniform_block_data.lut_dirty[index]) { | 311 | if (uniform_block_data.lut_dirty[index]) { |
| 318 | SyncLightingLUT(index); | 312 | SyncLightingLUT(index); |
| 319 | uniform_block_data.lut_dirty[index] = false; | 313 | uniform_block_data.lut_dirty[index] = false; |
| @@ -851,7 +845,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | |||
| 851 | case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce): | 845 | case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce): |
| 852 | case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf): { | 846 | case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf): { |
| 853 | auto& lut_config = regs.lighting.lut_config; | 847 | auto& lut_config = regs.lighting.lut_config; |
| 854 | uniform_block_data.lut_dirty[lut_config.type / 4] = true; | 848 | uniform_block_data.lut_dirty[lut_config.type] = true; |
| 855 | break; | 849 | break; |
| 856 | } | 850 | } |
| 857 | } | 851 | } |
| @@ -1187,77 +1181,57 @@ void RasterizerOpenGL::SetShader() { | |||
| 1187 | state.Apply(); | 1181 | state.Apply(); |
| 1188 | 1182 | ||
| 1189 | // Set the texture samplers to correspond to different texture units | 1183 | // Set the texture samplers to correspond to different texture units |
| 1190 | GLuint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]"); | 1184 | GLint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]"); |
| 1191 | if (uniform_tex != -1) { | 1185 | if (uniform_tex != -1) { |
| 1192 | glUniform1i(uniform_tex, 0); | 1186 | glUniform1i(uniform_tex, TextureUnits::PicaTexture(0).id); |
| 1193 | } | 1187 | } |
| 1194 | uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[1]"); | 1188 | uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[1]"); |
| 1195 | if (uniform_tex != -1) { | 1189 | if (uniform_tex != -1) { |
| 1196 | glUniform1i(uniform_tex, 1); | 1190 | glUniform1i(uniform_tex, TextureUnits::PicaTexture(1).id); |
| 1197 | } | 1191 | } |
| 1198 | uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]"); | 1192 | uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]"); |
| 1199 | if (uniform_tex != -1) { | 1193 | if (uniform_tex != -1) { |
| 1200 | glUniform1i(uniform_tex, 2); | 1194 | glUniform1i(uniform_tex, TextureUnits::PicaTexture(2).id); |
| 1201 | } | 1195 | } |
| 1202 | 1196 | ||
| 1203 | // Set the texture samplers to correspond to different lookup table texture units | 1197 | // Set the texture samplers to correspond to different lookup table texture units |
| 1204 | GLuint uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[0]"); | 1198 | GLint uniform_lut = glGetUniformLocation(shader->shader.handle, "lighting_lut"); |
| 1205 | if (uniform_lut != -1) { | ||
| 1206 | glUniform1i(uniform_lut, 3); | ||
| 1207 | } | ||
| 1208 | uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[1]"); | ||
| 1209 | if (uniform_lut != -1) { | ||
| 1210 | glUniform1i(uniform_lut, 4); | ||
| 1211 | } | ||
| 1212 | uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[2]"); | ||
| 1213 | if (uniform_lut != -1) { | ||
| 1214 | glUniform1i(uniform_lut, 5); | ||
| 1215 | } | ||
| 1216 | uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[3]"); | ||
| 1217 | if (uniform_lut != -1) { | ||
| 1218 | glUniform1i(uniform_lut, 6); | ||
| 1219 | } | ||
| 1220 | uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[4]"); | ||
| 1221 | if (uniform_lut != -1) { | 1199 | if (uniform_lut != -1) { |
| 1222 | glUniform1i(uniform_lut, 7); | 1200 | glUniform1i(uniform_lut, TextureUnits::LightingLUT.id); |
| 1223 | } | ||
| 1224 | uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]"); | ||
| 1225 | if (uniform_lut != -1) { | ||
| 1226 | glUniform1i(uniform_lut, 8); | ||
| 1227 | } | 1201 | } |
| 1228 | 1202 | ||
| 1229 | GLuint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut"); | 1203 | GLint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut"); |
| 1230 | if (uniform_fog_lut != -1) { | 1204 | if (uniform_fog_lut != -1) { |
| 1231 | glUniform1i(uniform_fog_lut, 9); | 1205 | glUniform1i(uniform_fog_lut, TextureUnits::FogLUT.id); |
| 1232 | } | 1206 | } |
| 1233 | 1207 | ||
| 1234 | GLuint uniform_proctex_noise_lut = | 1208 | GLint uniform_proctex_noise_lut = |
| 1235 | glGetUniformLocation(shader->shader.handle, "proctex_noise_lut"); | 1209 | glGetUniformLocation(shader->shader.handle, "proctex_noise_lut"); |
| 1236 | if (uniform_proctex_noise_lut != -1) { | 1210 | if (uniform_proctex_noise_lut != -1) { |
| 1237 | glUniform1i(uniform_proctex_noise_lut, 10); | 1211 | glUniform1i(uniform_proctex_noise_lut, TextureUnits::ProcTexNoiseLUT.id); |
| 1238 | } | 1212 | } |
| 1239 | 1213 | ||
| 1240 | GLuint uniform_proctex_color_map = | 1214 | GLint uniform_proctex_color_map = |
| 1241 | glGetUniformLocation(shader->shader.handle, "proctex_color_map"); | 1215 | glGetUniformLocation(shader->shader.handle, "proctex_color_map"); |
| 1242 | if (uniform_proctex_color_map != -1) { | 1216 | if (uniform_proctex_color_map != -1) { |
| 1243 | glUniform1i(uniform_proctex_color_map, 11); | 1217 | glUniform1i(uniform_proctex_color_map, TextureUnits::ProcTexColorMap.id); |
| 1244 | } | 1218 | } |
| 1245 | 1219 | ||
| 1246 | GLuint uniform_proctex_alpha_map = | 1220 | GLint uniform_proctex_alpha_map = |
| 1247 | glGetUniformLocation(shader->shader.handle, "proctex_alpha_map"); | 1221 | glGetUniformLocation(shader->shader.handle, "proctex_alpha_map"); |
| 1248 | if (uniform_proctex_alpha_map != -1) { | 1222 | if (uniform_proctex_alpha_map != -1) { |
| 1249 | glUniform1i(uniform_proctex_alpha_map, 12); | 1223 | glUniform1i(uniform_proctex_alpha_map, TextureUnits::ProcTexAlphaMap.id); |
| 1250 | } | 1224 | } |
| 1251 | 1225 | ||
| 1252 | GLuint uniform_proctex_lut = glGetUniformLocation(shader->shader.handle, "proctex_lut"); | 1226 | GLint uniform_proctex_lut = glGetUniformLocation(shader->shader.handle, "proctex_lut"); |
| 1253 | if (uniform_proctex_lut != -1) { | 1227 | if (uniform_proctex_lut != -1) { |
| 1254 | glUniform1i(uniform_proctex_lut, 13); | 1228 | glUniform1i(uniform_proctex_lut, TextureUnits::ProcTexLUT.id); |
| 1255 | } | 1229 | } |
| 1256 | 1230 | ||
| 1257 | GLuint uniform_proctex_diff_lut = | 1231 | GLint uniform_proctex_diff_lut = |
| 1258 | glGetUniformLocation(shader->shader.handle, "proctex_diff_lut"); | 1232 | glGetUniformLocation(shader->shader.handle, "proctex_diff_lut"); |
| 1259 | if (uniform_proctex_diff_lut != -1) { | 1233 | if (uniform_proctex_diff_lut != -1) { |
| 1260 | glUniform1i(uniform_proctex_diff_lut, 14); | 1234 | glUniform1i(uniform_proctex_diff_lut, TextureUnits::ProcTexDiffLUT.id); |
| 1261 | } | 1235 | } |
| 1262 | 1236 | ||
| 1263 | current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); | 1237 | current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); |
| @@ -1380,16 +1354,17 @@ void RasterizerOpenGL::SyncFogColor() { | |||
| 1380 | } | 1354 | } |
| 1381 | 1355 | ||
| 1382 | void RasterizerOpenGL::SyncFogLUT() { | 1356 | void RasterizerOpenGL::SyncFogLUT() { |
| 1383 | std::array<GLuint, 128> new_data; | 1357 | std::array<GLvec2, 128> new_data; |
| 1384 | 1358 | ||
| 1385 | std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), | 1359 | std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), |
| 1386 | [](const auto& entry) { return entry.raw; }); | 1360 | [](const auto& entry) { |
| 1361 | return GLvec2{entry.ToFloat(), entry.DiffToFloat()}; | ||
| 1362 | }); | ||
| 1387 | 1363 | ||
| 1388 | if (new_data != fog_lut_data) { | 1364 | if (new_data != fog_lut_data) { |
| 1389 | fog_lut_data = new_data; | 1365 | fog_lut_data = new_data; |
| 1390 | glActiveTexture(GL_TEXTURE9); | 1366 | glBindBuffer(GL_TEXTURE_BUFFER, fog_lut_buffer.handle); |
| 1391 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 128, GL_RED_INTEGER, GL_UNSIGNED_INT, | 1367 | glBufferSubData(GL_TEXTURE_BUFFER, 0, new_data.size() * sizeof(GLvec2), new_data.data()); |
| 1392 | fog_lut_data.data()); | ||
| 1393 | } | 1368 | } |
| 1394 | } | 1369 | } |
| 1395 | 1370 | ||
| @@ -1426,17 +1401,18 @@ static void SyncProcTexValueLUT(const std::array<Pica::State::ProcTex::ValueEntr | |||
| 1426 | } | 1401 | } |
| 1427 | 1402 | ||
| 1428 | void RasterizerOpenGL::SyncProcTexNoiseLUT() { | 1403 | void RasterizerOpenGL::SyncProcTexNoiseLUT() { |
| 1429 | SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, GL_TEXTURE10); | 1404 | SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, |
| 1405 | TextureUnits::ProcTexNoiseLUT.Enum()); | ||
| 1430 | } | 1406 | } |
| 1431 | 1407 | ||
| 1432 | void RasterizerOpenGL::SyncProcTexColorMap() { | 1408 | void RasterizerOpenGL::SyncProcTexColorMap() { |
| 1433 | SyncProcTexValueLUT(Pica::g_state.proctex.color_map_table, proctex_color_map_data, | 1409 | SyncProcTexValueLUT(Pica::g_state.proctex.color_map_table, proctex_color_map_data, |
| 1434 | GL_TEXTURE11); | 1410 | TextureUnits::ProcTexColorMap.Enum()); |
| 1435 | } | 1411 | } |
| 1436 | 1412 | ||
| 1437 | void RasterizerOpenGL::SyncProcTexAlphaMap() { | 1413 | void RasterizerOpenGL::SyncProcTexAlphaMap() { |
| 1438 | SyncProcTexValueLUT(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, | 1414 | SyncProcTexValueLUT(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, |
| 1439 | GL_TEXTURE12); | 1415 | TextureUnits::ProcTexAlphaMap.Enum()); |
| 1440 | } | 1416 | } |
| 1441 | 1417 | ||
| 1442 | void RasterizerOpenGL::SyncProcTexLUT() { | 1418 | void RasterizerOpenGL::SyncProcTexLUT() { |
| @@ -1451,7 +1427,7 @@ void RasterizerOpenGL::SyncProcTexLUT() { | |||
| 1451 | 1427 | ||
| 1452 | if (new_data != proctex_lut_data) { | 1428 | if (new_data != proctex_lut_data) { |
| 1453 | proctex_lut_data = new_data; | 1429 | proctex_lut_data = new_data; |
| 1454 | glActiveTexture(GL_TEXTURE13); | 1430 | glActiveTexture(TextureUnits::ProcTexLUT.Enum()); |
| 1455 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_lut_data.data()); | 1431 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_lut_data.data()); |
| 1456 | } | 1432 | } |
| 1457 | } | 1433 | } |
| @@ -1468,7 +1444,7 @@ void RasterizerOpenGL::SyncProcTexDiffLUT() { | |||
| 1468 | 1444 | ||
| 1469 | if (new_data != proctex_diff_lut_data) { | 1445 | if (new_data != proctex_diff_lut_data) { |
| 1470 | proctex_diff_lut_data = new_data; | 1446 | proctex_diff_lut_data = new_data; |
| 1471 | glActiveTexture(GL_TEXTURE14); | 1447 | glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum()); |
| 1472 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_diff_lut_data.data()); | 1448 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_diff_lut_data.data()); |
| 1473 | } | 1449 | } |
| 1474 | } | 1450 | } |
| @@ -1571,20 +1547,17 @@ void RasterizerOpenGL::SyncGlobalAmbient() { | |||
| 1571 | } | 1547 | } |
| 1572 | 1548 | ||
| 1573 | void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) { | 1549 | void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) { |
| 1574 | std::array<GLvec4, 256> new_data; | 1550 | std::array<GLvec2, 256> new_data; |
| 1575 | 1551 | const auto& source_lut = Pica::g_state.lighting.luts[lut_index]; | |
| 1576 | for (unsigned offset = 0; offset < new_data.size(); ++offset) { | 1552 | std::transform(source_lut.begin(), source_lut.end(), new_data.begin(), [](const auto& entry) { |
| 1577 | new_data[offset][0] = Pica::g_state.lighting.luts[(lut_index * 4) + 0][offset].ToFloat(); | 1553 | return GLvec2{entry.ToFloat(), entry.DiffToFloat()}; |
| 1578 | new_data[offset][1] = Pica::g_state.lighting.luts[(lut_index * 4) + 1][offset].ToFloat(); | 1554 | }); |
| 1579 | new_data[offset][2] = Pica::g_state.lighting.luts[(lut_index * 4) + 2][offset].ToFloat(); | ||
| 1580 | new_data[offset][3] = Pica::g_state.lighting.luts[(lut_index * 4) + 3][offset].ToFloat(); | ||
| 1581 | } | ||
| 1582 | 1555 | ||
| 1583 | if (new_data != lighting_lut_data[lut_index]) { | 1556 | if (new_data != lighting_lut_data[lut_index]) { |
| 1584 | lighting_lut_data[lut_index] = new_data; | 1557 | lighting_lut_data[lut_index] = new_data; |
| 1585 | glActiveTexture(GL_TEXTURE3 + lut_index); | 1558 | glBindBuffer(GL_TEXTURE_BUFFER, lighting_lut_buffer.handle); |
| 1586 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, | 1559 | glBufferSubData(GL_TEXTURE_BUFFER, lut_index * new_data.size() * sizeof(GLvec2), |
| 1587 | lighting_lut_data[lut_index].data()); | 1560 | new_data.size() * sizeof(GLvec2), new_data.data()); |
| 1588 | } | 1561 | } |
| 1589 | } | 1562 | } |
| 1590 | 1563 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d9a3e9d1c..a433c1d4a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -263,7 +263,7 @@ private: | |||
| 263 | 263 | ||
| 264 | struct { | 264 | struct { |
| 265 | UniformData data; | 265 | UniformData data; |
| 266 | bool lut_dirty[6]; | 266 | std::array<bool, Pica::LightingRegs::NumLightingSampler> lut_dirty; |
| 267 | bool fog_lut_dirty; | 267 | bool fog_lut_dirty; |
| 268 | bool proctex_noise_lut_dirty; | 268 | bool proctex_noise_lut_dirty; |
| 269 | bool proctex_color_map_dirty; | 269 | bool proctex_color_map_dirty; |
| @@ -279,11 +279,13 @@ private: | |||
| 279 | OGLBuffer uniform_buffer; | 279 | OGLBuffer uniform_buffer; |
| 280 | OGLFramebuffer framebuffer; | 280 | OGLFramebuffer framebuffer; |
| 281 | 281 | ||
| 282 | std::array<OGLTexture, 6> lighting_luts; | 282 | OGLBuffer lighting_lut_buffer; |
| 283 | std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{}; | 283 | OGLTexture lighting_lut; |
| 284 | std::array<std::array<GLvec2, 256>, Pica::LightingRegs::NumLightingSampler> lighting_lut_data{}; | ||
| 284 | 285 | ||
| 286 | OGLBuffer fog_lut_buffer; | ||
| 285 | OGLTexture fog_lut; | 287 | OGLTexture fog_lut; |
| 286 | std::array<GLuint, 128> fog_lut_data{}; | 288 | std::array<GLvec2, 128> fog_lut_data{}; |
| 287 | 289 | ||
| 288 | OGLTexture proctex_noise_lut; | 290 | OGLTexture proctex_noise_lut; |
| 289 | std::array<GLvec2, 128> proctex_noise_lut_data{}; | 291 | std::array<GLvec2, 128> proctex_noise_lut_data{}; |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index db53710aa..c93b108fb 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -73,6 +73,8 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { | |||
| 73 | state.lighting.light[light_index].num = num; | 73 | state.lighting.light[light_index].num = num; |
| 74 | state.lighting.light[light_index].directional = light.config.directional != 0; | 74 | state.lighting.light[light_index].directional = light.config.directional != 0; |
| 75 | state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; | 75 | state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; |
| 76 | state.lighting.light[light_index].geometric_factor_0 = light.config.geometric_factor_0 != 0; | ||
| 77 | state.lighting.light[light_index].geometric_factor_1 = light.config.geometric_factor_1 != 0; | ||
| 76 | state.lighting.light[light_index].dist_atten_enable = | 78 | state.lighting.light[light_index].dist_atten_enable = |
| 77 | !regs.lighting.IsDistAttenDisabled(num); | 79 | !regs.lighting.IsDistAttenDisabled(num); |
| 78 | state.lighting.light[light_index].spot_atten_enable = | 80 | state.lighting.light[light_index].spot_atten_enable = |
| @@ -518,14 +520,16 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 518 | "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" | 520 | "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" |
| 519 | "vec3 light_vector = vec3(0.0);\n" | 521 | "vec3 light_vector = vec3(0.0);\n" |
| 520 | "vec3 refl_value = vec3(0.0);\n" | 522 | "vec3 refl_value = vec3(0.0);\n" |
| 521 | "vec3 spot_dir = vec3(0.0);\n;"; | 523 | "vec3 spot_dir = vec3(0.0);\n" |
| 524 | "vec3 half_vector = vec3(0.0);\n" | ||
| 525 | "float geo_factor = 1.0;\n"; | ||
| 522 | 526 | ||
| 523 | // Compute fragment normals | 527 | // Compute fragment normals and tangents |
| 528 | const std::string pertubation = | ||
| 529 | "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; | ||
| 524 | if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { | 530 | if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { |
| 525 | // Bump mapping is enabled using a normal map, read perturbation vector from the selected | 531 | // Bump mapping is enabled using a normal map |
| 526 | // texture | 532 | out += "vec3 surface_normal = " + pertubation + ";\n"; |
| 527 | out += "vec3 surface_normal = 2.0 * (" + SampleTexture(config, lighting.bump_selector) + | ||
| 528 | ").rgb - 1.0;\n"; | ||
| 529 | 533 | ||
| 530 | // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher | 534 | // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher |
| 531 | // precision result | 535 | // precision result |
| @@ -534,31 +538,41 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 534 | "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; | 538 | "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; |
| 535 | out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; | 539 | out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; |
| 536 | } | 540 | } |
| 541 | |||
| 542 | // The tangent vector is not perturbed by the normal map and is just a unit vector. | ||
| 543 | out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; | ||
| 537 | } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { | 544 | } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { |
| 538 | // Bump mapping is enabled using a tangent map | 545 | // Bump mapping is enabled using a tangent map |
| 539 | LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)"); | 546 | out += "vec3 surface_tangent = " + pertubation + ";\n"; |
| 540 | UNIMPLEMENTED(); | 547 | // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant |
| 548 | // computation below, which is also confirmed on 3DS. So we don't bother recomputing here | ||
| 549 | // even if 'renorm' is enabled. | ||
| 550 | |||
| 551 | // The normal vector is not perturbed by the tangent map and is just a unit vector. | ||
| 552 | out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; | ||
| 541 | } else { | 553 | } else { |
| 542 | // No bump mapping - surface local normal is just a unit normal | 554 | // No bump mapping - surface local normal and tangent are just unit vectors |
| 543 | out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; | 555 | out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; |
| 556 | out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; | ||
| 544 | } | 557 | } |
| 545 | 558 | ||
| 546 | // Rotate the surface-local normal by the interpolated normal quaternion to convert it to | 559 | // Rotate the surface-local normal by the interpolated normal quaternion to convert it to |
| 547 | // eyespace. | 560 | // eyespace. |
| 548 | out += "vec3 normal = quaternion_rotate(normalize(normquat), surface_normal);\n"; | 561 | out += "vec4 normalized_normquat = normalize(normquat);\n"; |
| 562 | out += "vec3 normal = quaternion_rotate(normalized_normquat, surface_normal);\n"; | ||
| 563 | out += "vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n"; | ||
| 549 | 564 | ||
| 550 | // Gets the index into the specified lookup table for specular lighting | 565 | // Samples the specified lookup table for specular lighting |
| 551 | auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input, | 566 | auto GetLutValue = [&lighting](LightingRegs::LightingSampler sampler, unsigned light_num, |
| 552 | bool abs) { | 567 | LightingRegs::LightingLutInput input, bool abs) { |
| 553 | const std::string half_angle = "normalize(normalize(view) + light_vector)"; | ||
| 554 | std::string index; | 568 | std::string index; |
| 555 | switch (input) { | 569 | switch (input) { |
| 556 | case LightingRegs::LightingLutInput::NH: | 570 | case LightingRegs::LightingLutInput::NH: |
| 557 | index = "dot(normal, " + half_angle + ")"; | 571 | index = "dot(normal, normalize(half_vector))"; |
| 558 | break; | 572 | break; |
| 559 | 573 | ||
| 560 | case LightingRegs::LightingLutInput::VH: | 574 | case LightingRegs::LightingLutInput::VH: |
| 561 | index = std::string("dot(normalize(view), " + half_angle + ")"); | 575 | index = std::string("dot(normalize(view), normalize(half_vector))"); |
| 562 | break; | 576 | break; |
| 563 | 577 | ||
| 564 | case LightingRegs::LightingLutInput::NV: | 578 | case LightingRegs::LightingLutInput::NV: |
| @@ -573,6 +587,22 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 573 | index = std::string("dot(light_vector, spot_dir)"); | 587 | index = std::string("dot(light_vector, spot_dir)"); |
| 574 | break; | 588 | break; |
| 575 | 589 | ||
| 590 | case LightingRegs::LightingLutInput::CP: | ||
| 591 | // CP input is only available with configuration 7 | ||
| 592 | if (lighting.config == LightingRegs::LightingConfig::Config7) { | ||
| 593 | // Note: even if the normal vector is modified by normal map, which is not the | ||
| 594 | // normal of the tangent plane anymore, the half angle vector is still projected | ||
| 595 | // using the modified normal vector. | ||
| 596 | std::string half_angle_proj = "normalize(half_vector) - normal / dot(normal, " | ||
| 597 | "normal) * dot(normal, normalize(half_vector))"; | ||
| 598 | // Note: the half angle vector projection is confirmed not normalized before the dot | ||
| 599 | // product. The result is in fact not cos(phi) as the name suggested. | ||
| 600 | index = "dot(" + half_angle_proj + ", tangent)"; | ||
| 601 | } else { | ||
| 602 | index = "0.0"; | ||
| 603 | } | ||
| 604 | break; | ||
| 605 | |||
| 576 | default: | 606 | default: |
| 577 | LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); | 607 | LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); |
| 578 | UNIMPLEMENTED(); | 608 | UNIMPLEMENTED(); |
| @@ -580,22 +610,18 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 580 | break; | 610 | break; |
| 581 | } | 611 | } |
| 582 | 612 | ||
| 613 | std::string sampler_string = std::to_string(static_cast<unsigned>(sampler)); | ||
| 614 | |||
| 583 | if (abs) { | 615 | if (abs) { |
| 584 | // LUT index is in the range of (0.0, 1.0) | 616 | // LUT index is in the range of (0.0, 1.0) |
| 585 | index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" | 617 | index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" |
| 586 | : "max(" + index + ", 0.0)"; | 618 | : "max(" + index + ", 0.0)"; |
| 619 | return "LookupLightingLUTUnsigned(" + sampler_string + ", " + index + ")"; | ||
| 587 | } else { | 620 | } else { |
| 588 | // LUT index is in the range of (-1.0, 1.0) | 621 | // LUT index is in the range of (-1.0, 1.0) |
| 589 | index = "((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0"; | 622 | return "LookupLightingLUTSigned(" + sampler_string + ", " + index + ")"; |
| 590 | } | 623 | } |
| 591 | 624 | ||
| 592 | return "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; | ||
| 593 | }; | ||
| 594 | |||
| 595 | // Gets the lighting lookup table value given the specified sampler and index | ||
| 596 | auto GetLutValue = [](LightingRegs::LightingSampler sampler, std::string lut_index) { | ||
| 597 | return std::string("texture(lut[" + std::to_string((unsigned)sampler / 4) + "], " + | ||
| 598 | lut_index + ")[" + std::to_string((unsigned)sampler & 3) + "]"); | ||
| 599 | }; | 625 | }; |
| 600 | 626 | ||
| 601 | // Write the code to emulate each enabled light | 627 | // Write the code to emulate each enabled light |
| @@ -610,6 +636,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 610 | out += "light_vector = normalize(" + light_src + ".position + view);\n"; | 636 | out += "light_vector = normalize(" + light_src + ".position + view);\n"; |
| 611 | 637 | ||
| 612 | out += "spot_dir = " + light_src + ".spot_direction;\n"; | 638 | out += "spot_dir = " + light_src + ".spot_direction;\n"; |
| 639 | out += "half_vector = normalize(view) + light_vector;\n"; | ||
| 613 | 640 | ||
| 614 | // Compute dot product of light_vector and normal, adjust if lighting is one-sided or | 641 | // Compute dot product of light_vector and normal, adjust if lighting is one-sided or |
| 615 | // two-sided | 642 | // two-sided |
| @@ -622,48 +649,57 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 622 | if (light_config.spot_atten_enable && | 649 | if (light_config.spot_atten_enable && |
| 623 | LightingRegs::IsLightingSamplerSupported( | 650 | LightingRegs::IsLightingSamplerSupported( |
| 624 | lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { | 651 | lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { |
| 625 | std::string index = | 652 | std::string value = |
| 626 | GetLutIndex(light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); | 653 | GetLutValue(LightingRegs::SpotlightAttenuationSampler(light_config.num), |
| 627 | auto sampler = LightingRegs::SpotlightAttenuationSampler(light_config.num); | 654 | light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); |
| 628 | spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + | 655 | spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + value + ")"; |
| 629 | GetLutValue(sampler, index) + ")"; | ||
| 630 | } | 656 | } |
| 631 | 657 | ||
| 632 | // If enabled, compute distance attenuation value | 658 | // If enabled, compute distance attenuation value |
| 633 | std::string dist_atten = "1.0"; | 659 | std::string dist_atten = "1.0"; |
| 634 | if (light_config.dist_atten_enable) { | 660 | if (light_config.dist_atten_enable) { |
| 635 | std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + | 661 | std::string index = "clamp(" + light_src + ".dist_atten_scale * length(-view - " + |
| 636 | light_src + ".position) + " + light_src + ".dist_atten_bias)"; | 662 | light_src + ".position) + " + light_src + |
| 637 | index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; | 663 | ".dist_atten_bias, 0.0, 1.0)"; |
| 638 | auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); | 664 | auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); |
| 639 | dist_atten = GetLutValue(sampler, index); | 665 | dist_atten = "LookupLightingLUTUnsigned(" + |
| 666 | std::to_string(static_cast<unsigned>(sampler)) + "," + index + ")"; | ||
| 640 | } | 667 | } |
| 641 | 668 | ||
| 642 | // If enabled, clamp specular component if lighting result is negative | 669 | // If enabled, clamp specular component if lighting result is negative |
| 643 | std::string clamp_highlights = | 670 | std::string clamp_highlights = |
| 644 | lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; | 671 | lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; |
| 645 | 672 | ||
| 673 | if (light_config.geometric_factor_0 || light_config.geometric_factor_1) { | ||
| 674 | out += "geo_factor = dot(half_vector, half_vector);\n" | ||
| 675 | "geo_factor = geo_factor == 0.0 ? 0.0 : min(" + | ||
| 676 | dot_product + " / geo_factor, 1.0);\n"; | ||
| 677 | } | ||
| 678 | |||
| 646 | // Specular 0 component | 679 | // Specular 0 component |
| 647 | std::string d0_lut_value = "1.0"; | 680 | std::string d0_lut_value = "1.0"; |
| 648 | if (lighting.lut_d0.enable && | 681 | if (lighting.lut_d0.enable && |
| 649 | LightingRegs::IsLightingSamplerSupported( | 682 | LightingRegs::IsLightingSamplerSupported( |
| 650 | lighting.config, LightingRegs::LightingSampler::Distribution0)) { | 683 | lighting.config, LightingRegs::LightingSampler::Distribution0)) { |
| 651 | // Lookup specular "distribution 0" LUT value | 684 | // Lookup specular "distribution 0" LUT value |
| 652 | std::string index = | 685 | std::string value = |
| 653 | GetLutIndex(light_config.num, lighting.lut_d0.type, lighting.lut_d0.abs_input); | 686 | GetLutValue(LightingRegs::LightingSampler::Distribution0, light_config.num, |
| 654 | d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + | 687 | lighting.lut_d0.type, lighting.lut_d0.abs_input); |
| 655 | GetLutValue(LightingRegs::LightingSampler::Distribution0, index) + ")"; | 688 | d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + value + ")"; |
| 656 | } | 689 | } |
| 657 | std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; | 690 | std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; |
| 691 | if (light_config.geometric_factor_0) { | ||
| 692 | specular_0 = "(" + specular_0 + " * geo_factor)"; | ||
| 693 | } | ||
| 658 | 694 | ||
| 659 | // If enabled, lookup ReflectRed value, otherwise, 1.0 is used | 695 | // If enabled, lookup ReflectRed value, otherwise, 1.0 is used |
| 660 | if (lighting.lut_rr.enable && | 696 | if (lighting.lut_rr.enable && |
| 661 | LightingRegs::IsLightingSamplerSupported(lighting.config, | 697 | LightingRegs::IsLightingSamplerSupported(lighting.config, |
| 662 | LightingRegs::LightingSampler::ReflectRed)) { | 698 | LightingRegs::LightingSampler::ReflectRed)) { |
| 663 | std::string index = | 699 | std::string value = |
| 664 | GetLutIndex(light_config.num, lighting.lut_rr.type, lighting.lut_rr.abs_input); | 700 | GetLutValue(LightingRegs::LightingSampler::ReflectRed, light_config.num, |
| 665 | std::string value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + | 701 | lighting.lut_rr.type, lighting.lut_rr.abs_input); |
| 666 | GetLutValue(LightingRegs::LightingSampler::ReflectRed, index) + ")"; | 702 | value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + value + ")"; |
| 667 | out += "refl_value.r = " + value + ";\n"; | 703 | out += "refl_value.r = " + value + ";\n"; |
| 668 | } else { | 704 | } else { |
| 669 | out += "refl_value.r = 1.0;\n"; | 705 | out += "refl_value.r = 1.0;\n"; |
| @@ -673,11 +709,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 673 | if (lighting.lut_rg.enable && | 709 | if (lighting.lut_rg.enable && |
| 674 | LightingRegs::IsLightingSamplerSupported(lighting.config, | 710 | LightingRegs::IsLightingSamplerSupported(lighting.config, |
| 675 | LightingRegs::LightingSampler::ReflectGreen)) { | 711 | LightingRegs::LightingSampler::ReflectGreen)) { |
| 676 | std::string index = | 712 | std::string value = |
| 677 | GetLutIndex(light_config.num, lighting.lut_rg.type, lighting.lut_rg.abs_input); | 713 | GetLutValue(LightingRegs::LightingSampler::ReflectGreen, light_config.num, |
| 678 | std::string value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + | 714 | lighting.lut_rg.type, lighting.lut_rg.abs_input); |
| 679 | GetLutValue(LightingRegs::LightingSampler::ReflectGreen, index) + | 715 | value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + value + ")"; |
| 680 | ")"; | ||
| 681 | out += "refl_value.g = " + value + ";\n"; | 716 | out += "refl_value.g = " + value + ";\n"; |
| 682 | } else { | 717 | } else { |
| 683 | out += "refl_value.g = refl_value.r;\n"; | 718 | out += "refl_value.g = refl_value.r;\n"; |
| @@ -687,11 +722,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 687 | if (lighting.lut_rb.enable && | 722 | if (lighting.lut_rb.enable && |
| 688 | LightingRegs::IsLightingSamplerSupported(lighting.config, | 723 | LightingRegs::IsLightingSamplerSupported(lighting.config, |
| 689 | LightingRegs::LightingSampler::ReflectBlue)) { | 724 | LightingRegs::LightingSampler::ReflectBlue)) { |
| 690 | std::string index = | 725 | std::string value = |
| 691 | GetLutIndex(light_config.num, lighting.lut_rb.type, lighting.lut_rb.abs_input); | 726 | GetLutValue(LightingRegs::LightingSampler::ReflectBlue, light_config.num, |
| 692 | std::string value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + | 727 | lighting.lut_rb.type, lighting.lut_rb.abs_input); |
| 693 | GetLutValue(LightingRegs::LightingSampler::ReflectBlue, index) + | 728 | value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + value + ")"; |
| 694 | ")"; | ||
| 695 | out += "refl_value.b = " + value + ";\n"; | 729 | out += "refl_value.b = " + value + ";\n"; |
| 696 | } else { | 730 | } else { |
| 697 | out += "refl_value.b = refl_value.r;\n"; | 731 | out += "refl_value.b = refl_value.r;\n"; |
| @@ -703,23 +737,26 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { | |||
| 703 | LightingRegs::IsLightingSamplerSupported( | 737 | LightingRegs::IsLightingSamplerSupported( |
| 704 | lighting.config, LightingRegs::LightingSampler::Distribution1)) { | 738 | lighting.config, LightingRegs::LightingSampler::Distribution1)) { |
| 705 | // Lookup specular "distribution 1" LUT value | 739 | // Lookup specular "distribution 1" LUT value |
| 706 | std::string index = | 740 | std::string value = |
| 707 | GetLutIndex(light_config.num, lighting.lut_d1.type, lighting.lut_d1.abs_input); | 741 | GetLutValue(LightingRegs::LightingSampler::Distribution1, light_config.num, |
| 708 | d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + | 742 | lighting.lut_d1.type, lighting.lut_d1.abs_input); |
| 709 | GetLutValue(LightingRegs::LightingSampler::Distribution1, index) + ")"; | 743 | d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + value + ")"; |
| 710 | } | 744 | } |
| 711 | std::string specular_1 = | 745 | std::string specular_1 = |
| 712 | "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; | 746 | "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; |
| 747 | if (light_config.geometric_factor_1) { | ||
| 748 | specular_1 = "(" + specular_1 + " * geo_factor)"; | ||
| 749 | } | ||
| 713 | 750 | ||
| 714 | // Fresnel | 751 | // Fresnel |
| 715 | if (lighting.lut_fr.enable && | 752 | if (lighting.lut_fr.enable && |
| 716 | LightingRegs::IsLightingSamplerSupported(lighting.config, | 753 | LightingRegs::IsLightingSamplerSupported(lighting.config, |
| 717 | LightingRegs::LightingSampler::Fresnel)) { | 754 | LightingRegs::LightingSampler::Fresnel)) { |
| 718 | // Lookup fresnel LUT value | 755 | // Lookup fresnel LUT value |
| 719 | std::string index = | 756 | std::string value = |
| 720 | GetLutIndex(light_config.num, lighting.lut_fr.type, lighting.lut_fr.abs_input); | 757 | GetLutValue(LightingRegs::LightingSampler::Fresnel, light_config.num, |
| 721 | std::string value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + | 758 | lighting.lut_fr.type, lighting.lut_fr.abs_input); |
| 722 | GetLutValue(LightingRegs::LightingSampler::Fresnel, index) + ")"; | 759 | value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + value + ")"; |
| 723 | 760 | ||
| 724 | // Enabled for difffuse lighting alpha component | 761 | // Enabled for difffuse lighting alpha component |
| 725 | if (lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::PrimaryAlpha || | 762 | if (lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::PrimaryAlpha || |
| @@ -973,10 +1010,6 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) { | |||
| 973 | #define NUM_TEV_STAGES 6 | 1010 | #define NUM_TEV_STAGES 6 |
| 974 | #define NUM_LIGHTS 8 | 1011 | #define NUM_LIGHTS 8 |
| 975 | 1012 | ||
| 976 | // Texture coordinate offsets and scales | ||
| 977 | #define OFFSET_256 (0.5 / 256.0) | ||
| 978 | #define SCALE_256 (255.0 / 256.0) | ||
| 979 | |||
| 980 | in vec4 primary_color; | 1013 | in vec4 primary_color; |
| 981 | in vec2 texcoord[3]; | 1014 | in vec2 texcoord[3]; |
| 982 | in float texcoord0_w; | 1015 | in float texcoord0_w; |
| @@ -1018,8 +1051,8 @@ layout (std140) uniform shader_data { | |||
| 1018 | }; | 1051 | }; |
| 1019 | 1052 | ||
| 1020 | uniform sampler2D tex[3]; | 1053 | uniform sampler2D tex[3]; |
| 1021 | uniform sampler1D lut[6]; | 1054 | uniform samplerBuffer lighting_lut; |
| 1022 | uniform usampler1D fog_lut; | 1055 | uniform samplerBuffer fog_lut; |
| 1023 | uniform sampler1D proctex_noise_lut; | 1056 | uniform sampler1D proctex_noise_lut; |
| 1024 | uniform sampler1D proctex_color_map; | 1057 | uniform sampler1D proctex_color_map; |
| 1025 | uniform sampler1D proctex_alpha_map; | 1058 | uniform sampler1D proctex_alpha_map; |
| @@ -1031,6 +1064,24 @@ vec3 quaternion_rotate(vec4 q, vec3 v) { | |||
| 1031 | return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); | 1064 | return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); |
| 1032 | } | 1065 | } |
| 1033 | 1066 | ||
| 1067 | float LookupLightingLUT(int lut_index, int index, float delta) { | ||
| 1068 | vec2 entry = texelFetch(lighting_lut, lut_index * 256 + index).rg; | ||
| 1069 | return entry.r + entry.g * delta; | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | float LookupLightingLUTUnsigned(int lut_index, float pos) { | ||
| 1073 | int index = clamp(int(pos * 256.0), 0, 255); | ||
| 1074 | float delta = pos * 256.0 - index; | ||
| 1075 | return LookupLightingLUT(lut_index, index, delta); | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | float LookupLightingLUTSigned(int lut_index, float pos) { | ||
| 1079 | int index = clamp(int(pos * 128.0), -128, 127); | ||
| 1080 | float delta = pos * 128.0 - index; | ||
| 1081 | if (index < 0) index += 256; | ||
| 1082 | return LookupLightingLUT(lut_index, index, delta); | ||
| 1083 | } | ||
| 1084 | |||
| 1034 | )"; | 1085 | )"; |
| 1035 | 1086 | ||
| 1036 | if (config.state.proctex.enable) | 1087 | if (config.state.proctex.enable) |
| @@ -1094,12 +1145,8 @@ vec4 secondary_fragment_color = vec4(0.0); | |||
| 1094 | // Generate clamped fog factor from LUT for given fog index | 1145 | // Generate clamped fog factor from LUT for given fog index |
| 1095 | out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n"; | 1146 | out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n"; |
| 1096 | out += "float fog_f = fog_index - fog_i;\n"; | 1147 | out += "float fog_f = fog_index - fog_i;\n"; |
| 1097 | out += "uint fog_lut_entry = texelFetch(fog_lut, int(fog_i), 0).r;\n"; | 1148 | out += "vec2 fog_lut_entry = texelFetch(fog_lut, int(fog_i)).rg;\n"; |
| 1098 | out += "float fog_lut_entry_difference = float(int((fog_lut_entry & 0x1FFFU) << 19U) >> " | 1149 | out += "float fog_factor = fog_lut_entry.r + fog_lut_entry.g * fog_f;\n"; |
| 1099 | "19);\n"; // Extract signed difference | ||
| 1100 | out += "float fog_lut_entry_value = float((fog_lut_entry >> 13U) & 0x7FFU);\n"; | ||
| 1101 | out += "float fog_factor = (fog_lut_entry_value + fog_lut_entry_difference * fog_f) / " | ||
| 1102 | "2047.0;\n"; | ||
| 1103 | out += "fog_factor = clamp(fog_factor, 0.0, 1.0);\n"; | 1150 | out += "fog_factor = clamp(fog_factor, 0.0, 1.0);\n"; |
| 1104 | 1151 | ||
| 1105 | // Blend the fog | 1152 | // Blend the fog |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 9c90eadf9..2302ae453 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -94,6 +94,8 @@ union PicaShaderConfig { | |||
| 94 | bool two_sided_diffuse; | 94 | bool two_sided_diffuse; |
| 95 | bool dist_atten_enable; | 95 | bool dist_atten_enable; |
| 96 | bool spot_atten_enable; | 96 | bool spot_atten_enable; |
| 97 | bool geometric_factor_0; | ||
| 98 | bool geometric_factor_1; | ||
| 97 | } light[8]; | 99 | } light[8]; |
| 98 | 100 | ||
| 99 | bool enable; | 101 | bool enable; |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index bf837a7fb..eface2dea 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -52,11 +52,9 @@ OpenGLState::OpenGLState() { | |||
| 52 | texture_unit.sampler = 0; | 52 | texture_unit.sampler = 0; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | for (auto& lut : lighting_luts) { | 55 | lighting_lut.texture_buffer = 0; |
| 56 | lut.texture_1d = 0; | ||
| 57 | } | ||
| 58 | 56 | ||
| 59 | fog_lut.texture_1d = 0; | 57 | fog_lut.texture_buffer = 0; |
| 60 | 58 | ||
| 61 | proctex_lut.texture_1d = 0; | 59 | proctex_lut.texture_1d = 0; |
| 62 | proctex_diff_lut.texture_1d = 0; | 60 | proctex_diff_lut.texture_1d = 0; |
| @@ -185,7 +183,7 @@ void OpenGLState::Apply() const { | |||
| 185 | // Textures | 183 | // Textures |
| 186 | for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) { | 184 | for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) { |
| 187 | if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { | 185 | if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { |
| 188 | glActiveTexture(GL_TEXTURE0 + i); | 186 | glActiveTexture(TextureUnits::PicaTexture(i).Enum()); |
| 189 | glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d); | 187 | glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d); |
| 190 | } | 188 | } |
| 191 | if (texture_units[i].sampler != cur_state.texture_units[i].sampler) { | 189 | if (texture_units[i].sampler != cur_state.texture_units[i].sampler) { |
| @@ -194,46 +192,44 @@ void OpenGLState::Apply() const { | |||
| 194 | } | 192 | } |
| 195 | 193 | ||
| 196 | // Lighting LUTs | 194 | // Lighting LUTs |
| 197 | for (unsigned i = 0; i < ARRAY_SIZE(lighting_luts); ++i) { | 195 | if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) { |
| 198 | if (lighting_luts[i].texture_1d != cur_state.lighting_luts[i].texture_1d) { | 196 | glActiveTexture(TextureUnits::LightingLUT.Enum()); |
| 199 | glActiveTexture(GL_TEXTURE3 + i); | 197 | glBindTexture(GL_TEXTURE_BUFFER, cur_state.lighting_lut.texture_buffer); |
| 200 | glBindTexture(GL_TEXTURE_1D, lighting_luts[i].texture_1d); | ||
| 201 | } | ||
| 202 | } | 198 | } |
| 203 | 199 | ||
| 204 | // Fog LUT | 200 | // Fog LUT |
| 205 | if (fog_lut.texture_1d != cur_state.fog_lut.texture_1d) { | 201 | if (fog_lut.texture_buffer != cur_state.fog_lut.texture_buffer) { |
| 206 | glActiveTexture(GL_TEXTURE9); | 202 | glActiveTexture(TextureUnits::FogLUT.Enum()); |
| 207 | glBindTexture(GL_TEXTURE_1D, fog_lut.texture_1d); | 203 | glBindTexture(GL_TEXTURE_BUFFER, fog_lut.texture_buffer); |
| 208 | } | 204 | } |
| 209 | 205 | ||
| 210 | // ProcTex Noise LUT | 206 | // ProcTex Noise LUT |
| 211 | if (proctex_noise_lut.texture_1d != cur_state.proctex_noise_lut.texture_1d) { | 207 | if (proctex_noise_lut.texture_1d != cur_state.proctex_noise_lut.texture_1d) { |
| 212 | glActiveTexture(GL_TEXTURE10); | 208 | glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum()); |
| 213 | glBindTexture(GL_TEXTURE_1D, proctex_noise_lut.texture_1d); | 209 | glBindTexture(GL_TEXTURE_1D, proctex_noise_lut.texture_1d); |
| 214 | } | 210 | } |
| 215 | 211 | ||
| 216 | // ProcTex Color Map | 212 | // ProcTex Color Map |
| 217 | if (proctex_color_map.texture_1d != cur_state.proctex_color_map.texture_1d) { | 213 | if (proctex_color_map.texture_1d != cur_state.proctex_color_map.texture_1d) { |
| 218 | glActiveTexture(GL_TEXTURE11); | 214 | glActiveTexture(TextureUnits::ProcTexColorMap.Enum()); |
| 219 | glBindTexture(GL_TEXTURE_1D, proctex_color_map.texture_1d); | 215 | glBindTexture(GL_TEXTURE_1D, proctex_color_map.texture_1d); |
| 220 | } | 216 | } |
| 221 | 217 | ||
| 222 | // ProcTex Alpha Map | 218 | // ProcTex Alpha Map |
| 223 | if (proctex_alpha_map.texture_1d != cur_state.proctex_alpha_map.texture_1d) { | 219 | if (proctex_alpha_map.texture_1d != cur_state.proctex_alpha_map.texture_1d) { |
| 224 | glActiveTexture(GL_TEXTURE12); | 220 | glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum()); |
| 225 | glBindTexture(GL_TEXTURE_1D, proctex_alpha_map.texture_1d); | 221 | glBindTexture(GL_TEXTURE_1D, proctex_alpha_map.texture_1d); |
| 226 | } | 222 | } |
| 227 | 223 | ||
| 228 | // ProcTex LUT | 224 | // ProcTex LUT |
| 229 | if (proctex_lut.texture_1d != cur_state.proctex_lut.texture_1d) { | 225 | if (proctex_lut.texture_1d != cur_state.proctex_lut.texture_1d) { |
| 230 | glActiveTexture(GL_TEXTURE13); | 226 | glActiveTexture(TextureUnits::ProcTexLUT.Enum()); |
| 231 | glBindTexture(GL_TEXTURE_1D, proctex_lut.texture_1d); | 227 | glBindTexture(GL_TEXTURE_1D, proctex_lut.texture_1d); |
| 232 | } | 228 | } |
| 233 | 229 | ||
| 234 | // ProcTex Diff LUT | 230 | // ProcTex Diff LUT |
| 235 | if (proctex_diff_lut.texture_1d != cur_state.proctex_diff_lut.texture_1d) { | 231 | if (proctex_diff_lut.texture_1d != cur_state.proctex_diff_lut.texture_1d) { |
| 236 | glActiveTexture(GL_TEXTURE14); | 232 | glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum()); |
| 237 | glBindTexture(GL_TEXTURE_1D, proctex_diff_lut.texture_1d); | 233 | glBindTexture(GL_TEXTURE_1D, proctex_diff_lut.texture_1d); |
| 238 | } | 234 | } |
| 239 | 235 | ||
| @@ -274,6 +270,20 @@ void OpenGLState::ResetTexture(GLuint handle) { | |||
| 274 | unit.texture_2d = 0; | 270 | unit.texture_2d = 0; |
| 275 | } | 271 | } |
| 276 | } | 272 | } |
| 273 | if (cur_state.lighting_lut.texture_buffer == handle) | ||
| 274 | cur_state.lighting_lut.texture_buffer = 0; | ||
| 275 | if (cur_state.fog_lut.texture_buffer == handle) | ||
| 276 | cur_state.fog_lut.texture_buffer = 0; | ||
| 277 | if (cur_state.proctex_noise_lut.texture_1d == handle) | ||
| 278 | cur_state.proctex_noise_lut.texture_1d = 0; | ||
| 279 | if (cur_state.proctex_color_map.texture_1d == handle) | ||
| 280 | cur_state.proctex_color_map.texture_1d = 0; | ||
| 281 | if (cur_state.proctex_alpha_map.texture_1d == handle) | ||
| 282 | cur_state.proctex_alpha_map.texture_1d = 0; | ||
| 283 | if (cur_state.proctex_lut.texture_1d == handle) | ||
| 284 | cur_state.proctex_lut.texture_1d = 0; | ||
| 285 | if (cur_state.proctex_diff_lut.texture_1d == handle) | ||
| 286 | cur_state.proctex_diff_lut.texture_1d = 0; | ||
| 277 | } | 287 | } |
| 278 | 288 | ||
| 279 | void OpenGLState::ResetSampler(GLuint handle) { | 289 | void OpenGLState::ResetSampler(GLuint handle) { |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 7dcc03bd5..1efcf0811 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -6,6 +6,29 @@ | |||
| 6 | 6 | ||
| 7 | #include <glad/glad.h> | 7 | #include <glad/glad.h> |
| 8 | 8 | ||
| 9 | namespace TextureUnits { | ||
| 10 | |||
| 11 | struct TextureUnit { | ||
| 12 | GLint id; | ||
| 13 | constexpr GLenum Enum() const { | ||
| 14 | return static_cast<GLenum>(GL_TEXTURE0 + id); | ||
| 15 | } | ||
| 16 | }; | ||
| 17 | |||
| 18 | constexpr TextureUnit PicaTexture(int unit) { | ||
| 19 | return TextureUnit{unit}; | ||
| 20 | } | ||
| 21 | |||
| 22 | constexpr TextureUnit LightingLUT{3}; | ||
| 23 | constexpr TextureUnit FogLUT{4}; | ||
| 24 | constexpr TextureUnit ProcTexNoiseLUT{5}; | ||
| 25 | constexpr TextureUnit ProcTexColorMap{6}; | ||
| 26 | constexpr TextureUnit ProcTexAlphaMap{7}; | ||
| 27 | constexpr TextureUnit ProcTexLUT{8}; | ||
| 28 | constexpr TextureUnit ProcTexDiffLUT{9}; | ||
| 29 | |||
| 30 | } // namespace TextureUnits | ||
| 31 | |||
| 9 | class OpenGLState { | 32 | class OpenGLState { |
| 10 | public: | 33 | public: |
| 11 | struct { | 34 | struct { |
| @@ -64,11 +87,11 @@ public: | |||
| 64 | } texture_units[3]; | 87 | } texture_units[3]; |
| 65 | 88 | ||
| 66 | struct { | 89 | struct { |
| 67 | GLuint texture_1d; // GL_TEXTURE_BINDING_1D | 90 | GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER |
| 68 | } lighting_luts[6]; | 91 | } lighting_lut; |
| 69 | 92 | ||
| 70 | struct { | 93 | struct { |
| 71 | GLuint texture_1d; // GL_TEXTURE_BINDING_1D | 94 | GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER |
| 72 | } fog_lut; | 95 | } fog_lut; |
| 73 | 96 | ||
| 74 | struct { | 97 | struct { |
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 93d7b0b71..70298e211 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h | |||
| @@ -55,6 +55,12 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) { | |||
| 55 | GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder | 55 | GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder |
| 56 | GL_REPEAT, // WrapMode::Repeat | 56 | GL_REPEAT, // WrapMode::Repeat |
| 57 | GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat | 57 | GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat |
| 58 | // TODO(wwylele): ClampToEdge2 and ClampToBorder2 are not properly implemented here. See the | ||
| 59 | // comments in enum WrapMode. | ||
| 60 | GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge2 | ||
| 61 | GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder2 | ||
| 62 | GL_REPEAT, // WrapMode::Repeat2 | ||
| 63 | GL_REPEAT, // WrapMode::Repeat3 | ||
| 58 | }; | 64 | }; |
| 59 | 65 | ||
| 60 | // Range check table for input | 66 | // Range check table for input |
| @@ -65,6 +71,13 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) { | |||
| 65 | return GL_CLAMP_TO_EDGE; | 71 | return GL_CLAMP_TO_EDGE; |
| 66 | } | 72 | } |
| 67 | 73 | ||
| 74 | if (static_cast<u32>(mode) > 3) { | ||
| 75 | // It is still unclear whether mode 4-7 are valid, so log it if a game uses them. | ||
| 76 | // TODO(wwylele): telemetry should be added here so we can collect more info about which | ||
| 77 | // game uses this. | ||
| 78 | LOG_WARNING(Render_OpenGL, "Using texture wrap mode %u", static_cast<u32>(mode)); | ||
| 79 | } | ||
| 80 | |||
| 68 | GLenum gl_mode = wrap_mode_table[mode]; | 81 | GLenum gl_mode = wrap_mode_table[mode]; |
| 69 | 82 | ||
| 70 | // Check for dummy values indicating an unknown mode | 83 | // Check for dummy values indicating an unknown mode |
diff --git a/src/video_core/shader/shader_jit_x64_compiler.cpp b/src/video_core/shader/shader_jit_x64_compiler.cpp index 5d9b6448c..42a57aab1 100644 --- a/src/video_core/shader/shader_jit_x64_compiler.cpp +++ b/src/video_core/shader/shader_jit_x64_compiler.cpp | |||
| @@ -321,27 +321,27 @@ void JitShader::Compile_EvaluateCondition(Instruction instr) { | |||
| 321 | case Instruction::FlowControlType::Or: | 321 | case Instruction::FlowControlType::Or: |
| 322 | mov(eax, COND0); | 322 | mov(eax, COND0); |
| 323 | mov(ebx, COND1); | 323 | mov(ebx, COND1); |
| 324 | xor(eax, (instr.flow_control.refx.Value() ^ 1)); | 324 | xor_(eax, (instr.flow_control.refx.Value() ^ 1)); |
| 325 | xor(ebx, (instr.flow_control.refy.Value() ^ 1)); | 325 | xor_(ebx, (instr.flow_control.refy.Value() ^ 1)); |
| 326 | or (eax, ebx); | 326 | or_(eax, ebx); |
| 327 | break; | 327 | break; |
| 328 | 328 | ||
| 329 | case Instruction::FlowControlType::And: | 329 | case Instruction::FlowControlType::And: |
| 330 | mov(eax, COND0); | 330 | mov(eax, COND0); |
| 331 | mov(ebx, COND1); | 331 | mov(ebx, COND1); |
| 332 | xor(eax, (instr.flow_control.refx.Value() ^ 1)); | 332 | xor_(eax, (instr.flow_control.refx.Value() ^ 1)); |
| 333 | xor(ebx, (instr.flow_control.refy.Value() ^ 1)); | 333 | xor_(ebx, (instr.flow_control.refy.Value() ^ 1)); |
| 334 | and(eax, ebx); | 334 | and_(eax, ebx); |
| 335 | break; | 335 | break; |
| 336 | 336 | ||
| 337 | case Instruction::FlowControlType::JustX: | 337 | case Instruction::FlowControlType::JustX: |
| 338 | mov(eax, COND0); | 338 | mov(eax, COND0); |
| 339 | xor(eax, (instr.flow_control.refx.Value() ^ 1)); | 339 | xor_(eax, (instr.flow_control.refx.Value() ^ 1)); |
| 340 | break; | 340 | break; |
| 341 | 341 | ||
| 342 | case Instruction::FlowControlType::JustY: | 342 | case Instruction::FlowControlType::JustY: |
| 343 | mov(eax, COND1); | 343 | mov(eax, COND1); |
| 344 | xor(eax, (instr.flow_control.refy.Value() ^ 1)); | 344 | xor_(eax, (instr.flow_control.refy.Value() ^ 1)); |
| 345 | break; | 345 | break; |
| 346 | } | 346 | } |
| 347 | } | 347 | } |
| @@ -734,10 +734,10 @@ void JitShader::Compile_LOOP(Instruction instr) { | |||
| 734 | mov(LOOPCOUNT, dword[SETUP + offset]); | 734 | mov(LOOPCOUNT, dword[SETUP + offset]); |
| 735 | mov(LOOPCOUNT_REG, LOOPCOUNT); | 735 | mov(LOOPCOUNT_REG, LOOPCOUNT); |
| 736 | shr(LOOPCOUNT_REG, 4); | 736 | shr(LOOPCOUNT_REG, 4); |
| 737 | and(LOOPCOUNT_REG, 0xFF0); // Y-component is the start | 737 | and_(LOOPCOUNT_REG, 0xFF0); // Y-component is the start |
| 738 | mov(LOOPINC, LOOPCOUNT); | 738 | mov(LOOPINC, LOOPCOUNT); |
| 739 | shr(LOOPINC, 12); | 739 | shr(LOOPINC, 12); |
| 740 | and(LOOPINC, 0xFF0); // Z-component is the incrementer | 740 | and_(LOOPINC, 0xFF0); // Z-component is the incrementer |
| 741 | movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count | 741 | movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count |
| 742 | add(LOOPCOUNT, 1); // Iteration count is X-component + 1 | 742 | add(LOOPCOUNT, 1); // Iteration count is X-component + 1 |
| 743 | 743 | ||
| @@ -858,9 +858,9 @@ void JitShader::Compile(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>* program_ | |||
| 858 | mov(STATE, ABI_PARAM2); | 858 | mov(STATE, ABI_PARAM2); |
| 859 | 859 | ||
| 860 | // Zero address/loop registers | 860 | // Zero address/loop registers |
| 861 | xor(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); | 861 | xor_(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); |
| 862 | xor(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); | 862 | xor_(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); |
| 863 | xor(LOOPCOUNT_REG, LOOPCOUNT_REG); | 863 | xor_(LOOPCOUNT_REG, LOOPCOUNT_REG); |
| 864 | 864 | ||
| 865 | // Used to set a register to one | 865 | // Used to set a register to one |
| 866 | static const __m128 one = {1.f, 1.f, 1.f, 1.f}; | 866 | static const __m128 one = {1.f, 1.f, 1.f, 1.f}; |
diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp index 8b7b1defb..512e81c08 100644 --- a/src/video_core/swrasterizer/rasterizer.cpp +++ b/src/video_core/swrasterizer/rasterizer.cpp | |||
| @@ -357,10 +357,22 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | |||
| 357 | int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height))) | 357 | int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height))) |
| 358 | .ToFloat32(); | 358 | .ToFloat32(); |
| 359 | 359 | ||
| 360 | if ((texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder && | 360 | bool use_border_s = false; |
| 361 | (s < 0 || static_cast<u32>(s) >= texture.config.width)) || | 361 | bool use_border_t = false; |
| 362 | (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder && | 362 | |
| 363 | (t < 0 || static_cast<u32>(t) >= texture.config.height))) { | 363 | if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder) { |
| 364 | use_border_s = s < 0 || s >= static_cast<int>(texture.config.width); | ||
| 365 | } else if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder2) { | ||
| 366 | use_border_s = s >= static_cast<int>(texture.config.width); | ||
| 367 | } | ||
| 368 | |||
| 369 | if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder) { | ||
| 370 | use_border_t = t < 0 || t >= static_cast<int>(texture.config.height); | ||
| 371 | } else if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder2) { | ||
| 372 | use_border_t = t >= static_cast<int>(texture.config.height); | ||
| 373 | } | ||
| 374 | |||
| 375 | if (use_border_s || use_border_t) { | ||
| 364 | auto border_color = texture.config.border_color; | 376 | auto border_color = texture.config.border_color; |
| 365 | texture_color[i] = {border_color.r, border_color.g, border_color.b, | 377 | texture_color[i] = {border_color.r, border_color.g, border_color.b, |
| 366 | border_color.a}; | 378 | border_color.a}; |
| @@ -572,8 +584,7 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | |||
| 572 | float fog_i = MathUtil::Clamp(floorf(fog_index), 0.0f, 127.0f); | 584 | float fog_i = MathUtil::Clamp(floorf(fog_index), 0.0f, 127.0f); |
| 573 | float fog_f = fog_index - fog_i; | 585 | float fog_f = fog_index - fog_i; |
| 574 | const auto& fog_lut_entry = g_state.fog.lut[static_cast<unsigned int>(fog_i)]; | 586 | const auto& fog_lut_entry = g_state.fog.lut[static_cast<unsigned int>(fog_i)]; |
| 575 | float fog_factor = (fog_lut_entry.value + fog_lut_entry.difference * fog_f) / | 587 | float fog_factor = fog_lut_entry.ToFloat() + fog_lut_entry.DiffToFloat() * fog_f; |
| 576 | 2047.0f; // This is signed fixed point 1.11 | ||
| 577 | fog_factor = MathUtil::Clamp(fog_factor, 0.0f, 1.0f); | 588 | fog_factor = MathUtil::Clamp(fog_factor, 0.0f, 1.0f); |
| 578 | 589 | ||
| 579 | // Blend the fog | 590 | // Blend the fog |
diff --git a/src/video_core/swrasterizer/texturing.cpp b/src/video_core/swrasterizer/texturing.cpp index aeb6aeb8c..4f02b93f2 100644 --- a/src/video_core/swrasterizer/texturing.cpp +++ b/src/video_core/swrasterizer/texturing.cpp | |||
| @@ -18,22 +18,33 @@ using TevStageConfig = TexturingRegs::TevStageConfig; | |||
| 18 | 18 | ||
| 19 | int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) { | 19 | int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) { |
| 20 | switch (mode) { | 20 | switch (mode) { |
| 21 | case TexturingRegs::TextureConfig::ClampToEdge2: | ||
| 22 | // For negative coordinate, ClampToEdge2 behaves the same as Repeat | ||
| 23 | if (val < 0) { | ||
| 24 | return static_cast<int>(static_cast<unsigned>(val) % size); | ||
| 25 | } | ||
| 26 | // [[fallthrough]] | ||
| 21 | case TexturingRegs::TextureConfig::ClampToEdge: | 27 | case TexturingRegs::TextureConfig::ClampToEdge: |
| 22 | val = std::max(val, 0); | 28 | val = std::max(val, 0); |
| 23 | val = std::min(val, (int)size - 1); | 29 | val = std::min(val, static_cast<int>(size) - 1); |
| 24 | return val; | 30 | return val; |
| 25 | 31 | ||
| 26 | case TexturingRegs::TextureConfig::ClampToBorder: | 32 | case TexturingRegs::TextureConfig::ClampToBorder: |
| 27 | return val; | 33 | return val; |
| 28 | 34 | ||
| 35 | case TexturingRegs::TextureConfig::ClampToBorder2: | ||
| 36 | // For ClampToBorder2, the case of positive coordinate beyond the texture size is already | ||
| 37 | // handled outside. Here we only handle the negative coordinate in the same way as Repeat. | ||
| 38 | case TexturingRegs::TextureConfig::Repeat2: | ||
| 39 | case TexturingRegs::TextureConfig::Repeat3: | ||
| 29 | case TexturingRegs::TextureConfig::Repeat: | 40 | case TexturingRegs::TextureConfig::Repeat: |
| 30 | return (int)((unsigned)val % size); | 41 | return static_cast<int>(static_cast<unsigned>(val) % size); |
| 31 | 42 | ||
| 32 | case TexturingRegs::TextureConfig::MirroredRepeat: { | 43 | case TexturingRegs::TextureConfig::MirroredRepeat: { |
| 33 | unsigned int coord = ((unsigned)val % (2 * size)); | 44 | unsigned int coord = (static_cast<unsigned>(val) % (2 * size)); |
| 34 | if (coord >= size) | 45 | if (coord >= size) |
| 35 | coord = 2 * size - 1 - coord; | 46 | coord = 2 * size - 1 - coord; |
| 36 | return (int)coord; | 47 | return static_cast<int>(coord); |
| 37 | } | 48 | } |
| 38 | 49 | ||
| 39 | default: | 50 | default: |