diff options
55 files changed, 737 insertions, 283 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f0af2d41..4668d4bea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,8 @@ else()      add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS)      # Avoid windows.h junk      add_definitions(/DNOMINMAX) +    # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. +    add_definitions(/DWIN32_LEAN_AND_MEAN)      # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)      set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) @@ -92,10 +94,13 @@ else()      # /W3 - Level 3 warnings      # /MP - Multi-threaded compilation      # /Zi - Output debugging information -    # /Zo - enahnced debug info for optimized builds -    set(CMAKE_C_FLAGS   "/W3 /MP /Zi /Zo" CACHE STRING "" FORCE) +    # /Zo - enhanced debug info for optimized builds +    # /permissive- - enables stricter C++ standards conformance checks +    set(CMAKE_C_FLAGS   "/W3 /MP /Zi /Zo /permissive-" CACHE STRING "" FORCE)      # /EHsc - C++-only exception handling semantics -    set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc" CACHE STRING "" FORCE) +    # /Zc:throwingNew - let codegen assume `operator new` will never return null +    # /Zc:inline - let codegen omit inline functions in object files +    set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc /Zc:throwingNew,inline" CACHE STRING "" FORCE)      # /MDd - Multi-threaded Debug Runtime DLL      set(CMAKE_C_FLAGS_DEBUG   "/Od /MDd" CACHE STRING "" FORCE) 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)      # Defined before "dynarmic" above      # add_library(xbyak INTERFACE)      target_include_directories(xbyak INTERFACE ./xbyak/xbyak) -    if (NOT MSVC) -        target_compile_options(xbyak INTERFACE -fno-operator-names) -    endif() +    target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)  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")      add_definitions(-wd68 -wd186 -wd279 -wd327 -wd161 -wd3180)  endif() +if(MSVC) +  # Disable C4390: empty controlled statement found: is this the intent? +  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4390") +endif() +  # Endianness  TEST_BIG_ENDIAN(IS_BIG_ENDIAN)  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 @@  #endif  #ifdef _WIN32 +// windows.h needs to be included before shellapi.h  #include <windows.h> + +#include <shellapi.h>  #endif  #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() {      Settings::values.toggle_framelimit =          sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); -    Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); -    Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); -    Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); +    Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); +    Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); +    Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);      // Layout      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() {      Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool();      Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); -    Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); -    Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); -    Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); +    Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); +    Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); +    Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat();      qt_config->endGroup();      qt_config->beginGroup("Layout"); diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 18b6e7017..410bb87ea 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -226,9 +226,18 @@ void Wrap() {      u32 retval = func(¶m_1, ¶m_2,                        reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3))                       .raw; -    // The first out parameter is moved into R2 and the second is moved into R1. -    Core::CPU().SetReg(1, param_2); -    Core::CPU().SetReg(2, param_1); +    Core::CPU().SetReg(1, param_1); +    Core::CPU().SetReg(2, param_2); +    FuncReturn(retval); +} + +template <ResultCode func(Kernel::Handle*, Kernel::Handle*)> +void Wrap() { +    Kernel::Handle param_1 = 0; +    Kernel::Handle param_2 = 0; +    u32 retval = func(¶m_1, ¶m_2).raw; +    Core::CPU().SetReg(1, param_1); +    Core::CPU().SetReg(2, param_2);      FuncReturn(retval);  } 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 {      OutOfHandles = 19,      SessionClosedByRemote = 26,      PortNameTooLong = 30, +    NoPendingSessions = 35,      WrongPermission = 46,      InvalidBufferDescriptor = 48,      MaxConnectionsReached = 52, @@ -94,5 +95,9 @@ constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(ErrorDescription::OutOfRange, Error                                               ErrorLevel::Permanent); // 0xD8E007FD  constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS,                                      ErrorSummary::StatusChanged, ErrorLevel::Info); +/// Returned when Accept() is called on a port with no sessions to be accepted. +constexpr ResultCode ERR_NO_PENDING_SESSIONS(ErrCodes::NoPendingSessions, ErrorModule::OS, +                                             ErrorSummary::WouldBlock, +                                             ErrorLevel::Permanent); // 0xD8401823  } // 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      boost::range::remove_erase(connected_sessions, server_session);  } +HLERequestContext::HLERequestContext(SharedPtr<ServerSession> session) +    : session(std::move(session)) { +    cmd_buf[0] = 0; +} +  HLERequestContext::~HLERequestContext() = default;  SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { @@ -62,10 +67,13 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr              ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error              for (u32 j = 0; j < num_handles; ++j) {                  Handle handle = src_cmdbuf[i]; -                SharedPtr<Object> object = src_table.GetGeneric(handle); -                ASSERT(object != nullptr); // TODO(yuriks): Return error -                if (descriptor == IPC::DescriptorType::MoveHandle) { -                    src_table.Close(handle); +                SharedPtr<Object> object = nullptr; +                if (handle != 0) { +                    object = src_table.GetGeneric(handle); +                    ASSERT(object != nullptr); // TODO(yuriks): Return error +                    if (descriptor == IPC::DescriptorType::MoveHandle) { +                        src_table.Close(handle); +                    }                  }                  cmd_buf[i++] = AddOutgoingHandle(std::move(object)); @@ -107,9 +115,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P              ASSERT(i + num_handles <= command_size);              for (u32 j = 0; j < num_handles; ++j) {                  SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); - -                // TODO(yuriks): Figure out the proper error handling for if this fails -                Handle handle = dst_table.Create(object).Unwrap(); +                Handle handle = 0; +                if (object != nullptr) { +                    // TODO(yuriks): Figure out the proper error handling for if this fails +                    handle = dst_table.Create(object).Unwrap(); +                }                  dst_cmdbuf[i++] = handle;              }              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:   */  class HLERequestContext {  public: +    HLERequestContext(SharedPtr<ServerSession> session);      ~HLERequestContext();      /// Returns a pointer to the IPC command buffer for this request. @@ -118,14 +119,14 @@ public:       */      void ClearIncomingObjects(); -private: -    friend class Service::ServiceFrameworkBase; - +    /// Populates this context with data from the requesting process/thread.      ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process,                                                   HandleTable& src_table); +    /// Writes data from this context back to the requesting process/thread.      ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,                                              HandleTable& dst_table) const; +private:      std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;      SharedPtr<ServerSession> session;      // 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      auto vma = address_space                     .MapBackingMemory(mapping.address, target_pointer + offset_into_region,                                       mapping.size, memory_state) -                   .MoveFrom(); +                   .Unwrap();      address_space.Reprotect(vma,                              mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite);  } @@ -176,14 +176,14 @@ void MapSharedPages(VMManager& address_space) {                             .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR,                                               reinterpret_cast<u8*>(&ConfigMem::config_mem),                                               Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) -                           .MoveFrom(); +                           .Unwrap();      address_space.Reprotect(cfg_mem_vma, VMAPermission::Read);      auto shared_page_vma = address_space                                 .MapBackingMemory(Memory::SHARED_PAGE_VADDR,                                                   reinterpret_cast<u8*>(&SharedPage::shared_page),                                                   Memory::SHARED_PAGE_SIZE, MemoryState::Shared) -                               .MoveFrom(); +                               .Unwrap();      address_space.Reprotect(shared_page_vma, VMAPermission::Read);  } 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) {  }  VAddr Process::GetLinearHeapAreaAddress() const { +    // Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of +    // the extra RAM in the n3DS.      return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR;  } 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 @@  #include <tuple>  #include "common/assert.h"  #include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/errors.h"  #include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/server_port.h" +#include "core/hle/kernel/server_session.h"  #include "core/hle/kernel/thread.h"  namespace Kernel { @@ -14,6 +16,16 @@ namespace Kernel {  ServerPort::ServerPort() {}  ServerPort::~ServerPort() {} +ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { +    if (pending_sessions.empty()) { +        return ERR_NO_PENDING_SESSIONS; +    } + +    auto session = std::move(pending_sessions.back()); +    pending_sessions.pop_back(); +    return MakeResult(std::move(session)); +} +  bool ServerPort::ShouldWait(Thread* thread) const {      // If there are no pending sessions, we wait until a new one is added.      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 @@  namespace Kernel {  class ClientPort; +class ServerSession;  class SessionRequestHandler;  class ServerPort final : public WaitObject { @@ -41,6 +42,12 @@ public:      }      /** +     * Accepts a pending incoming connection on this port. If there are no pending sessions, will +     * return ERR_NO_PENDING_SESSIONS. +     */ +    ResultVal<SharedPtr<ServerSession>> Accept(); + +    /**       * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port       * will inherit a reference to this handler.       */ @@ -50,8 +57,8 @@ public:      std::string name; ///< Name of port (optional) -    std::vector<SharedPtr<WaitObject>> -        pending_sessions; ///< ServerSessions waiting to be accepted by the port +    /// ServerSessions waiting to be accepted by the port +    std::vector<SharedPtr<ServerSession>> pending_sessions;      /// This session's HLE request handler template (optional)      /// 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 2dc709bc9..d197137c3 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -71,7 +71,7 @@ ResultCode ServerSession::HandleSyncRequest() {  ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name,                                                              SharedPtr<ClientPort> port) { -    auto server_session = ServerSession::Create(name + "_Server").MoveFrom(); +    auto server_session = ServerSession::Create(name + "_Server").Unwrap();      SharedPtr<ClientSession> client_session(new ClientSession);      client_session->name = name + "_Client"; 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,      thread->wait_objects.clear();      thread->wait_address = 0;      thread->name = std::move(name); -    thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); +    thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();      thread->owner_process = g_current_process;      // Find the next available TLS index, and mark it as used @@ -484,7 +484,7 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {      auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,                                       Memory::HEAP_VADDR_END); -    SharedPtr<Thread> thread = thread_res.MoveFrom(); +    SharedPtr<Thread> thread = std::move(thread_res).Unwrap();      thread->context.fpscr =          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) {      timer->name = std::move(name);      timer->initial_delay = 0;      timer->interval_delay = 0; -    timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); +    timer->callback_handle = timer_callback_handle_table.Create(timer).Unwrap();      return timer;  } 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:      }      /// Asserts that the result succeeded and returns a reference to it. -    T& Unwrap() { +    T& Unwrap() & {          ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");          return **this;      } -    T&& MoveFrom() { -        return std::move(Unwrap()); +    T&& Unwrap() && { +        ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); +        return std::move(**this);      }  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) {      u32 flags = rp.Pop<u32>();      IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);      rb.Push(RESULT_SUCCESS); -    rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).MoveFrom(), -                       Kernel::g_handle_table.Create(parameter_event).MoveFrom()); +    rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), +                       Kernel::g_handle_table.Create(parameter_event).Unwrap());      // TODO(bunnei): Check if these events are cleared every time Initialize is called.      notification_event->Clear(); @@ -93,7 +93,7 @@ void GetSharedFont(Service::Interface* self) {      // allocated, the real APT service calculates this address by scanning the entire address space      // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font.      rb.Push(target_address); -    rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).MoveFrom()); +    rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).Unwrap());  }  void NotifyToWait(Service::Interface* self) { @@ -115,7 +115,7 @@ void GetLockHandle(Service::Interface* self) {      rb.Push(RESULT_SUCCESS);    // No error      rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.      rb.Push<u32>(0);            // Least significant bit = power button state -    Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).MoveFrom(); +    Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap();      rb.PushCopyHandles(handle_copy);      LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy, @@ -231,7 +231,7 @@ void ReceiveParameter(Service::Interface* self) {      rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size      rb.PushMoveHandles((next_parameter.object != nullptr) -                           ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() +                           ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap()                             : 0);      rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); @@ -261,7 +261,7 @@ void GlanceParameter(Service::Interface* self) {      rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size      rb.PushCopyHandles((next_parameter.object != nullptr) -                           ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() +                           ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap()                             : 0);      rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); 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) {          int port = *port_select.begin();          rb.Push(RESULT_SUCCESS);          rb.PushCopyHandles( -            Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom()); +            Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).Unwrap());      } else {          LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);          rb.Push(ERROR_INVALID_ENUM_VALUE); @@ -366,7 +366,7 @@ void GetBufferErrorInterruptEvent(Service::Interface* self) {          int port = *port_select.begin();          rb.Push(RESULT_SUCCESS);          rb.PushCopyHandles( -            Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom()); +            Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).Unwrap());      } else {          LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);          rb.Push(ERROR_INVALID_ENUM_VALUE); @@ -400,7 +400,7 @@ void SetReceiving(Service::Interface* self) {          }          rb.Push(RESULT_SUCCESS); -        rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).MoveFrom()); +        rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).Unwrap());      } else {          LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);          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) {  void GetCecInfoEventHandle(Service::Interface* self) {      u32* cmd_buff = Kernel::GetCommandBuffer(); -    cmd_buff[1] = RESULT_SUCCESS.raw;                                      // No error -    cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).MoveFrom(); // Event handle +    cmd_buff[1] = RESULT_SUCCESS.raw;                                    // No error +    cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).Unwrap(); // Event handle      LOG_WARNING(Service_CECD, "(STUBBED) called");  } @@ -40,8 +40,8 @@ void GetCecInfoEventHandle(Service::Interface* self) {  void GetChangeStateEventHandle(Service::Interface* self) {      u32* cmd_buff = Kernel::GetCommandBuffer(); -    cmd_buff[1] = RESULT_SUCCESS.raw;                                           // No error -    cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).MoveFrom(); // Event handle +    cmd_buff[1] = RESULT_SUCCESS.raw;                                         // No error +    cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).Unwrap(); // Event handle      LOG_WARNING(Service_CECD, "(STUBBED) called");  } 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() {      auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode);      ASSERT_MSG(config_result.Succeeded(), "could not open file"); -    auto config = config_result.MoveFrom(); +    auto config = std::move(config_result).Unwrap();      config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data());      return RESULT_SUCCESS; @@ -560,7 +560,7 @@ ResultCode LoadConfigNANDSaveFile() {      // Read the file if it already exists      if (config_result.Succeeded()) { -        auto config = config_result.MoveFrom(); +        auto config = std::move(config_result).Unwrap();          config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());          return RESULT_SUCCESS;      } 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) {      cmd_buff[1] = RESULT_SUCCESS.raw;      cmd_buff[2] = IPC::CopyHandleDesc(2); -    cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom(); -    cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom(); +    cmd_buff[3] = Kernel::g_handle_table.Create(mutex).Unwrap(); +    cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).Unwrap();      LOG_WARNING(Service_CSND, "(STUBBED) called");  } 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) {      cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2);      cmd_buff[1] = RESULT_SUCCESS.raw; // No error      // cmd_buff[2] not set -    cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle +    cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).Unwrap(); // Event handle      LOG_WARNING(Service_DSP, "(STUBBED) called");  } 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      if (backend.Failed())          return backend.Code(); -    auto file = std::shared_ptr<File>(new File(backend.MoveFrom(), path)); +    auto file = std::shared_ptr<File>(new File(std::move(backend).Unwrap(), path));      return MakeResult<std::shared_ptr<File>>(std::move(file));  } @@ -401,7 +401,7 @@ ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle arc      if (backend.Failed())          return backend.Code(); -    auto directory = std::shared_ptr<Directory>(new Directory(backend.MoveFrom(), path)); +    auto directory = std::shared_ptr<Directory>(new Directory(std::move(backend).Unwrap(), path));      return MakeResult<std::shared_ptr<Directory>>(std::move(directory));  } 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) {          file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));          rb.PushMoveHandles( -            Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom()); +            Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap());      } else {          rb.PushMoveHandles(0);          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) {          file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));          cmd_buff[3] = -            Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); +            Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap();      } else {          cmd_buff[3] = 0;          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) {          directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));          cmd_buff[3] = -            Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); +            Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap();      } else {          LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s",                    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) {      } else {          cmd_buff[1] = RESULT_SUCCESS.raw;      } -    cmd_buff[2] = g_thread_id++;                                             // Thread ID -    cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); // GSP shared memory +    cmd_buff[2] = g_thread_id++;                                           // Thread ID +    cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).Unwrap(); // GSP shared memory      g_interrupt_event->Signal(); // TODO(bunnei): Is this correct? 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) {      cmd_buff[1] = 0;          // No error      cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header      // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) -    cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).MoveFrom(); -    cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).MoveFrom(); -    cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).MoveFrom(); -    cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).MoveFrom(); -    cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).MoveFrom(); -    cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).MoveFrom(); +    cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).Unwrap(); +    cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).Unwrap(); +    cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).Unwrap(); +    cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).Unwrap(); +    cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).Unwrap(); +    cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).Unwrap();  }  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) {      IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 0, 0);      IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);      rb.Push(RESULT_SUCCESS); -    rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(), -                       Kernel::g_handle_table.Create(Service::IR::update_event).MoveFrom()); +    rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).Unwrap(), +                       Kernel::g_handle_table.Create(Service::IR::update_event).Unwrap());  }  /** 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) {      IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2);      rb.Push(RESULT_SUCCESS); -    rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).MoveFrom()); +    rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).Unwrap());      LOG_INFO(Service_IR, "called");  } @@ -354,7 +354,7 @@ void GetSendEvent(Interface* self) {      IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2);      rb.Push(RESULT_SUCCESS); -    rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).MoveFrom()); +    rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).Unwrap());      LOG_INFO(Service_IR, "called");  } @@ -394,7 +394,7 @@ static void GetConnectionStatusEvent(Interface* self) {      IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2);      rb.Push(RESULT_SUCCESS); -    rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom()); +    rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).Unwrap());      LOG_INFO(Service_IR, "called");  } 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) {  static void GetBufferFullEvent(Interface* self) {      u32* cmd_buff = Kernel::GetCommandBuffer();      cmd_buff[1] = RESULT_SUCCESS.raw; // No error -    cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom(); +    cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).Unwrap();      LOG_WARNING(Service_MIC, "(STUBBED) called");  } 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) {      cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2);      cmd_buff[1] = RESULT_SUCCESS.raw;      cmd_buff[2] = IPC::CopyHandleDesc(); -    cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).MoveFrom(); +    cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).Unwrap();      LOG_WARNING(Service_NFC, "(STUBBED) called");  } @@ -105,7 +105,7 @@ void GetTagOutOfRangeEvent(Interface* self) {      cmd_buff[0] = IPC::MakeHeader(0xC, 1, 2);      cmd_buff[1] = RESULT_SUCCESS.raw;      cmd_buff[2] = IPC::CopyHandleDesc(); -    cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).MoveFrom(); +    cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).Unwrap();      LOG_WARNING(Service_NFC, "(STUBBED) called");  } diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 35fa1cd77..6dbdff044 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -191,7 +191,7 @@ static void InitializeWithVersion(Interface* self) {      IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);      rb.Push(RESULT_SUCCESS); -    rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).MoveFrom()); +    rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap());      LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",                sharedmem_size, version, sharedmem_handle); @@ -266,7 +266,7 @@ static void Bind(Interface* self) {      IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);      rb.Push(RESULT_SUCCESS); -    rb.PushCopyHandles(Kernel::g_handle_table.Create(event).MoveFrom()); +    rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap());  }  /** @@ -618,6 +618,42 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {                                beacon_broadcast_event, 0);  } +/* + * Returns an available index in the nodes array for the + * currently-hosted UDS network. + */ +static u32 GetNextAvailableNodeId() { +    ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), +               "Can not accept clients if we're not hosting a network"); + +    for (unsigned index = 0; index < connection_status.max_nodes; ++index) { +        if ((connection_status.node_bitmask & (1 << index)) == 0) +            return index; +    } + +    // Any connection attempts to an already full network should have been refused. +    ASSERT_MSG(false, "No available connection slots in the network"); +} + +/* + * Called when a client connects to an UDS network we're hosting, + * updates the connection status and signals the update event. + * @param network_node_id Network Node Id of the connecting client. + */ +void OnClientConnected(u16 network_node_id) { +    ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), +               "Can not accept clients if we're not hosting a network"); +    ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, +               "Can not accept connections on a full network"); + +    u32 node_id = GetNextAvailableNodeId(); +    connection_status.node_bitmask |= 1 << node_id; +    connection_status.changed_nodes |= 1 << node_id; +    connection_status.nodes[node_id] = network_node_id; +    connection_status.total_nodes++; +    connection_status_event->Signal(); +} +  const Interface::FunctionInfo FunctionTable[] = {      {0x00010442, nullptr, "Initialize (deprecated)"},      {0x00020000, nullptr, "Scrap"}, 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() {          auto gamecoin_result =              Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);          if (gamecoin_result.Succeeded()) { -            auto gamecoin = gamecoin_result.MoveFrom(); +            auto gamecoin = std::move(gamecoin_result).Unwrap();              gamecoin->backend->Write(0, sizeof(GameCoin), true,                                       reinterpret_cast<const u8*>(&default_game_coin));              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      // TODO(yuriks): The kernel should be the one handling this as part of translation after      // everything else is migrated -    Kernel::HLERequestContext context; -    context.session = std::move(server_session); +    Kernel::HLERequestContext context(std::move(server_session));      context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,                                                Kernel::g_handle_table); @@ -207,7 +206,7 @@ void AddService(Interface* interface_) {      auto server_port =          SM::g_service_manager              ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) -            .MoveFrom(); +            .Unwrap();      server_port->SetHleHandler(std::shared_ptr<Interface>(interface_));  } 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) {                    (*session)->GetObjectId());          IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);          rb.Push(session.Code()); -        rb.PushObjects(session.MoveFrom()); +        rb.PushObjects(std::move(session).Unwrap());      } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) {          LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u",                      name.c_str(), (*client_port)->GetObjectId());          IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);          rb.Push(ERR_MAX_CONNECTIONS_REACHED); -        rb.PushObjects(client_port.MoveFrom()); +        rb.PushObjects(std::move(client_port).Unwrap());      } else {          LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code());          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) {      cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);      cmd_buff[1] = RESULT_SUCCESS.raw; -    cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); +    cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).Unwrap();      LOG_DEBUG(Service_Y2R, "called");  } diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index e68b9f16a..c05401143 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -36,8 +36,9 @@  ////////////////////////////////////////////////////////////////////////////////////////////////////  // Namespace SVC -using Kernel::SharedPtr;  using Kernel::ERR_INVALID_HANDLE; +using Kernel::Handle; +using Kernel::SharedPtr;  namespace SVC { @@ -933,7 +934,6 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client      using Kernel::ServerPort;      using Kernel::ClientPort; -    using Kernel::SharedPtr;      auto ports = ServerPort::CreatePortPair(max_sessions);      CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create( @@ -947,6 +947,41 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client      return RESULT_SUCCESS;  } +static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) { +    using Kernel::ClientPort; +    SharedPtr<ClientPort> client_port = Kernel::g_handle_table.Get<ClientPort>(client_port_handle); +    if (client_port == nullptr) +        return ERR_INVALID_HANDLE; + +    CASCADE_RESULT(auto session, client_port->Connect()); +    CASCADE_RESULT(*out_client_session, Kernel::g_handle_table.Create(std::move(session))); +    return RESULT_SUCCESS; +} + +static ResultCode CreateSession(Handle* server_session, Handle* client_session) { +    auto sessions = Kernel::ServerSession::CreateSessionPair(); + +    auto& server = std::get<SharedPtr<Kernel::ServerSession>>(sessions); +    CASCADE_RESULT(*server_session, Kernel::g_handle_table.Create(std::move(server))); + +    auto& client = std::get<SharedPtr<Kernel::ClientSession>>(sessions); +    CASCADE_RESULT(*client_session, Kernel::g_handle_table.Create(std::move(client))); + +    LOG_TRACE(Kernel_SVC, "called"); +    return RESULT_SUCCESS; +} + +static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle) { +    using Kernel::ServerPort; +    SharedPtr<ServerPort> server_port = Kernel::g_handle_table.Get<ServerPort>(server_port_handle); +    if (server_port == nullptr) +        return ERR_INVALID_HANDLE; + +    CASCADE_RESULT(auto session, server_port->Accept()); +    CASCADE_RESULT(*out_server_session, Kernel::g_handle_table.Create(std::move(session))); +    return RESULT_SUCCESS; +} +  static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {      using Kernel::MemoryRegion; @@ -1121,9 +1156,9 @@ static const FunctionDef SVC_Table[] = {      {0x45, nullptr, "Unknown"},      {0x46, nullptr, "Unknown"},      {0x47, HLE::Wrap<CreatePort>, "CreatePort"}, -    {0x48, nullptr, "CreateSessionToPort"}, -    {0x49, nullptr, "CreateSession"}, -    {0x4A, nullptr, "AcceptSession"}, +    {0x48, HLE::Wrap<CreateSessionToPort>, "CreateSessionToPort"}, +    {0x49, HLE::Wrap<CreateSession>, "CreateSession"}, +    {0x4A, HLE::Wrap<AcceptSession>, "AcceptSession"},      {0x4B, nullptr, "ReplyAndReceive1"},      {0x4C, nullptr, "ReplyAndReceive2"},      {0x4D, nullptr, "ReplyAndReceive3"}, 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 {      /// Main FCRAM      FCRAM_PADDR = 0x20000000, -    FCRAM_SIZE = 0x08000000, ///< FCRAM size (128MB) +    FCRAM_SIZE = 0x08000000,      ///< FCRAM size on the Old 3DS (128MB) +    FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB)      FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE, +    FCRAM_N3DS_PADDR_END = FCRAM_PADDR + FCRAM_N3DS_SIZE,  };  /// 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 @@  set(SRCS -            glad.cpp -            tests.cpp              common/param_package.cpp              core/file_sys/path_parser.cpp +            core/hle/kernel/hle_ipc.cpp +            glad.cpp +            tests.cpp              )  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 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <catch.hpp> +#include "core/hle/ipc.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/server_session.h" + +namespace Kernel { + +static SharedPtr<Object> MakeObject() { +    return Event::Create(ResetType::OneShot); +} + +TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { +    auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); +    HLERequestContext context(std::move(session)); + +    auto process = Process::Create(CodeSet::Create("", 0)); +    HandleTable handle_table; + +    SECTION("works with empty cmdbuf") { +        const u32_le input[]{ +            IPC::MakeHeader(0x1234, 0, 0), +        }; + +        context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + +        REQUIRE(context.CommandBuffer()[0] == 0x12340000); +    } + +    SECTION("translates regular params") { +        const u32_le input[]{ +            IPC::MakeHeader(0, 3, 0), 0x12345678, 0x21122112, 0xAABBCCDD, +        }; + +        context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + +        auto* output = context.CommandBuffer(); +        REQUIRE(output[1] == 0x12345678); +        REQUIRE(output[2] == 0x21122112); +        REQUIRE(output[3] == 0xAABBCCDD); +    } + +    SECTION("translates move handles") { +        auto a = MakeObject(); +        Handle a_handle = handle_table.Create(a).Unwrap(); +        const u32_le input[]{ +            IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), a_handle, +        }; + +        context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + +        auto* output = context.CommandBuffer(); +        REQUIRE(context.GetIncomingHandle(output[2]) == a); +        REQUIRE(handle_table.GetGeneric(a_handle) == nullptr); +    } + +    SECTION("translates copy handles") { +        auto a = MakeObject(); +        Handle a_handle = handle_table.Create(a).Unwrap(); +        const u32_le input[]{ +            IPC::MakeHeader(0, 0, 2), IPC::CopyHandleDesc(1), a_handle, +        }; + +        context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + +        auto* output = context.CommandBuffer(); +        REQUIRE(context.GetIncomingHandle(output[2]) == a); +        REQUIRE(handle_table.GetGeneric(a_handle) == a); +    } + +    SECTION("translates multi-handle descriptors") { +        auto a = MakeObject(); +        auto b = MakeObject(); +        auto c = MakeObject(); +        const u32_le input[]{ +            IPC::MakeHeader(0, 0, 5),        IPC::MoveHandleDesc(2), +            handle_table.Create(a).Unwrap(), handle_table.Create(b).Unwrap(), +            IPC::MoveHandleDesc(1),          handle_table.Create(c).Unwrap(), +        }; + +        context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + +        auto* output = context.CommandBuffer(); +        REQUIRE(context.GetIncomingHandle(output[2]) == a); +        REQUIRE(context.GetIncomingHandle(output[3]) == b); +        REQUIRE(context.GetIncomingHandle(output[5]) == c); +    } + +    SECTION("translates null handles") { +        const u32_le input[]{ +            IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), 0, +        }; + +        auto result = context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + +        REQUIRE(result == RESULT_SUCCESS); +        auto* output = context.CommandBuffer(); +        REQUIRE(context.GetIncomingHandle(output[2]) == nullptr); +    } + +    SECTION("translates CallingPid descriptors") { +        const u32_le input[]{ +            IPC::MakeHeader(0, 0, 2), IPC::CallingPidDesc(), 0x98989898, +        }; + +        context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + +        REQUIRE(context.CommandBuffer()[2] == process->process_id); +    } + +    SECTION("translates mixed params") { +        auto a = MakeObject(); +        const u32_le input[]{ +            IPC::MakeHeader(0, 2, 4), +            0x12345678, +            0xABCDEF00, +            IPC::MoveHandleDesc(1), +            handle_table.Create(a).Unwrap(), +            IPC::CallingPidDesc(), +            0, +        }; + +        context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + +        auto* output = context.CommandBuffer(); +        REQUIRE(output[1] == 0x12345678); +        REQUIRE(output[2] == 0xABCDEF00); +        REQUIRE(context.GetIncomingHandle(output[4]) == a); +        REQUIRE(output[6] == process->process_id); +    } +} + +TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { +    auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); +    HLERequestContext context(std::move(session)); + +    auto process = Process::Create(CodeSet::Create("", 0)); +    HandleTable handle_table; +    auto* input = context.CommandBuffer(); +    u32_le output[IPC::COMMAND_BUFFER_LENGTH]; + +    SECTION("works with empty cmdbuf") { +        input[0] = IPC::MakeHeader(0x1234, 0, 0); + +        context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + +        REQUIRE(output[0] == 0x12340000); +    } + +    SECTION("translates regular params") { +        input[0] = IPC::MakeHeader(0, 3, 0); +        input[1] = 0x12345678; +        input[2] = 0x21122112; +        input[3] = 0xAABBCCDD; + +        context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + +        REQUIRE(output[1] == 0x12345678); +        REQUIRE(output[2] == 0x21122112); +        REQUIRE(output[3] == 0xAABBCCDD); +    } + +    SECTION("translates move/copy handles") { +        auto a = MakeObject(); +        auto b = MakeObject(); +        input[0] = IPC::MakeHeader(0, 0, 4); +        input[1] = IPC::MoveHandleDesc(1); +        input[2] = context.AddOutgoingHandle(a); +        input[3] = IPC::CopyHandleDesc(1); +        input[4] = context.AddOutgoingHandle(b); + +        context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + +        REQUIRE(handle_table.GetGeneric(output[2]) == a); +        REQUIRE(handle_table.GetGeneric(output[4]) == b); +    } + +    SECTION("translates null handles") { +        input[0] = IPC::MakeHeader(0, 0, 2); +        input[1] = IPC::MoveHandleDesc(1); +        input[2] = context.AddOutgoingHandle(nullptr); + +        auto result = context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + +        REQUIRE(result == RESULT_SUCCESS); +        REQUIRE(output[2] == 0); +    } + +    SECTION("translates multi-handle descriptors") { +        auto a = MakeObject(); +        auto b = MakeObject(); +        auto c = MakeObject(); +        input[0] = IPC::MakeHeader(0, 0, 5); +        input[1] = IPC::MoveHandleDesc(2); +        input[2] = context.AddOutgoingHandle(a); +        input[3] = context.AddOutgoingHandle(b); +        input[4] = IPC::CopyHandleDesc(1); +        input[5] = context.AddOutgoingHandle(c); + +        context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + +        REQUIRE(handle_table.GetGeneric(output[2]) == a); +        REQUIRE(handle_table.GetGeneric(output[3]) == b); +        REQUIRE(handle_table.GetGeneric(output[5]) == c); +    } +} + +} // namespace Kernel diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h index f46db09fb..3b00df0b3 100644 --- a/src/video_core/pica_state.h +++ b/src/video_core/pica_state.h @@ -87,12 +87,18 @@ struct State {              // LUT value, encoded as 12-bit fixed point, with 12 fraction bits              BitField<0, 12, u32> value; // 0.0.12 fixed point -            // Used by HW for efficient interpolation, Citra does not use these -            BitField<12, 12, s32> difference; // 1.0.11 fixed point +            // Used for efficient interpolation. +            BitField<12, 11, u32> difference; // 0.0.11 fixed point +            BitField<23, 1, u32> neg_difference; -            float ToFloat() { +            float ToFloat() const {                  return static_cast<float>(value) / 4095.f;              } + +            float DiffToFloat() const { +                float diff = static_cast<float>(difference) / 2047.f; +                return neg_difference ? -diff : diff; +            }          };          std::array<std::array<LutEntry, 256>, 24> luts; 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 {          DistanceAttenuation = 16,      }; +    static constexpr unsigned NumLightingSampler = 24; +      static LightingSampler SpotlightAttenuationSampler(unsigned index) {          return static_cast<LightingSampler>(              static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index); @@ -84,7 +86,7 @@ struct LightingRegs {          NV = 2, // Cosine of the angle between the normal and the view vector          LN = 3, // Cosine of the angle between the light and the normal vectors          SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors -        CP = 5, // TODO: document and implement +        CP = 5, // Cosine of the angle between the tangent and projection of half-angle vectors      };      enum class LightingBumpMode : u32 { @@ -168,6 +170,8 @@ struct LightingRegs {          union {              BitField<0, 1, u32> directional;              BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 +            BitField<2, 1, u32> geometric_factor_0; +            BitField<3, 1, u32> geometric_factor_1;          } config;          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 {              Repeat = 2,              MirroredRepeat = 3,              // Mode 4-7 produces some weird result and may be just invalid: -            // 4: Positive coord: clamp to edge; negative coord: repeat -            // 5: Positive coord: clamp to border; negative coord: repeat -            // 6: Repeat -            // 7: Repeat +            ClampToEdge2 = 4,   // Positive coord: clamp to edge; negative coord: repeat +            ClampToBorder2 = 5, // Positive coord: clamp to border; negative coord: repeat +            Repeat2 = 6,        // Same as Repeat +            Repeat3 = 7,        // Same as Repeat          };          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..8b7991c04 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) {      uniform_block_data.dirty = true; -    for (unsigned index = 0; index < lighting_luts.size(); index++) { -        uniform_block_data.lut_dirty[index] = true; -    } +    uniform_block_data.lut_dirty.fill(true);      uniform_block_data.fog_lut_dirty = true; @@ -96,18 +94,16 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {      framebuffer.Create();      // Allocate and bind lighting lut textures -    for (size_t i = 0; i < lighting_luts.size(); ++i) { -        lighting_luts[i].Create(); -        state.lighting_luts[i].texture_1d = lighting_luts[i].handle; -    } +    lighting_lut_buffer.Create(); +    state.lighting_lut.texture_buffer = lighting_lut.handle;      state.Apply(); - -    for (size_t i = 0; i < lighting_luts.size(); ++i) { -        glActiveTexture(static_cast<GLenum>(GL_TEXTURE3 + i)); -        glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); -        glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -        glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -    } +    lighting_lut.Create(); +    glBindBuffer(GL_TEXTURE_BUFFER, lighting_lut_buffer.handle); +    glBufferData(GL_TEXTURE_BUFFER, +                 sizeof(GLfloat) * 2 * 256 * Pica::LightingRegs::NumLightingSampler, nullptr, +                 GL_DYNAMIC_DRAW); +    glActiveTexture(TextureUnits::LightingLUT.Enum()); +    glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, lighting_lut_buffer.handle);      // Setup the LUT for the fog      { @@ -116,7 +112,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {      }      state.Apply(); -    glActiveTexture(GL_TEXTURE9); +    glActiveTexture(TextureUnits::FogLUT.Enum());      glTexImage1D(GL_TEXTURE_1D, 0, GL_R32UI, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -125,7 +121,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {      proctex_noise_lut.Create();      state.proctex_noise_lut.texture_1d = proctex_noise_lut.handle;      state.Apply(); -    glActiveTexture(GL_TEXTURE10); +    glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum());      glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -134,7 +130,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {      proctex_color_map.Create();      state.proctex_color_map.texture_1d = proctex_color_map.handle;      state.Apply(); -    glActiveTexture(GL_TEXTURE11); +    glActiveTexture(TextureUnits::ProcTexColorMap.Enum());      glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -143,7 +139,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {      proctex_alpha_map.Create();      state.proctex_alpha_map.texture_1d = proctex_alpha_map.handle;      state.Apply(); -    glActiveTexture(GL_TEXTURE12); +    glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum());      glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -152,7 +148,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {      proctex_lut.Create();      state.proctex_lut.texture_1d = proctex_lut.handle;      state.Apply(); -    glActiveTexture(GL_TEXTURE13); +    glActiveTexture(TextureUnits::ProcTexLUT.Enum());      glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -161,7 +157,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {      proctex_diff_lut.Create();      state.proctex_diff_lut.texture_1d = proctex_diff_lut.handle;      state.Apply(); -    glActiveTexture(GL_TEXTURE14); +    glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum());      glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);      glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -313,7 +309,7 @@ void RasterizerOpenGL::DrawTriangles() {      }      // Sync the lighting luts -    for (unsigned index = 0; index < lighting_luts.size(); index++) { +    for (unsigned index = 0; index < uniform_block_data.lut_dirty.size(); index++) {          if (uniform_block_data.lut_dirty[index]) {              SyncLightingLUT(index);              uniform_block_data.lut_dirty[index] = false; @@ -851,7 +847,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {      case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce):      case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf): {          auto& lut_config = regs.lighting.lut_config; -        uniform_block_data.lut_dirty[lut_config.type / 4] = true; +        uniform_block_data.lut_dirty[lut_config.type] = true;          break;      }      } @@ -1187,77 +1183,57 @@ void RasterizerOpenGL::SetShader() {          state.Apply();          // Set the texture samplers to correspond to different texture units -        GLuint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]"); +        GLint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]");          if (uniform_tex != -1) { -            glUniform1i(uniform_tex, 0); +            glUniform1i(uniform_tex, TextureUnits::PicaTexture(0).id);          }          uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[1]");          if (uniform_tex != -1) { -            glUniform1i(uniform_tex, 1); +            glUniform1i(uniform_tex, TextureUnits::PicaTexture(1).id);          }          uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]");          if (uniform_tex != -1) { -            glUniform1i(uniform_tex, 2); +            glUniform1i(uniform_tex, TextureUnits::PicaTexture(2).id);          }          // Set the texture samplers to correspond to different lookup table texture units -        GLuint uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[0]"); -        if (uniform_lut != -1) { -            glUniform1i(uniform_lut, 3); -        } -        uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[1]"); -        if (uniform_lut != -1) { -            glUniform1i(uniform_lut, 4); -        } -        uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[2]"); -        if (uniform_lut != -1) { -            glUniform1i(uniform_lut, 5); -        } -        uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[3]"); +        GLint uniform_lut = glGetUniformLocation(shader->shader.handle, "lighting_lut");          if (uniform_lut != -1) { -            glUniform1i(uniform_lut, 6); -        } -        uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[4]"); -        if (uniform_lut != -1) { -            glUniform1i(uniform_lut, 7); -        } -        uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]"); -        if (uniform_lut != -1) { -            glUniform1i(uniform_lut, 8); +            glUniform1i(uniform_lut, TextureUnits::LightingLUT.id);          } -        GLuint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut"); +        GLint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut");          if (uniform_fog_lut != -1) { -            glUniform1i(uniform_fog_lut, 9); +            glUniform1i(uniform_fog_lut, TextureUnits::FogLUT.id);          } -        GLuint uniform_proctex_noise_lut = +        GLint uniform_proctex_noise_lut =              glGetUniformLocation(shader->shader.handle, "proctex_noise_lut");          if (uniform_proctex_noise_lut != -1) { -            glUniform1i(uniform_proctex_noise_lut, 10); +            glUniform1i(uniform_proctex_noise_lut, TextureUnits::ProcTexNoiseLUT.id);          } -        GLuint uniform_proctex_color_map = +        GLint uniform_proctex_color_map =              glGetUniformLocation(shader->shader.handle, "proctex_color_map");          if (uniform_proctex_color_map != -1) { -            glUniform1i(uniform_proctex_color_map, 11); +            glUniform1i(uniform_proctex_color_map, TextureUnits::ProcTexColorMap.id);          } -        GLuint uniform_proctex_alpha_map = +        GLint uniform_proctex_alpha_map =              glGetUniformLocation(shader->shader.handle, "proctex_alpha_map");          if (uniform_proctex_alpha_map != -1) { -            glUniform1i(uniform_proctex_alpha_map, 12); +            glUniform1i(uniform_proctex_alpha_map, TextureUnits::ProcTexAlphaMap.id);          } -        GLuint uniform_proctex_lut = glGetUniformLocation(shader->shader.handle, "proctex_lut"); +        GLint uniform_proctex_lut = glGetUniformLocation(shader->shader.handle, "proctex_lut");          if (uniform_proctex_lut != -1) { -            glUniform1i(uniform_proctex_lut, 13); +            glUniform1i(uniform_proctex_lut, TextureUnits::ProcTexLUT.id);          } -        GLuint uniform_proctex_diff_lut = +        GLint uniform_proctex_diff_lut =              glGetUniformLocation(shader->shader.handle, "proctex_diff_lut");          if (uniform_proctex_diff_lut != -1) { -            glUniform1i(uniform_proctex_diff_lut, 14); +            glUniform1i(uniform_proctex_diff_lut, TextureUnits::ProcTexDiffLUT.id);          }          current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); @@ -1387,7 +1363,7 @@ void RasterizerOpenGL::SyncFogLUT() {      if (new_data != fog_lut_data) {          fog_lut_data = new_data; -        glActiveTexture(GL_TEXTURE9); +        glActiveTexture(TextureUnits::FogLUT.Enum());          glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 128, GL_RED_INTEGER, GL_UNSIGNED_INT,                          fog_lut_data.data());      } @@ -1426,17 +1402,18 @@ static void SyncProcTexValueLUT(const std::array<Pica::State::ProcTex::ValueEntr  }  void RasterizerOpenGL::SyncProcTexNoiseLUT() { -    SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, GL_TEXTURE10); +    SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, +                        TextureUnits::ProcTexNoiseLUT.Enum());  }  void RasterizerOpenGL::SyncProcTexColorMap() {      SyncProcTexValueLUT(Pica::g_state.proctex.color_map_table, proctex_color_map_data, -                        GL_TEXTURE11); +                        TextureUnits::ProcTexColorMap.Enum());  }  void RasterizerOpenGL::SyncProcTexAlphaMap() {      SyncProcTexValueLUT(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, -                        GL_TEXTURE12); +                        TextureUnits::ProcTexAlphaMap.Enum());  }  void RasterizerOpenGL::SyncProcTexLUT() { @@ -1451,7 +1428,7 @@ void RasterizerOpenGL::SyncProcTexLUT() {      if (new_data != proctex_lut_data) {          proctex_lut_data = new_data; -        glActiveTexture(GL_TEXTURE13); +        glActiveTexture(TextureUnits::ProcTexLUT.Enum());          glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_lut_data.data());      }  } @@ -1468,7 +1445,7 @@ void RasterizerOpenGL::SyncProcTexDiffLUT() {      if (new_data != proctex_diff_lut_data) {          proctex_diff_lut_data = new_data; -        glActiveTexture(GL_TEXTURE14); +        glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum());          glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_diff_lut_data.data());      }  } @@ -1571,20 +1548,17 @@ void RasterizerOpenGL::SyncGlobalAmbient() {  }  void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) { -    std::array<GLvec4, 256> new_data; - -    for (unsigned offset = 0; offset < new_data.size(); ++offset) { -        new_data[offset][0] = Pica::g_state.lighting.luts[(lut_index * 4) + 0][offset].ToFloat(); -        new_data[offset][1] = Pica::g_state.lighting.luts[(lut_index * 4) + 1][offset].ToFloat(); -        new_data[offset][2] = Pica::g_state.lighting.luts[(lut_index * 4) + 2][offset].ToFloat(); -        new_data[offset][3] = Pica::g_state.lighting.luts[(lut_index * 4) + 3][offset].ToFloat(); -    } +    std::array<GLvec2, 256> new_data; +    const auto& source_lut = Pica::g_state.lighting.luts[lut_index]; +    std::transform(source_lut.begin(), source_lut.end(), new_data.begin(), [](const auto& entry) { +        return GLvec2{entry.ToFloat(), entry.DiffToFloat()}; +    });      if (new_data != lighting_lut_data[lut_index]) {          lighting_lut_data[lut_index] = new_data; -        glActiveTexture(GL_TEXTURE3 + lut_index); -        glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, -                        lighting_lut_data[lut_index].data()); +        glBindBuffer(GL_TEXTURE_BUFFER, lighting_lut_buffer.handle); +        glBufferSubData(GL_TEXTURE_BUFFER, lut_index * new_data.size() * sizeof(GLvec2), +                        new_data.size() * sizeof(GLvec2), new_data.data());      }  } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d9a3e9d1c..79acd4230 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -263,7 +263,7 @@ private:      struct {          UniformData data; -        bool lut_dirty[6]; +        std::array<bool, Pica::LightingRegs::NumLightingSampler> lut_dirty;          bool fog_lut_dirty;          bool proctex_noise_lut_dirty;          bool proctex_color_map_dirty; @@ -279,8 +279,9 @@ private:      OGLBuffer uniform_buffer;      OGLFramebuffer framebuffer; -    std::array<OGLTexture, 6> lighting_luts; -    std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{}; +    OGLBuffer lighting_lut_buffer; +    OGLTexture lighting_lut; +    std::array<std::array<GLvec2, 256>, Pica::LightingRegs::NumLightingSampler> lighting_lut_data{};      OGLTexture fog_lut;      std::array<GLuint, 128> fog_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..0c7c4dd5c 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) {          state.lighting.light[light_index].num = num;          state.lighting.light[light_index].directional = light.config.directional != 0;          state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; +        state.lighting.light[light_index].geometric_factor_0 = light.config.geometric_factor_0 != 0; +        state.lighting.light[light_index].geometric_factor_1 = light.config.geometric_factor_1 != 0;          state.lighting.light[light_index].dist_atten_enable =              !regs.lighting.IsDistAttenDisabled(num);          state.lighting.light[light_index].spot_atten_enable = @@ -518,14 +520,16 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {             "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"             "vec3 light_vector = vec3(0.0);\n"             "vec3 refl_value = vec3(0.0);\n" -           "vec3 spot_dir = vec3(0.0);\n;"; +           "vec3 spot_dir = vec3(0.0);\n" +           "vec3 half_vector = vec3(0.0);\n" +           "float geo_factor = 1.0;\n"; -    // Compute fragment normals +    // Compute fragment normals and tangents +    const std::string pertubation = +        "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0";      if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { -        // Bump mapping is enabled using a normal map, read perturbation vector from the selected -        // texture -        out += "vec3 surface_normal = 2.0 * (" + SampleTexture(config, lighting.bump_selector) + -               ").rgb - 1.0;\n"; +        // Bump mapping is enabled using a normal map +        out += "vec3 surface_normal = " + pertubation + ";\n";          // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher          // precision result @@ -534,31 +538,41 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {                  "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))";              out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n";          } + +        // The tangent vector is not perturbed by the normal map and is just a unit vector. +        out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n";      } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) {          // Bump mapping is enabled using a tangent map -        LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)"); -        UNIMPLEMENTED(); +        out += "vec3 surface_tangent = " + pertubation + ";\n"; +        // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant +        // computation below, which is also confirmed on 3DS. So we don't bother recomputing here +        // even if 'renorm' is enabled. + +        // The normal vector is not perturbed by the tangent map and is just a unit vector. +        out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n";      } else { -        // No bump mapping - surface local normal is just a unit normal +        // No bump mapping - surface local normal and tangent are just unit vectors          out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; +        out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n";      }      // Rotate the surface-local normal by the interpolated normal quaternion to convert it to      // eyespace. -    out += "vec3 normal = quaternion_rotate(normalize(normquat), surface_normal);\n"; +    out += "vec4 normalized_normquat = normalize(normquat);\n"; +    out += "vec3 normal = quaternion_rotate(normalized_normquat, surface_normal);\n"; +    out += "vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n"; -    // Gets the index into the specified lookup table for specular lighting -    auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input, -                                   bool abs) { -        const std::string half_angle = "normalize(normalize(view) + light_vector)"; +    // Samples the specified lookup table for specular lighting +    auto GetLutValue = [&lighting](LightingRegs::LightingSampler sampler, unsigned light_num, +                                   LightingRegs::LightingLutInput input, bool abs) {          std::string index;          switch (input) {          case LightingRegs::LightingLutInput::NH: -            index = "dot(normal, " + half_angle + ")"; +            index = "dot(normal, normalize(half_vector))";              break;          case LightingRegs::LightingLutInput::VH: -            index = std::string("dot(normalize(view), " + half_angle + ")"); +            index = std::string("dot(normalize(view), normalize(half_vector))");              break;          case LightingRegs::LightingLutInput::NV: @@ -573,6 +587,22 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {              index = std::string("dot(light_vector, spot_dir)");              break; +        case LightingRegs::LightingLutInput::CP: +            // CP input is only available with configuration 7 +            if (lighting.config == LightingRegs::LightingConfig::Config7) { +                // Note: even if the normal vector is modified by normal map, which is not the +                // normal of the tangent plane anymore, the half angle vector is still projected +                // using the modified normal vector. +                std::string half_angle_proj = "normalize(half_vector) - normal / dot(normal, " +                                              "normal) * dot(normal, normalize(half_vector))"; +                // Note: the half angle vector projection is confirmed not normalized before the dot +                // product. The result is in fact not cos(phi) as the name suggested. +                index = "dot(" + half_angle_proj + ", tangent)"; +            } else { +                index = "0.0"; +            } +            break; +          default:              LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input);              UNIMPLEMENTED(); @@ -580,22 +610,18 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {              break;          } +        std::string sampler_string = std::to_string(static_cast<unsigned>(sampler)); +          if (abs) {              // LUT index is in the range of (0.0, 1.0)              index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")"                                                                  : "max(" + index + ", 0.0)"; +            return "LookupLightingLUTUnsigned(" + sampler_string + ", " + index + ")";          } else {              // LUT index is in the range of (-1.0, 1.0) -            index = "((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0"; +            return "LookupLightingLUTSigned(" + sampler_string + ", " + index + ")";          } -        return "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; -    }; - -    // Gets the lighting lookup table value given the specified sampler and index -    auto GetLutValue = [](LightingRegs::LightingSampler sampler, std::string lut_index) { -        return std::string("texture(lut[" + std::to_string((unsigned)sampler / 4) + "], " + -                           lut_index + ")[" + std::to_string((unsigned)sampler & 3) + "]");      };      // Write the code to emulate each enabled light @@ -610,6 +636,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {              out += "light_vector = normalize(" + light_src + ".position + view);\n";          out += "spot_dir = " + light_src + ".spot_direction;\n"; +        out += "half_vector = normalize(view) + light_vector;\n";          // Compute dot product of light_vector and normal, adjust if lighting is one-sided or          // two-sided @@ -622,48 +649,57 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {          if (light_config.spot_atten_enable &&              LightingRegs::IsLightingSamplerSupported(                  lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { -            std::string index = -                GetLutIndex(light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); -            auto sampler = LightingRegs::SpotlightAttenuationSampler(light_config.num); -            spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + -                         GetLutValue(sampler, index) + ")"; +            std::string value = +                GetLutValue(LightingRegs::SpotlightAttenuationSampler(light_config.num), +                            light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); +            spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + value + ")";          }          // If enabled, compute distance attenuation value          std::string dist_atten = "1.0";          if (light_config.dist_atten_enable) { -            std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + -                                light_src + ".position) + " + light_src + ".dist_atten_bias)"; -            index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; +            std::string index = "clamp(" + light_src + ".dist_atten_scale * length(-view - " + +                                light_src + ".position) + " + light_src + +                                ".dist_atten_bias, 0.0, 1.0)";              auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); -            dist_atten = GetLutValue(sampler, index); +            dist_atten = "LookupLightingLUTUnsigned(" + +                         std::to_string(static_cast<unsigned>(sampler)) + "," + index + ")";          }          // If enabled, clamp specular component if lighting result is negative          std::string clamp_highlights =              lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; +        if (light_config.geometric_factor_0 || light_config.geometric_factor_1) { +            out += "geo_factor = dot(half_vector, half_vector);\n" +                   "geo_factor = geo_factor == 0.0 ? 0.0 : min(" + +                   dot_product + " / geo_factor, 1.0);\n"; +        } +          // Specular 0 component          std::string d0_lut_value = "1.0";          if (lighting.lut_d0.enable &&              LightingRegs::IsLightingSamplerSupported(                  lighting.config, LightingRegs::LightingSampler::Distribution0)) {              // Lookup specular "distribution 0" LUT value -            std::string index = -                GetLutIndex(light_config.num, lighting.lut_d0.type, lighting.lut_d0.abs_input); -            d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + -                           GetLutValue(LightingRegs::LightingSampler::Distribution0, index) + ")"; +            std::string value = +                GetLutValue(LightingRegs::LightingSampler::Distribution0, light_config.num, +                            lighting.lut_d0.type, lighting.lut_d0.abs_input); +            d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + value + ")";          }          std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; +        if (light_config.geometric_factor_0) { +            specular_0 = "(" + specular_0 + " * geo_factor)"; +        }          // If enabled, lookup ReflectRed value, otherwise, 1.0 is used          if (lighting.lut_rr.enable &&              LightingRegs::IsLightingSamplerSupported(lighting.config,                                                       LightingRegs::LightingSampler::ReflectRed)) { -            std::string index = -                GetLutIndex(light_config.num, lighting.lut_rr.type, lighting.lut_rr.abs_input); -            std::string value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + -                                GetLutValue(LightingRegs::LightingSampler::ReflectRed, index) + ")"; +            std::string value = +                GetLutValue(LightingRegs::LightingSampler::ReflectRed, light_config.num, +                            lighting.lut_rr.type, lighting.lut_rr.abs_input); +            value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + value + ")";              out += "refl_value.r = " + value + ";\n";          } else {              out += "refl_value.r = 1.0;\n"; @@ -673,11 +709,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {          if (lighting.lut_rg.enable &&              LightingRegs::IsLightingSamplerSupported(lighting.config,                                                       LightingRegs::LightingSampler::ReflectGreen)) { -            std::string index = -                GetLutIndex(light_config.num, lighting.lut_rg.type, lighting.lut_rg.abs_input); -            std::string value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + -                                GetLutValue(LightingRegs::LightingSampler::ReflectGreen, index) + -                                ")"; +            std::string value = +                GetLutValue(LightingRegs::LightingSampler::ReflectGreen, light_config.num, +                            lighting.lut_rg.type, lighting.lut_rg.abs_input); +            value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + value + ")";              out += "refl_value.g = " + value + ";\n";          } else {              out += "refl_value.g = refl_value.r;\n"; @@ -687,11 +722,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {          if (lighting.lut_rb.enable &&              LightingRegs::IsLightingSamplerSupported(lighting.config,                                                       LightingRegs::LightingSampler::ReflectBlue)) { -            std::string index = -                GetLutIndex(light_config.num, lighting.lut_rb.type, lighting.lut_rb.abs_input); -            std::string value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + -                                GetLutValue(LightingRegs::LightingSampler::ReflectBlue, index) + -                                ")"; +            std::string value = +                GetLutValue(LightingRegs::LightingSampler::ReflectBlue, light_config.num, +                            lighting.lut_rb.type, lighting.lut_rb.abs_input); +            value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + value + ")";              out += "refl_value.b = " + value + ";\n";          } else {              out += "refl_value.b = refl_value.r;\n"; @@ -703,23 +737,26 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {              LightingRegs::IsLightingSamplerSupported(                  lighting.config, LightingRegs::LightingSampler::Distribution1)) {              // Lookup specular "distribution 1" LUT value -            std::string index = -                GetLutIndex(light_config.num, lighting.lut_d1.type, lighting.lut_d1.abs_input); -            d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + -                           GetLutValue(LightingRegs::LightingSampler::Distribution1, index) + ")"; +            std::string value = +                GetLutValue(LightingRegs::LightingSampler::Distribution1, light_config.num, +                            lighting.lut_d1.type, lighting.lut_d1.abs_input); +            d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + value + ")";          }          std::string specular_1 =              "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; +        if (light_config.geometric_factor_1) { +            specular_1 = "(" + specular_1 + " * geo_factor)"; +        }          // Fresnel          if (lighting.lut_fr.enable &&              LightingRegs::IsLightingSamplerSupported(lighting.config,                                                       LightingRegs::LightingSampler::Fresnel)) {              // Lookup fresnel LUT value -            std::string index = -                GetLutIndex(light_config.num, lighting.lut_fr.type, lighting.lut_fr.abs_input); -            std::string value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + -                                GetLutValue(LightingRegs::LightingSampler::Fresnel, index) + ")"; +            std::string value = +                GetLutValue(LightingRegs::LightingSampler::Fresnel, light_config.num, +                            lighting.lut_fr.type, lighting.lut_fr.abs_input); +            value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + value + ")";              // Enabled for difffuse lighting alpha component              if (lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::PrimaryAlpha || @@ -973,10 +1010,6 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) {  #define NUM_TEV_STAGES 6  #define NUM_LIGHTS 8 -// Texture coordinate offsets and scales -#define OFFSET_256 (0.5 / 256.0) -#define SCALE_256 (255.0 / 256.0) -  in vec4 primary_color;  in vec2 texcoord[3];  in float texcoord0_w; @@ -1018,7 +1051,7 @@ layout (std140) uniform shader_data {  };  uniform sampler2D tex[3]; -uniform sampler1D lut[6]; +uniform samplerBuffer lighting_lut;  uniform usampler1D fog_lut;  uniform sampler1D proctex_noise_lut;  uniform sampler1D proctex_color_map; @@ -1031,6 +1064,24 @@ vec3 quaternion_rotate(vec4 q, vec3 v) {      return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);  } +float LookupLightingLUT(int lut_index, int index, float delta) { +    vec2 entry = texelFetch(lighting_lut, lut_index * 256 + index).rg; +    return entry.r + entry.g * delta; +} + +float LookupLightingLUTUnsigned(int lut_index, float pos) { +    int index = clamp(int(pos * 256.0), 0, 255); +    float delta = pos * 256.0 - index; +    return LookupLightingLUT(lut_index, index, delta); +} + +float LookupLightingLUTSigned(int lut_index, float pos) { +    int index = clamp(int(pos * 128.0), -128, 127); +    float delta = pos * 128.0 - index; +    if (index < 0) index += 256; +    return LookupLightingLUT(lut_index, index, delta); +} +  )";      if (config.state.proctex.enable) 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 {                  bool two_sided_diffuse;                  bool dist_atten_enable;                  bool spot_atten_enable; +                bool geometric_factor_0; +                bool geometric_factor_1;              } light[8];              bool enable; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index bf837a7fb..14e63115c 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -52,9 +52,7 @@ OpenGLState::OpenGLState() {          texture_unit.sampler = 0;      } -    for (auto& lut : lighting_luts) { -        lut.texture_1d = 0; -    } +    lighting_lut.texture_buffer = 0;      fog_lut.texture_1d = 0; @@ -185,7 +183,7 @@ void OpenGLState::Apply() const {      // Textures      for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {          if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { -            glActiveTexture(GL_TEXTURE0 + i); +            glActiveTexture(TextureUnits::PicaTexture(i).Enum());              glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);          }          if (texture_units[i].sampler != cur_state.texture_units[i].sampler) { @@ -194,46 +192,44 @@ void OpenGLState::Apply() const {      }      // Lighting LUTs -    for (unsigned i = 0; i < ARRAY_SIZE(lighting_luts); ++i) { -        if (lighting_luts[i].texture_1d != cur_state.lighting_luts[i].texture_1d) { -            glActiveTexture(GL_TEXTURE3 + i); -            glBindTexture(GL_TEXTURE_1D, lighting_luts[i].texture_1d); -        } +    if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) { +        glActiveTexture(TextureUnits::LightingLUT.Enum()); +        glBindTexture(GL_TEXTURE_BUFFER, cur_state.lighting_lut.texture_buffer);      }      // Fog LUT      if (fog_lut.texture_1d != cur_state.fog_lut.texture_1d) { -        glActiveTexture(GL_TEXTURE9); +        glActiveTexture(TextureUnits::FogLUT.Enum());          glBindTexture(GL_TEXTURE_1D, fog_lut.texture_1d);      }      // ProcTex Noise LUT      if (proctex_noise_lut.texture_1d != cur_state.proctex_noise_lut.texture_1d) { -        glActiveTexture(GL_TEXTURE10); +        glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum());          glBindTexture(GL_TEXTURE_1D, proctex_noise_lut.texture_1d);      }      // ProcTex Color Map      if (proctex_color_map.texture_1d != cur_state.proctex_color_map.texture_1d) { -        glActiveTexture(GL_TEXTURE11); +        glActiveTexture(TextureUnits::ProcTexColorMap.Enum());          glBindTexture(GL_TEXTURE_1D, proctex_color_map.texture_1d);      }      // ProcTex Alpha Map      if (proctex_alpha_map.texture_1d != cur_state.proctex_alpha_map.texture_1d) { -        glActiveTexture(GL_TEXTURE12); +        glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum());          glBindTexture(GL_TEXTURE_1D, proctex_alpha_map.texture_1d);      }      // ProcTex LUT      if (proctex_lut.texture_1d != cur_state.proctex_lut.texture_1d) { -        glActiveTexture(GL_TEXTURE13); +        glActiveTexture(TextureUnits::ProcTexLUT.Enum());          glBindTexture(GL_TEXTURE_1D, proctex_lut.texture_1d);      }      // ProcTex Diff LUT      if (proctex_diff_lut.texture_1d != cur_state.proctex_diff_lut.texture_1d) { -        glActiveTexture(GL_TEXTURE14); +        glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum());          glBindTexture(GL_TEXTURE_1D, proctex_diff_lut.texture_1d);      } @@ -274,6 +270,20 @@ void OpenGLState::ResetTexture(GLuint handle) {              unit.texture_2d = 0;          }      } +    if (cur_state.lighting_lut.texture_buffer == handle) +        cur_state.lighting_lut.texture_buffer = 0; +    if (cur_state.fog_lut.texture_1d == handle) +        cur_state.fog_lut.texture_1d = 0; +    if (cur_state.proctex_noise_lut.texture_1d == handle) +        cur_state.proctex_noise_lut.texture_1d = 0; +    if (cur_state.proctex_color_map.texture_1d == handle) +        cur_state.proctex_color_map.texture_1d = 0; +    if (cur_state.proctex_alpha_map.texture_1d == handle) +        cur_state.proctex_alpha_map.texture_1d = 0; +    if (cur_state.proctex_lut.texture_1d == handle) +        cur_state.proctex_lut.texture_1d = 0; +    if (cur_state.proctex_diff_lut.texture_1d == handle) +        cur_state.proctex_diff_lut.texture_1d = 0;  }  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..bb0218708 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -6,6 +6,29 @@  #include <glad/glad.h> +namespace TextureUnits { + +struct TextureUnit { +    GLint id; +    constexpr GLenum Enum() const { +        return static_cast<GLenum>(GL_TEXTURE0 + id); +    } +}; + +constexpr TextureUnit PicaTexture(int unit) { +    return TextureUnit{unit}; +} + +constexpr TextureUnit LightingLUT{3}; +constexpr TextureUnit FogLUT{4}; +constexpr TextureUnit ProcTexNoiseLUT{5}; +constexpr TextureUnit ProcTexColorMap{6}; +constexpr TextureUnit ProcTexAlphaMap{7}; +constexpr TextureUnit ProcTexLUT{8}; +constexpr TextureUnit ProcTexDiffLUT{9}; + +} // namespace TextureUnits +  class OpenGLState {  public:      struct { @@ -64,8 +87,8 @@ public:      } texture_units[3];      struct { -        GLuint texture_1d; // GL_TEXTURE_BINDING_1D -    } lighting_luts[6]; +        GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER +    } lighting_lut;      struct {          GLuint texture_1d; // GL_TEXTURE_BINDING_1D 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) {          GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder          GL_REPEAT,          // WrapMode::Repeat          GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat +        // TODO(wwylele): ClampToEdge2 and ClampToBorder2 are not properly implemented here. See the +        // comments in enum WrapMode. +        GL_CLAMP_TO_EDGE,   // WrapMode::ClampToEdge2 +        GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder2 +        GL_REPEAT,          // WrapMode::Repeat2 +        GL_REPEAT,          // WrapMode::Repeat3      };      // Range check table for input @@ -65,6 +71,13 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {          return GL_CLAMP_TO_EDGE;      } +    if (static_cast<u32>(mode) > 3) { +        // It is still unclear whether mode 4-7 are valid, so log it if a game uses them. +        // TODO(wwylele): telemetry should be added here so we can collect more info about which +        // game uses this. +        LOG_WARNING(Render_OpenGL, "Using texture wrap mode %u", static_cast<u32>(mode)); +    } +      GLenum gl_mode = wrap_mode_table[mode];      // 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) {      case Instruction::FlowControlType::Or:          mov(eax, COND0);          mov(ebx, COND1); -        xor(eax, (instr.flow_control.refx.Value() ^ 1)); -        xor(ebx, (instr.flow_control.refy.Value() ^ 1)); -        or (eax, ebx); +        xor_(eax, (instr.flow_control.refx.Value() ^ 1)); +        xor_(ebx, (instr.flow_control.refy.Value() ^ 1)); +        or_(eax, ebx);          break;      case Instruction::FlowControlType::And:          mov(eax, COND0);          mov(ebx, COND1); -        xor(eax, (instr.flow_control.refx.Value() ^ 1)); -        xor(ebx, (instr.flow_control.refy.Value() ^ 1)); -        and(eax, ebx); +        xor_(eax, (instr.flow_control.refx.Value() ^ 1)); +        xor_(ebx, (instr.flow_control.refy.Value() ^ 1)); +        and_(eax, ebx);          break;      case Instruction::FlowControlType::JustX:          mov(eax, COND0); -        xor(eax, (instr.flow_control.refx.Value() ^ 1)); +        xor_(eax, (instr.flow_control.refx.Value() ^ 1));          break;      case Instruction::FlowControlType::JustY:          mov(eax, COND1); -        xor(eax, (instr.flow_control.refy.Value() ^ 1)); +        xor_(eax, (instr.flow_control.refy.Value() ^ 1));          break;      }  } @@ -734,10 +734,10 @@ void JitShader::Compile_LOOP(Instruction instr) {      mov(LOOPCOUNT, dword[SETUP + offset]);      mov(LOOPCOUNT_REG, LOOPCOUNT);      shr(LOOPCOUNT_REG, 4); -    and(LOOPCOUNT_REG, 0xFF0); // Y-component is the start +    and_(LOOPCOUNT_REG, 0xFF0); // Y-component is the start      mov(LOOPINC, LOOPCOUNT);      shr(LOOPINC, 12); -    and(LOOPINC, 0xFF0);                // Z-component is the incrementer +    and_(LOOPINC, 0xFF0);               // Z-component is the incrementer      movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count      add(LOOPCOUNT, 1);                  // Iteration count is X-component + 1 @@ -858,9 +858,9 @@ void JitShader::Compile(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>* program_      mov(STATE, ABI_PARAM2);      // Zero address/loop  registers -    xor(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); -    xor(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); -    xor(LOOPCOUNT_REG, LOOPCOUNT_REG); +    xor_(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); +    xor_(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); +    xor_(LOOPCOUNT_REG, LOOPCOUNT_REG);      // Used to set a register to one      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..cd7b6c39d 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                  int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height)))                              .ToFloat32(); -                if ((texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder && -                     (s < 0 || static_cast<u32>(s) >= texture.config.width)) || -                    (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder && -                     (t < 0 || static_cast<u32>(t) >= texture.config.height))) { +                bool use_border_s = false; +                bool use_border_t = false; + +                if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder) { +                    use_border_s = s < 0 || s >= static_cast<int>(texture.config.width); +                } else if (texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder2) { +                    use_border_s = s >= static_cast<int>(texture.config.width); +                } + +                if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder) { +                    use_border_t = t < 0 || t >= static_cast<int>(texture.config.height); +                } else if (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder2) { +                    use_border_t = t >= static_cast<int>(texture.config.height); +                } + +                if (use_border_s || use_border_t) {                      auto border_color = texture.config.border_color;                      texture_color[i] = {border_color.r, border_color.g, border_color.b,                                          border_color.a}; 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;  int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) {      switch (mode) { +    case TexturingRegs::TextureConfig::ClampToEdge2: +        // For negative coordinate, ClampToEdge2 behaves the same as Repeat +        if (val < 0) { +            return static_cast<int>(static_cast<unsigned>(val) % size); +        } +    // [[fallthrough]]      case TexturingRegs::TextureConfig::ClampToEdge:          val = std::max(val, 0); -        val = std::min(val, (int)size - 1); +        val = std::min(val, static_cast<int>(size) - 1);          return val;      case TexturingRegs::TextureConfig::ClampToBorder:          return val; +    case TexturingRegs::TextureConfig::ClampToBorder2: +    // For ClampToBorder2, the case of positive coordinate beyond the texture size is already +    // handled outside. Here we only handle the negative coordinate in the same way as Repeat. +    case TexturingRegs::TextureConfig::Repeat2: +    case TexturingRegs::TextureConfig::Repeat3:      case TexturingRegs::TextureConfig::Repeat: -        return (int)((unsigned)val % size); +        return static_cast<int>(static_cast<unsigned>(val) % size);      case TexturingRegs::TextureConfig::MirroredRepeat: { -        unsigned int coord = ((unsigned)val % (2 * size)); +        unsigned int coord = (static_cast<unsigned>(val) % (2 * size));          if (coord >= size)              coord = 2 * size - 1 - coord; -        return (int)coord; +        return static_cast<int>(coord);      }      default:  | 
